]> git.kaiwu.me - quickjs.git/commitdiff
support JSON modules in qjsc - added support of JSON5 modules (using type = "json5")
authorFabrice Bellard <fabrice@bellard.org>
Sat, 24 May 2025 08:24:01 +0000 (10:24 +0200)
committerFabrice Bellard <fabrice@bellard.org>
Sat, 24 May 2025 08:24:01 +0000 (10:24 +0200)
examples/hello_module.js
examples/message.json [new file with mode: 0644]
qjsc.c
quickjs-libc.c
quickjs-libc.h
quickjs.c
run-test262.c

index 463660f6a925e00991cd326b22ad0c8037c40f54..5d4c78ef28e1a4ca891197435c8e4f418c7c6b06 100644 (file)
@@ -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 (file)
index 0000000..3b7fe48
--- /dev/null
@@ -0,0 +1,2 @@
+{ "x" : 1, "tab": [ 1, 2, 3 ] }
+
diff --git a/qjsc.c b/qjsc.c
index 4c6b28bf0ce89f04e92994774eeb44c307d1b170..49aa4498570fbdfbac0b7f98359f3020f82fd4f2 100644 (file)
--- 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);
             }
index ca8e359cc4a0c0f0ecddfa4d18df872880c8f08b..10a7d00cc25d373bf9e8ed36f9f1c750967e8e00 100644 (file)
@@ -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);
+    }
+}
+
index f24e161b14aaeb2ca7f5158213b6bc167d3eb79b..5c8301b7173a3c7c97cc177f5d53d2b2a48e64e9 100644 (file)
@@ -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);
index e56adb87933cc04540ce854bab94b32638aef17d..35411ef187e2447c3389534f81a391f50efc4a02 100644 (file)
--- 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;
         }
     }
 
index be358342c4cd76c2a236ed7cd7f48f377b86af72..03b37c6e1d479b22cacfcb5285e0287411982943 100644 (file)
@@ -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);