同步操作将从 Gitee 极速下载/mJS 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
mJS is designed for microcontrollers with limited resources. Main design goals are: small footprint and simple C/C++ interoperability. mJS implements a strict subset of ES6 (JavaScript version 6):
On 32-bit ARM mJS engine takes about 50k of flash memory, and less than 1k of RAM (see intro article). mJS is part of MongooseOS, where it enables scripting for IoT devices.
JSON.parse()
and JSON.stringify()
are available.new
. In order to create an object with a custom prototype, use
Object.create()
, which is available.var
, only let
.for..of
, =>
, destructors, generators, proxies, promises.valueOf
, prototypes, classes, template strings.==
or !=
, only ===
and !==
.'ы'.length === 2
,
'ы'[0] === '\xd1'
, 'ы'[1] === '\x8b'
.
mJS string can represent any binary data chunk.mJS requires no glue code. The mJS's Foreign Function Interface (FFI) allows the user to call an existing C function with an arbitrary signature. Currently mJS provides a simple implementation of the FFI trampoline that supports up to 6 32-bit arguments, or up to 2 64-bit arguments:
let floor = ffi('double floor(double)');
print(floor(1.23456));
Function arguments should be simple: only int
, double
, char *
, void *
are supported. Use char *
for NUL-terminated C strings, void *
for any
other pointers. In order to import more complex functions
(e.g. the ones that use structures as arguments), write wrappers.
Callbacks are implemented similarly. Consider that you have a C function
that takes a callback and user data void *
pointer, which should be marked
as userdata
in the signature:
void timer(int seconds, void (*callback)(int, void *), void *user_data);
This is how to make an mJS callback - note the usage of userdata
:
let Timer = {
set: ffi('void timer(int, void (*)(int, userdata), userdata)')
};
Timer.set(200, function(t) {
print('Time now: ', t);
}, null);
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. mJS resolver uses dlsym
-compatible signature.
mJS provides a helper to facilitate coversion of C structs to JS objects.
The functions is called s2o
and takes two parameters: foreign pointer to
the struct and foreign pointer to the struct's descriptor which specifies
names and offsets of the struct's members. Here's an simple example:
C/C++ side code:
#include "mjs.h"
struct my_struct {
int a;
const char *b;
double c;
struct mg_str d;
struct mg_str *e;
float f;
bool g;
};
static const struct mjs_c_struct_member my_struct_descr[] = {
{"a", offsetof(struct my_struct, a), MJS_STRUCT_FIELD_TYPE_INT, NULL},
{"b", offsetof(struct my_struct, b), MJS_STRUCT_FIELD_TYPE_CHAR_PTR, NULL},
{"c", offsetof(struct my_struct, c), MJS_STRUCT_FIELD_TYPE_DOUBLE, NULL},
{"d", offsetof(struct my_struct, d), MJS_STRUCT_FIELD_TYPE_MG_STR, NULL},
{"e", offsetof(struct my_struct, e), MJS_STRUCT_FIELD_TYPE_MG_STR_PTR, NULL},
{"f", offsetof(struct my_struct, f), MJS_STRUCT_FIELD_TYPE_FLOAT, NULL},
{"g", offsetof(struct my_struct, g), MJS_STRUCT_FIELD_TYPE_BOOL, NULL},
{NULL, 0, MJS_STRUCT_FIELD_TYPE_INVALID, NULL},
};
const struct mjs_c_struct_member *get_my_struct_descr(void) {
return my_struct_descr;
};
JS side code:
// Assuming `s` is a foreign pointer to an instance of `my_struct`, obtained elsewhere.
let sd = ffi('void *get_my_struct_descr(void)')();
let o = s2o(s, sd);
print(o.a, o.b);
Nested structs are also supported - use MJS_STRUCT_FIELD_TYPE_STRUCT
field type
and provide pointer to the definition:
struct my_struct2 {
int8_t i8;
int16_t i16;
uint8_t u8;
uint16_t u16;
};
static const struct mjs_c_struct_member my_struct2_descr[] = {
{"i8", offsetof(struct my_struct2, i8), MJS_STRUCT_FIELD_TYPE_INT8, NULL},
{"i16", offsetof(struct my_struct2, i16), MJS_STRUCT_FIELD_TYPE_INT16, NULL},
{"u8", offsetof(struct my_struct2, u8), MJS_STRUCT_FIELD_TYPE_UINT8, NULL},
{"u16", offsetof(struct my_struct2, u16), MJS_STRUCT_FIELD_TYPE_UINT16, NULL},
{NULL, 0, MJS_STRUCT_FIELD_TYPE_INVALID, NULL},
};
struct my_struct {
struct my_struct2 s;
struct my_struct2 *sp;
};
static const struct mjs_c_struct_member my_struct_descr[] = {
{"s", offsetof(struct my_struct, s), MJS_STRUCT_FIELD_TYPE_STRUCT, my_struct2_descr},
{"sp", offsetof(struct my_struct, sp), MJS_STRUCT_FIELD_TYPE_STRUCT_PTR, my_struct2_descr},
{NULL, 0, MJS_STRUCT_FIELD_TYPE_INVALID, NULL},
};
For complicated cases, a custom conversion function can be invoked that returns value:
mjs_val_t custom_value_func(struct mjs *mjs, void *ap) {
/* Do something with ap, construct and return mjs_val_t */
}
static const struct mjs_c_struct_member my_struct_descr[] = {
...
{"x", offsetof(struct my_struct, x), MJS_STRUCT_FIELD_TYPE_CUSTOM, custom_value_func},
...
};
We export C function foo
to the JS environment and call it from the JS.
#include "strings.h"
#include "mjs.h"
void foo(int x) {
printf("Hello %d!\n", x);
}
void *my_dlsym(void *handle, const char *name) {
if (strcmp(name, "foo") == 0) return foo;
return NULL;
}
int main(void) {
struct mjs *mjs = mjs_create();
mjs_set_ffi_resolver(mjs, my_dlsym);
mjs_exec(mjs, "let f = ffi('void foo(int)'); f(1234)", NULL);
return 0;
}
Compile & run:
$ cc main.c mjs.c -o /tmp/x && /tmp/x
Hello 1234!
Build:
$ make
Use as a simple calculator:
$ ./build/mjs -e '1 + 2 * 3'
7
FFI standard C functions:
$ ./build/mjs -e 'ffi("double sin(double)")(1.23)'
0.942489
View generated bytecode:
$ ./build/mjs -l 3 -e '2 + 2'
------- MJS VM DUMP BEGIN
DATA_STACK (0 elems):
CALL_STACK (0 elems):
SCOPES (1 elems): [<object>]
LOOP_OFFSETS (0 elems):
CODE:
0 BCODE_HDR [<stdin>] size:28
21 PUSH_INT 2
23 PUSH_INT 2
25 EXPR +
27 EXIT
28 NOP
------- MJS VM DUMP END
4
The stand-alone binary uses dlsym()
symbol resolver, that's why
ffi("double sin(double)")(1.23)
works.
mJS is released under commercial and GNU GPL v.2 open source licenses.
Commercial Projects: once your project becomes commercialised, GPLv2 licensing dictates that you need to either open your source fully or purchase a commercial license. Cesanta offer full, royalty-free commercial licenses without any GPL restrictions. If your needs require a custom license, we’d be happy to work on a solution with you. Contact us for pricing
Prototyping: While your project is still in prototyping stage and not for sale, you can use MJS’s open source code without license restrictions.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。