mJS – A new approach to embedded scripting
mongoose-iot.com
mongoose-iot.com
How's the performance? Will my 72MHz Cortex or 16MHz Arduino be able to run interesting things with mJS? If I have to do everything through FFI, what's the benefit vs. e.g. C++11? The latter has nice language features too but compiles to much smaller native code!
With C++ one would unfortunately have to create C-wrappers, but with say something with easier C interop like Nim or Rust.
> In order to make FFI work, mJS must be able to get the address of a C function by its name. On POSIX systems, dlsym() API can do that. On Windows, GetProcAddress(). On embedded systems, a system resolver should be either manually written, or be implemented with some aid from a firmware linker script.
So I guess they don't provide any implementation for their FFI linking on embedded platforms? It's not clear if the 25k size figure includes any dlsym support, but if it requires instrumenting your firmware image on-disk then the size costs could balloon significantly.
https://github.com/cesanta/mongoose-os/blob/master/fw/exampl...
I don't know if you can do that with mJS, alas.
But do you really need eval?
let f = ffi('int gpio_write(int, int)');
vs. var current = ffi.Library(null, {
'atoi': [ 'int', [ 'string' ] ]
});In their docs, they claim 25K storage and 10K RAM. Blog post claims 25K storage and 1K RAM.
[0] https://github.com/cesanta/mjs/blob/master/mjs.c
V7 uses hand-written recursive-descent parser. Initially, it was using ordinary C functions, and that created a problem on systems with low stack size. E.g. each '(' starts statement parsing from the top, so 1 + (2 + (3 + (4 + 5))) consumed stack, and sometimes resulted in stack overflow in e.g. interrupt handlers or network callbacks.
Therefore we have rewritten recursive descent using C coroutines, and that is extremely sophisticated piece of work. See https://raw.githubusercontent.com/cesanta/v7/master/v7.c , search for #line 1 "v7/src/parser.c"
mJS on the other hand uses lemon parser generator - the one from sqlite project. It generates a LALR parser which is quite efficient in terms of memory.
To be honest, we do not think that JS is a good language for embedded. Like any other existing popular scripting language. Perhaps scripted Go would be a better choice.
The point is that in many, many cases scripting brings a lot of benefits to the embedded environment. It all depends on a specific tasks - for some tasks, scripting will never be appropriate.
I understand your point. These tools aren't appropriate for serious embedded systems, but they're also not meant for them. They're meant for people who want to have fun and want to play with things quickly.
"We can't do things that way, because we do things a different way", is something that many people will automatically recognize as a bad argument if you put it that way. (It's in the same bucket as, "Well, nobody has ever complained before.") But anyone—even by accident—can end up using this argument while saying different words. The effect is that it comes in a different package that's more difficult to spot. In the end, though, it's the same invalid discussion killer.
Anyway, that's a lot of exposition. What I came here to say is that your comment is tripping the filter for me right now.
What's inappropriate about a fast development feedback cycle when doing embedded programming?
2) C needs to be compiled and often flashed onto the device to run, sending new JS over the wire to execute is a lot more convenient, especially if you want customers to be able to customize their devices.
Not in the case of Lua. Well, the "whole language" part is correct, but Lua is a very tiny language spec. It's "standard library", however, is the opposite of "complete".
Also false for Lua. It is designed to be embedded.
Why is there so much stigma against _learning_ to code in something other than $favlang these days? Most hard core developers I know appreciate the importance of using the right tool for the job, I don't see embedded/desktop developers shying away from using whatever the native toolkit/language is for their chosen platform and instead shoehorning $x to fit as much as we see this constant trend to try to use "web tech" everywhere. (Scripting in embedded systems has long been a solved problem: use lua.)
mJS might also be an option. I don't really see what's wrong with that if we have the space for it.
This can probably get types from Flow, since it is valid es6. Flow has no compilation step or runtime component.
You may have a point about the debugging, but JS is pretty easy to debug by printing to stdout.
Not to mention Lua itself can be used as a data / configuration language, their tables syntax is made especially for that. Normally when I work with Lua I don't feel the need to embed any other parsers for configuration, but cjson is there if you want.
I'd say it's better to know one language and know it well.
"Embeddable, portable, compact: can run on platforms with 192kB flash and 64kB system RAM"
let malloc = ffi('void *malloc(int)');
let mem = malloc(10);
How do you manipulate that memory?How can you do the following in mJS?
int* mem = malloc(10);
mem[0] = 3;
mem[1] = 7;
mem[2] = 12;
int *rest = mem + 3;
There are some things you can do in C without functions. How does mJS achieve this?On how to access memory from mJS: write an accessor function!
// C
void setmem(uint8_t *ptr, int index, int value) {
ptr[index] = value;
}
// mJS
let setmem = ffi('void setmem(void *, int, int)');
let malloc = ffi('void *malloc(int)');
let mem = malloc(10);
setmem(mem, 3, 5); int *buffer = malloc(10);
SSL_read(ssl, buffer, 10);
into let buffer = ffi('void *malloc(int)');
let SSL_read = ffi('int SSL_read(void *ssl, void *buf, int num)');
let buffer = malloc(10);
SSL_read(ssl, buffer, 10);
Your own language generally has ways to allocate memory—but does not necessarily have a way to generate a pointer, or to manually manage the lifetime of that memory.Allows to run Lua on bare metal, no OS involved. It's what the nodemcu firmware for the ESP cards is based on.