Lua demo script crashes with :lua_error

For fun and learning I pasted this demo code ( the sieve of Eratosthenes ) into a LUA sequence block and it crashed inside the Lua sandbox :slight_smile:

Have I done something obviously silly ? [ My garden Loves Prime Numbers and I wanted to help out ]

05:20:01.845 [info]  Event scheduled at 2021-02-05 05:20:00Z failed to execute: {:lua_error, {:illegal_index, nil, "wrap"}, {:luerl, %{0 => {:table, {:array, 0, 10, nil, 10}, {{{{{:empty, "N", 500.0, :empty}, "_G", {:tref, 0}, {:empty, "_VERSION", "Lua 5.2", :empty}, "assert", {:erl_func, #Function<0.65135778/2 in :luerl_lib_basic."-fun.assert/2-">}, {:empty, "bit32", {:tref, 5}, :empty, "check_position", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}}, "collectgarbage", {:erl_func, #Function<1.65135778/2 in :luerl_lib_basic."-fun.collectgarbage/2-">}, {{:empty, "coordinate", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "current_hour", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {:empty, "current_minute", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "current_month", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {:empty, "current_second", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty, "debug", {:tref, 12}, :empty}}}, "dofile", {:erl_func, #Function<2.65135778/2 in :luerl_lib_basic."-fun.dofile/2-">}, {{{:empty, "emergency_lock", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "emergency_unlock", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {:empty, "eprint", {:erl_func, #Function<3.65135778/2 in :luerl_lib_basic."-fun.eprint/2-">}, :empty}}, "error", {:erl_func, #Function<4.65135778/2 in :luerl_lib_basic.table/0>}, {{:empty, "fbos_version", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "filter", {:lua_func, 0, 2, [], [-1, -2], [{:push_gvar, "coroutine"}, {:get_lit_key, "wrap"}, {:fdef, 0, 0, [], [{:push_last_evar, 1, 2}, {:pop_vals, 1}, {:gfor, [], [{:block, 1, 0, [{:push_vals, 1}, {:store_lvar, 1, ...}, {:push_lvar, ...}, {...}, ...]}]}]}, :multiple, {:fcall, 1}, {:return, 1}]}, {:empty, "find_axis_length", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}}}, "find_home", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {{{:empty, "firmware_version", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "gen", {:lua_func, 0, 1, [], [-1], [{:push_gvar, "coroutine"}, {:get_lit_key, "wrap"}, {:fdef, 0, 0, [], [{:push_lit, 2.0}, {:push_evar, 1, 1}, {:push_lit, 1.0}, {:nfor, {:lvar, "i", 1, 1}, [{:block, 1, 0, [{:store_lvar, 1, ...}, {:push_gvar, ...}, {...}, ...]}]}]}, :multiple, {:fcall, 1}, {:return, 1}]}, {:empty, "get_device", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}}, "get_fbos_config", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {{:empty, "get_firmware_config", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty, "get_position", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "getmetatable", {:erl_func, #Function<5.65135778/2 in :luerl_lib_basic."-fun.getmetatable/2-">}, {:empty, "go_to_home", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty, "io", {:tref, 6}, :empty}}}}, "ipairs", {:erl_func, #Function<6.65135778/2 in :luerl_lib_basic."-fun.ipairs/2-">}, {{{{:empty, "load", {:erl_func, #Function<7.65135778/2 in :luerl_lib_basic."-fun.load/2-">}, :empty}, "loadfile", {:erl_func, #Function<8.65135778/2 in :luerl_lib_basic."-fun.loadfile/2-">}, {:empty, "loadstring", {:erl_func, #Function<9.65135778/2 in :luerl_lib_basic."-fun.loadstring/2-">}, :empty}}, "math", {:tref, 7}, {{:empty, "move_absolute", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "new_farmware_env", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {:empty, "new_sensor_reading", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}}, "next", {:erl_func, #Function<10.65135778/2 in :luerl_lib_basic."">}, {{:empty, "os", {:tref, 8}, :empty}, "package", {:tref, 4}, {:empty, "pairs", {:erl_func, #Function<11.65135778/2 in :luerl_lib_basic."-fun.pairs/2-">}, :empty}, "pcall", {:erl_func, #Function<12.65135778/2 in :luerl_lib_basic."-fun.pcall/2-">}, {:empty, "print", {:erl_func, #Function<13.65135778/2 in :luerl_lib_basic."-fun.print/2-">}, :empty}}}, "rawequal", {:erl_func, #Function<14.65135778/2 in :luerl_lib_basic."-fun.rawequal/2-">}, {{{:empty, "rawget", {:erl_func, #Function<15.65135778/2 in :luerl_lib_basic."-fun.rawget/2-">}, :empty}, "rawlen", {:erl_func, #Function<16.65135778/2 in :luerl_lib_basic."-fun.rawlen/2-">}, {:empty, "rawset", {:erl_func, #Function<17.65135778/2 in :luerl_lib_basic."-fun.rawset/2-">}, :empty}, "read_pin", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {:empty, "read_status", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty, "require", {:erl_func, #Function<0.80287933/2 in :luerl_lib_package."-fun.require/2-">}, :empty}}, "select", {:erl_func, #Function<18.65135778/2 in :luerl_lib_basic."">}, {{:empty, "send_message", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty, "setmetatable", {:erl_func, #Function<19.65135778/2 in :luerl_lib_basic."-fun.setmetatable/2-">}, :empty}, "string", {:tref, 9}, {:empty, "table", {:tref, 11}, :empty}}}, "tonumber", {:erl_func, #Function<20.65135778/2 in :luerl_lib_basic."-fun.tonumber/2-">}, {{{:empty, "tostring", {:erl_func, #Function<21.65135778/2 in :luerl_lib_basic."-fun.tostring/2-">}, :empty}, "type", {:erl_func, #Function<22.65135778/2 in :luerl_lib_basic."-fun.type/2-">}, {:empty, "unpack", {:erl_func, #Function<23.65135778/2 in :luerl_lib_basic."-fun.unpack/2-">}, :empty}}, "update_device", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {{:empty, "update_fbos_config", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}, "update_firmware_config", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, {:empty, "variable", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty, "variables", {:erl_func, #Function<2.27962167/2 in :luerl.encode/2>}, :empty}}}}}, nil}, 1 => {:table, {:array, 3, 10, nil, {nil, {:erl_func, #Function<2.80287933/2 in :luerl_lib_package."-fun.preload_searcher/2-">}, {:erl_func, #Function<3.80287933/2 in :luerl_lib_package."-fun.lua_searcher/2-">}, nil, nil, nil, nil, nil, nil, nil}}, :empty, nil}, 2 => {:table, {:array, 0, 10, nil, 10}, {{{:empty, "_G", {:tref, 0}, :empty}, "bit32", {:tref, 5}, {:empty, "debug", {:tref, 12}, :empty}, "io", {:tref, 6}, {:empty, "math", {:tref, 7}, :empty}}, "os", {:tref, 8}, {{:empty, "package", {:tref, 4}, :empty}, "string", {:tref, 9}, {:empty, "table", {:tref, 11}, :empty}}}, nil}, 3 => {:table, {:array, 0, 10, nil, 10}, :empty, nil}, 4 => {:table, {:array, 0, 10, nil, 10}, {{:empty, "config", "/\n;\n?\n!\n-\n", :empty}, "loaded", {:tref, 2}, {:empty, "path", "./?.lua;./?/init.lua", :empty}, "preload", {:tref, 3}, {:empty, "searchers", {:tref, 1}, :empty, "searchpath", {:erl_func, #Function<1.80287933/2 in :luerl_lib_package."-fun.searchpath/2-">}, :empty}}, nil}, 5 => {:table, {:array, 0, 10, nil, 10}, {{{:empty, "arshift", {:erl_func, #Function<7.95060515/2 in :luerl_lib_bit32."-fun.farshift/2-">}, :empty, "band", {:erl_func, #Function<0.95060515/2 in :luerl_lib_bit32."-fun.fband/2-">}, :empty}, "bnot", {:erl_func, #Function<1.95060515/2 in :luerl_lib_bit32."-fun.fbnot/2-">}, {:empty, "bor", {:erl_func, #Function<2.95060515/2 in :luerl_lib_bit32."-fun.fbor/2-">}, :empty}}, "btest", {:erl_func, #Function<3.95060515/2 in :luerl_lib_bit32."-fun.fbtest/2-">}, {{:empty, "bxor", {:erl_func, #Function<4.95060515/2 in :luerl_lib_bit32."-fun.fbxor/2-">}, :empty}, "extract", {:erl_func, #Function<10.95060515/2 in :luerl_lib_bit32."-fun.fextract/2-">}, {:empty, "lrotate", {:erl_func, #Function<8.95060515/2 in :luerl_lib_bit32."-fun.flrotate/2-">}, :empty}}, "lshift", {:erl_func, #Function<5.95060515/2 in :luerl_lib_bit32."-fun.flshift/2-">}, {{:empty, "replace", {:erl_func, #Function<11.95060515/2 in :luerl_lib_bit32."-fun.freplace/2-">}, :empty}, "rrotate", {:erl_func, #Function<9.95060515/2 in :luerl_lib_bit32."-fun.frrotate/2-">}, {:empty, "rshift", {:erl_func, #Function<6.95060515/2 in :luerl_lib_bit32."-fun.frshift/2-">}, :empty}}}, nil}, 6 => {:table, {:array, 0, 10, nil, 10 (truncated)

Prime numbers are a critical part of any garden. I will investigate this matter further. Thanks for bringing it to our attention. :handshake:

@jsimmonds The code snippet uses coroutines. Our sandbox does not support them and probably never will, unfortunately.

Given the fact that the Lua sandbox is intended for small scripts that run to completion, I can’t see a real-world use case for coroutines in FBOS, so I am not too worried that the feature is missing.

If anyone is trying to write a script that has a real-world need for coroutines, please let me know. There might be a workaround.

Thanks @RickCarlino. Any other Lua 5.2 language features not implemented in luerl ?

@jsimmonds Not sure. If you find any others, I’d be interested to know for reference. Even though co-routines would be nice to have, I think the benefits outweigh the drawbacks, given then other nice things the library provides, and also weighed against the maintenance costs of using “real” Lua.

That’s interesting. I would have thought the implementation effort would swamp the ongoing maintenance cost :thinking: Probably off-topic . . we have luerl and that’s it for now :slightly_smiling_face:

Now I’m off to explore the FBOS integration functions and the Lua libraries that have been provided and to play with porting some Python3 Farmware over to Lua.

Thanks for your work bringing us a real working Lua sandbox ! :yum:

We did have “real” Lua for a while (we were considering it for an unrelated thing over a year ago) and it was not as easy to maintain as the current luerl. That being said, we’re not married to the current solution. If enough people find themselves bumping against limitations, we could swap out the VM we use. In the case of coroutines, I don’t think that’s something that a lot of people would be using (they seem like something reserved for long-running or stateful Lua code).

If you any ideas on this front, I’d be willing to listen. From what I’ve seen, it looks like the only real options are a library that wraps luerl or writing the sandbox as a native C extension.

Glad you like it! Based on the feedback I’ve received, the next three additions will be:

  • (easy) Expose take_photo to the sandbox. Currently, you can get around this limitation by splitting the Lua code into two blocks, but it would be more convenient to maintain context.
  • (easy) Add a JSON parser / decoder.
  • (medium) Allow access to farmware_env resource so that developers have a persistent K/V store for things like runtime state / 3rd party API keys / Etc…
  • (medium, but requires good planning) Add an HTTP library

If you have other things you would like to see, please let me know.

I’d like to know if you have considered a Javascript environment instead, and if so what the reasoning was to go with Lua instead. I think JS is becoming pretty ubiquitous now and if you’d like dev feedback, mine would be that I’d absolutely prefer JS over anything else.

1 Like

Probably off-topic for this post but I’m also firmly in your camp @mdingena.
I’d love to see Node.js running on the little RPi0W in Express :wink:
Such a rich and modern ecosystem for JS these days.

@RickCarlino we agree on that one . . it’s library code anyway and simply a nice way of structuring ( 1…n -threaded code ). I just wanted that demo to run :slightly_frowning_face:

1 Like

During FBOS’s lifetime, there have been more than one attempt to bring NodeJS into FBOS for different reasons. It’s been problematic every time, though it has been years since we tried last (and I actually don’t remember the specific issues of the top of my head, though there were multiple problems :thinking: ). It’s also important to note that the Lua sandbox is not intended to be an integrated development platform, so if we did bring in a Javascript runtime, there would be no NPM, import statements, etc…

That’s not to say we can’t add JS support someday. When we do though, it will most likely happen via duktape or a similar JS runtime that targets embedded systems rather than a full-blown Node installation.

1 Like

To keep this topic from going off the rails any further I’ll PM you some JS firmwares for microcontrollers.

1 Like