diff --git a/examples/hello_module.js b/examples/hello_module.js index 463660f..5d4c78e 100644 --- a/examples/hello_module.js +++ b/examples/hello_module.js @@ -1,6 +1,8 @@ -/* example of JS module */ +/* example of JS and JSON modules */ import { fib } from "./fib_module.js"; +import msg from "./message.json"; console.log("Hello World"); console.log("fib(10)=", fib(10)); +console.log("msg=", msg); diff --git a/examples/message.json b/examples/message.json new file mode 100644 index 0000000..3b7fe48 --- /dev/null +++ b/examples/message.json @@ -0,0 +1,2 @@ +{ "x" : 1, "tab": [ 1, 2, 3 ] } + diff --git a/qjsc.c b/qjsc.c index 4c6b28b..49aa449 100644 --- a/qjsc.c +++ b/qjsc.c @@ -170,14 +170,24 @@ static void dump_hex(FILE *f, const uint8_t *buf, size_t len) fprintf(f, "\n"); } +typedef enum { + CNAME_TYPE_SCRIPT, + CNAME_TYPE_MODULE, + CNAME_TYPE_JSON_MODULE, +} CNameTypeEnum; + static void output_object_code(JSContext *ctx, FILE *fo, JSValueConst obj, const char *c_name, - BOOL load_only) + CNameTypeEnum c_name_type) { uint8_t *out_buf; size_t out_buf_len; int flags; - flags = JS_WRITE_OBJ_BYTECODE; + + if (c_name_type == CNAME_TYPE_JSON_MODULE) + flags = 0; + else + flags = JS_WRITE_OBJ_BYTECODE; if (byte_swap) flags |= JS_WRITE_OBJ_BSWAP; out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); @@ -186,7 +196,7 @@ static void output_object_code(JSContext *ctx, exit(1); } - namelist_add(&cname_list, c_name, NULL, load_only); + namelist_add(&cname_list, c_name, NULL, c_name_type); fprintf(fo, "const uint32_t %s_size = %u;\n\n", c_name, (unsigned int)out_buf_len); @@ -227,7 +237,8 @@ static void find_unique_cname(char *cname, size_t cname_size) } JSModuleDef *jsc_module_loader(JSContext *ctx, - const char *module_name, void *opaque) + const char *module_name, void *opaque, + JSValueConst attributes) { JSModuleDef *m; namelist_entry_t *e; @@ -249,9 +260,9 @@ JSModuleDef *jsc_module_loader(JSContext *ctx, } else { size_t buf_len; uint8_t *buf; - JSValue func_val; char cname[1024]; - + int res; + buf = js_load_file(ctx, &buf_len, module_name); if (!buf) { JS_ThrowReferenceError(ctx, "could not load module filename '%s'", @@ -259,21 +270,59 @@ JSModuleDef *jsc_module_loader(JSContext *ctx, return NULL; } - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - get_c_name(cname, sizeof(cname), module_name); - if (namelist_find(&cname_list, cname)) { - find_unique_cname(cname, sizeof(cname)); - } - output_object_code(ctx, outfile, func_val, cname, TRUE); + res = js_module_test_json(ctx, attributes); + if (has_suffix(module_name, ".json") || res > 0) { + /* compile as JSON or JSON5 depending on "type" */ + JSValue val; + int flags; - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); + if (res == 2) + flags = JS_PARSE_JSON_EXT; + else + flags = 0; + val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags); + js_free(ctx, buf); + if (JS_IsException(val)) + return NULL; + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + if (!m) { + JS_FreeValue(ctx, val); + return NULL; + } + + get_c_name(cname, sizeof(cname), module_name); + if (namelist_find(&cname_list, cname)) { + find_unique_cname(cname, sizeof(cname)); + } + + /* output the module name */ + fprintf(outfile, "static const uint8_t %s_module_name[] = {\n", + cname); + dump_hex(outfile, (const uint8_t *)module_name, strlen(module_name) + 1); + fprintf(outfile, "};\n\n"); + + output_object_code(ctx, outfile, val, cname, CNAME_TYPE_JSON_MODULE); + JS_FreeValue(ctx, val); + } else { + JSValue func_val; + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + get_c_name(cname, sizeof(cname), module_name); + if (namelist_find(&cname_list, cname)) { + find_unique_cname(cname, sizeof(cname)); + } + output_object_code(ctx, outfile, func_val, cname, CNAME_TYPE_MODULE); + + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } } return m; } @@ -314,7 +363,7 @@ static void compile_file(JSContext *ctx, FILE *fo, } else { get_c_name(c_name, sizeof(c_name), filename); } - output_object_code(ctx, fo, obj, c_name, FALSE); + output_object_code(ctx, fo, obj, c_name, CNAME_TYPE_SCRIPT); JS_FreeValue(ctx, obj); } @@ -709,7 +758,7 @@ int main(int argc, char **argv) JS_SetStripInfo(rt, strip_flags); /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); + JS_SetModuleLoaderFunc2(rt, NULL, jsc_module_loader, NULL, NULL); fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" "\n" @@ -732,7 +781,7 @@ int main(int argc, char **argv) } for(i = 0; i < dynamic_module_list.count; i++) { - if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) { + if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL, JS_UNDEFINED)) { fprintf(stderr, "Could not load dynamic module '%s'\n", dynamic_module_list.array[i].name); exit(1); @@ -770,9 +819,12 @@ int main(int argc, char **argv) } for(i = 0; i < cname_list.count; i++) { namelist_entry_t *e = &cname_list.array[i]; - if (e->flags) { + if (e->flags == CNAME_TYPE_MODULE) { fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n", e->name, e->name); + } else if (e->flags == CNAME_TYPE_JSON_MODULE) { + fprintf(fo, " js_std_eval_binary_json_module(ctx, %s, %s_size, (const char *)%s_module_name);\n", + e->name, e->name, e->name); } } fprintf(fo, @@ -797,7 +849,7 @@ int main(int argc, char **argv) for(i = 0; i < cname_list.count; i++) { namelist_entry_t *e = &cname_list.array[i]; - if (!e->flags) { + if (e->flags == CNAME_TYPE_SCRIPT) { fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", e->name, e->name); } diff --git a/quickjs-libc.c b/quickjs-libc.c index ca8e359..10a7d00 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -599,6 +599,20 @@ static int json_module_init(JSContext *ctx, JSModuleDef *m) return 0; } +static JSModuleDef *create_json_module(JSContext *ctx, const char *module_name, JSValue val) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, json_module_init); + if (!m) { + JS_FreeValue(ctx, val); + return NULL; + } + /* only export the "default" symbol which will contain the JSON object */ + JS_AddModuleExport(ctx, m, "default"); + JS_SetModulePrivateValue(ctx, m, val); + return m; +} + /* in order to conform with the specification, only the keys should be tested and not the associated values. */ int js_module_check_attributes(JSContext *ctx, void *opaque, @@ -631,8 +645,8 @@ int js_module_check_attributes(JSContext *ctx, void *opaque, return ret; } -/* return TRUE if the attributes indicate a JSON module */ -JS_BOOL js_module_test_json(JSContext *ctx, JSValueConst attributes) +/* return > 0 if the attributes indicate a JSON module */ +int js_module_test_json(JSContext *ctx, JSValueConst attributes) { JSValue str; const char *cstr; @@ -649,7 +663,13 @@ JS_BOOL js_module_test_json(JSContext *ctx, JSValueConst attributes) if (!cstr) return FALSE; /* XXX: raise an error if unknown type ? */ - res = (len == 4 && !memcmp(cstr, "json", len)); + if (len == 4 && !memcmp(cstr, "json", len)) { + res = 1; + } else if (len == 5 && !memcmp(cstr, "json5", len)) { + res = 2; + } else { + res = 0; + } JS_FreeCString(ctx, cstr); return res; } @@ -659,7 +679,8 @@ JSModuleDef *js_module_loader(JSContext *ctx, JSValueConst attributes) { JSModuleDef *m; - + int res; + if (has_suffix(module_name, ".so")) { m = js_module_loader_so(ctx, module_name); } else { @@ -672,23 +693,22 @@ JSModuleDef *js_module_loader(JSContext *ctx, module_name); return NULL; } - - if (has_suffix(module_name, ".json") || - js_module_test_json(ctx, attributes)) { - /* compile as JSON */ + res = js_module_test_json(ctx, attributes); + if (has_suffix(module_name, ".json") || res > 0) { + /* compile as JSON or JSON5 depending on "type" */ JSValue val; - val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name); + int flags; + if (res == 2) + flags = JS_PARSE_JSON_EXT; + else + flags = 0; + val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags); js_free(ctx, buf); if (JS_IsException(val)) return NULL; - m = JS_NewCModule(ctx, module_name, json_module_init); - if (!m) { - JS_FreeValue(ctx, val); + m = create_json_module(ctx, module_name, val); + if (!m) return NULL; - } - /* only export the "default" symbol which will contain the JSON object */ - JS_AddModuleExport(ctx, m, "default"); - JS_SetModulePrivateValue(ctx, m, val); } else { JSValue func_val; /* compile the module */ @@ -4303,3 +4323,22 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, JS_FreeValue(ctx, val); } } + +void js_std_eval_binary_json_module(JSContext *ctx, + const uint8_t *buf, size_t buf_len, + const char *module_name) +{ + JSValue obj; + JSModuleDef *m; + + obj = JS_ReadObject(ctx, buf, buf_len, 0); + if (JS_IsException(obj)) + goto exception; + m = create_json_module(ctx, module_name, obj); + if (!m) { + exception: + js_std_dump_error(ctx); + exit(1); + } +} + diff --git a/quickjs-libc.h b/quickjs-libc.h index f24e161..5c8301b 100644 --- a/quickjs-libc.h +++ b/quickjs-libc.h @@ -44,13 +44,16 @@ void js_std_dump_error(JSContext *ctx); uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, JS_BOOL use_realpath, JS_BOOL is_main); -JS_BOOL js_module_test_json(JSContext *ctx, JSValueConst attributes); +int js_module_test_json(JSContext *ctx, JSValueConst attributes); int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes); JSModuleDef *js_module_loader(JSContext *ctx, const char *module_name, void *opaque, JSValueConst attributes); void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); +void js_std_eval_binary_json_module(JSContext *ctx, + const uint8_t *buf, size_t buf_len, + const char *module_name); void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque); diff --git a/quickjs.c b/quickjs.c index e56adb8..35411ef 100644 --- a/quickjs.c +++ b/quickjs.c @@ -36326,6 +36326,8 @@ static int JS_WriteModule(BCWriterState *s, JSValueConst obj) for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; bc_put_atom(s, rme->module_name); + if (JS_WriteObjectRec(s, rme->attributes)) + goto fail; } bc_put_leb128(s, m->export_entries_count); @@ -37325,8 +37327,13 @@ static JSValue JS_ReadModule(BCReaderState *s) goto fail; for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; + JSValue val; if (bc_get_atom(s, &rme->module_name)) goto fail; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + rme->attributes = val; } } diff --git a/run-test262.c b/run-test262.c index be35834..03b37c6 100644 --- a/run-test262.c +++ b/run-test262.c @@ -872,7 +872,7 @@ static JSModuleDef *js_module_loader_test(JSContext *ctx, return NULL; } - if (js_module_test_json(ctx, attributes)) { + if (js_module_test_json(ctx, attributes) == 1) { /* compile as JSON */ JSValue val; val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name);