summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabrice Bellard <fabrice@bellard.org>2025-05-24 10:24:01 +0200
committerFabrice Bellard <fabrice@bellard.org>2025-05-24 10:24:01 +0200
commit7c487f1c6a0eec73b71cc9941f575d8788a33ead (patch)
tree1769b986e215b9aa05e5f7ce083b4c908b484e0d
parent1dfaa616801a8f559eb7abf232f008a27ff5958a (diff)
downloadquickjs-7c487f1c6a0eec73b71cc9941f575d8788a33ead.tar.gz
quickjs-7c487f1c6a0eec73b71cc9941f575d8788a33ead.zip
support JSON modules in qjsc - added support of JSON5 modules (using type = "json5")
-rw-r--r--examples/hello_module.js4
-rw-r--r--examples/message.json2
-rw-r--r--qjsc.c102
-rw-r--r--quickjs-libc.c71
-rw-r--r--quickjs-libc.h5
-rw-r--r--quickjs.c7
-rw-r--r--run-test262.c2
7 files changed, 149 insertions, 44 deletions
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;
+
+ 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");
- /* the module is already referenced, so we must free it */
- m = JS_VALUE_GET_PTR(func_val);
- JS_FreeValue(ctx, func_val);
+ 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);