Skip to content

Commit 7c487f1

Browse files
author
Fabrice Bellard
committed
support JSON modules in qjsc - added support of JSON5 modules (using type = "json5")
1 parent 1dfaa61 commit 7c487f1

File tree

7 files changed

+149
-44
lines changed

7 files changed

+149
-44
lines changed

examples/hello_module.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
/* example of JS module */
1+
/* example of JS and JSON modules */
22

33
import { fib } from "./fib_module.js";
4+
import msg from "./message.json";
45

56
console.log("Hello World");
67
console.log("fib(10)=", fib(10));
8+
console.log("msg=", msg);

examples/message.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{ "x" : 1, "tab": [ 1, 2, 3 ] }
2+

qjsc.c

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,24 @@ static void dump_hex(FILE *f, const uint8_t *buf, size_t len)
170170
fprintf(f, "\n");
171171
}
172172

173+
typedef enum {
174+
CNAME_TYPE_SCRIPT,
175+
CNAME_TYPE_MODULE,
176+
CNAME_TYPE_JSON_MODULE,
177+
} CNameTypeEnum;
178+
173179
static void output_object_code(JSContext *ctx,
174180
FILE *fo, JSValueConst obj, const char *c_name,
175-
BOOL load_only)
181+
CNameTypeEnum c_name_type)
176182
{
177183
uint8_t *out_buf;
178184
size_t out_buf_len;
179185
int flags;
180-
flags = JS_WRITE_OBJ_BYTECODE;
186+
187+
if (c_name_type == CNAME_TYPE_JSON_MODULE)
188+
flags = 0;
189+
else
190+
flags = JS_WRITE_OBJ_BYTECODE;
181191
if (byte_swap)
182192
flags |= JS_WRITE_OBJ_BSWAP;
183193
out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
@@ -186,7 +196,7 @@ static void output_object_code(JSContext *ctx,
186196
exit(1);
187197
}
188198

189-
namelist_add(&cname_list, c_name, NULL, load_only);
199+
namelist_add(&cname_list, c_name, NULL, c_name_type);
190200

191201
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
192202
c_name, (unsigned int)out_buf_len);
@@ -227,7 +237,8 @@ static void find_unique_cname(char *cname, size_t cname_size)
227237
}
228238

229239
JSModuleDef *jsc_module_loader(JSContext *ctx,
230-
const char *module_name, void *opaque)
240+
const char *module_name, void *opaque,
241+
JSValueConst attributes)
231242
{
232243
JSModuleDef *m;
233244
namelist_entry_t *e;
@@ -249,31 +260,69 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
249260
} else {
250261
size_t buf_len;
251262
uint8_t *buf;
252-
JSValue func_val;
253263
char cname[1024];
254-
264+
int res;
265+
255266
buf = js_load_file(ctx, &buf_len, module_name);
256267
if (!buf) {
257268
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
258269
module_name);
259270
return NULL;
260271
}
261272

262-
/* compile the module */
263-
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
264-
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
265-
js_free(ctx, buf);
266-
if (JS_IsException(func_val))
267-
return NULL;
268-
get_c_name(cname, sizeof(cname), module_name);
269-
if (namelist_find(&cname_list, cname)) {
270-
find_unique_cname(cname, sizeof(cname));
271-
}
272-
output_object_code(ctx, outfile, func_val, cname, TRUE);
273+
res = js_module_test_json(ctx, attributes);
274+
if (has_suffix(module_name, ".json") || res > 0) {
275+
/* compile as JSON or JSON5 depending on "type" */
276+
JSValue val;
277+
int flags;
278+
279+
if (res == 2)
280+
flags = JS_PARSE_JSON_EXT;
281+
else
282+
flags = 0;
283+
val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags);
284+
js_free(ctx, buf);
285+
if (JS_IsException(val))
286+
return NULL;
287+
/* create a dummy module */
288+
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
289+
if (!m) {
290+
JS_FreeValue(ctx, val);
291+
return NULL;
292+
}
293+
294+
get_c_name(cname, sizeof(cname), module_name);
295+
if (namelist_find(&cname_list, cname)) {
296+
find_unique_cname(cname, sizeof(cname));
297+
}
298+
299+
/* output the module name */
300+
fprintf(outfile, "static const uint8_t %s_module_name[] = {\n",
301+
cname);
302+
dump_hex(outfile, (const uint8_t *)module_name, strlen(module_name) + 1);
303+
fprintf(outfile, "};\n\n");
273304

274-
/* the module is already referenced, so we must free it */
275-
m = JS_VALUE_GET_PTR(func_val);
276-
JS_FreeValue(ctx, func_val);
305+
output_object_code(ctx, outfile, val, cname, CNAME_TYPE_JSON_MODULE);
306+
JS_FreeValue(ctx, val);
307+
} else {
308+
JSValue func_val;
309+
310+
/* compile the module */
311+
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
312+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
313+
js_free(ctx, buf);
314+
if (JS_IsException(func_val))
315+
return NULL;
316+
get_c_name(cname, sizeof(cname), module_name);
317+
if (namelist_find(&cname_list, cname)) {
318+
find_unique_cname(cname, sizeof(cname));
319+
}
320+
output_object_code(ctx, outfile, func_val, cname, CNAME_TYPE_MODULE);
321+
322+
/* the module is already referenced, so we must free it */
323+
m = JS_VALUE_GET_PTR(func_val);
324+
JS_FreeValue(ctx, func_val);
325+
}
277326
}
278327
return m;
279328
}
@@ -314,7 +363,7 @@ static void compile_file(JSContext *ctx, FILE *fo,
314363
} else {
315364
get_c_name(c_name, sizeof(c_name), filename);
316365
}
317-
output_object_code(ctx, fo, obj, c_name, FALSE);
366+
output_object_code(ctx, fo, obj, c_name, CNAME_TYPE_SCRIPT);
318367
JS_FreeValue(ctx, obj);
319368
}
320369

@@ -709,7 +758,7 @@ int main(int argc, char **argv)
709758
JS_SetStripInfo(rt, strip_flags);
710759

711760
/* loader for ES6 modules */
712-
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
761+
JS_SetModuleLoaderFunc2(rt, NULL, jsc_module_loader, NULL, NULL);
713762

714763
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
715764
"\n"
@@ -732,7 +781,7 @@ int main(int argc, char **argv)
732781
}
733782

734783
for(i = 0; i < dynamic_module_list.count; i++) {
735-
if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) {
784+
if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL, JS_UNDEFINED)) {
736785
fprintf(stderr, "Could not load dynamic module '%s'\n",
737786
dynamic_module_list.array[i].name);
738787
exit(1);
@@ -770,9 +819,12 @@ int main(int argc, char **argv)
770819
}
771820
for(i = 0; i < cname_list.count; i++) {
772821
namelist_entry_t *e = &cname_list.array[i];
773-
if (e->flags) {
822+
if (e->flags == CNAME_TYPE_MODULE) {
774823
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n",
775824
e->name, e->name);
825+
} else if (e->flags == CNAME_TYPE_JSON_MODULE) {
826+
fprintf(fo, " js_std_eval_binary_json_module(ctx, %s, %s_size, (const char *)%s_module_name);\n",
827+
e->name, e->name, e->name);
776828
}
777829
}
778830
fprintf(fo,
@@ -797,7 +849,7 @@ int main(int argc, char **argv)
797849

798850
for(i = 0; i < cname_list.count; i++) {
799851
namelist_entry_t *e = &cname_list.array[i];
800-
if (!e->flags) {
852+
if (e->flags == CNAME_TYPE_SCRIPT) {
801853
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n",
802854
e->name, e->name);
803855
}

quickjs-libc.c

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,20 @@ static int json_module_init(JSContext *ctx, JSModuleDef *m)
599599
return 0;
600600
}
601601

602+
static JSModuleDef *create_json_module(JSContext *ctx, const char *module_name, JSValue val)
603+
{
604+
JSModuleDef *m;
605+
m = JS_NewCModule(ctx, module_name, json_module_init);
606+
if (!m) {
607+
JS_FreeValue(ctx, val);
608+
return NULL;
609+
}
610+
/* only export the "default" symbol which will contain the JSON object */
611+
JS_AddModuleExport(ctx, m, "default");
612+
JS_SetModulePrivateValue(ctx, m, val);
613+
return m;
614+
}
615+
602616
/* in order to conform with the specification, only the keys should be
603617
tested and not the associated values. */
604618
int js_module_check_attributes(JSContext *ctx, void *opaque,
@@ -631,8 +645,8 @@ int js_module_check_attributes(JSContext *ctx, void *opaque,
631645
return ret;
632646
}
633647

634-
/* return TRUE if the attributes indicate a JSON module */
635-
JS_BOOL js_module_test_json(JSContext *ctx, JSValueConst attributes)
648+
/* return > 0 if the attributes indicate a JSON module */
649+
int js_module_test_json(JSContext *ctx, JSValueConst attributes)
636650
{
637651
JSValue str;
638652
const char *cstr;
@@ -649,7 +663,13 @@ JS_BOOL js_module_test_json(JSContext *ctx, JSValueConst attributes)
649663
if (!cstr)
650664
return FALSE;
651665
/* XXX: raise an error if unknown type ? */
652-
res = (len == 4 && !memcmp(cstr, "json", len));
666+
if (len == 4 && !memcmp(cstr, "json", len)) {
667+
res = 1;
668+
} else if (len == 5 && !memcmp(cstr, "json5", len)) {
669+
res = 2;
670+
} else {
671+
res = 0;
672+
}
653673
JS_FreeCString(ctx, cstr);
654674
return res;
655675
}
@@ -659,7 +679,8 @@ JSModuleDef *js_module_loader(JSContext *ctx,
659679
JSValueConst attributes)
660680
{
661681
JSModuleDef *m;
662-
682+
int res;
683+
663684
if (has_suffix(module_name, ".so")) {
664685
m = js_module_loader_so(ctx, module_name);
665686
} else {
@@ -672,23 +693,22 @@ JSModuleDef *js_module_loader(JSContext *ctx,
672693
module_name);
673694
return NULL;
674695
}
675-
676-
if (has_suffix(module_name, ".json") ||
677-
js_module_test_json(ctx, attributes)) {
678-
/* compile as JSON */
696+
res = js_module_test_json(ctx, attributes);
697+
if (has_suffix(module_name, ".json") || res > 0) {
698+
/* compile as JSON or JSON5 depending on "type" */
679699
JSValue val;
680-
val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name);
700+
int flags;
701+
if (res == 2)
702+
flags = JS_PARSE_JSON_EXT;
703+
else
704+
flags = 0;
705+
val = JS_ParseJSON2(ctx, (char *)buf, buf_len, module_name, flags);
681706
js_free(ctx, buf);
682707
if (JS_IsException(val))
683708
return NULL;
684-
m = JS_NewCModule(ctx, module_name, json_module_init);
685-
if (!m) {
686-
JS_FreeValue(ctx, val);
709+
m = create_json_module(ctx, module_name, val);
710+
if (!m)
687711
return NULL;
688-
}
689-
/* only export the "default" symbol which will contain the JSON object */
690-
JS_AddModuleExport(ctx, m, "default");
691-
JS_SetModulePrivateValue(ctx, m, val);
692712
} else {
693713
JSValue func_val;
694714
/* compile the module */
@@ -4303,3 +4323,22 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
43034323
JS_FreeValue(ctx, val);
43044324
}
43054325
}
4326+
4327+
void js_std_eval_binary_json_module(JSContext *ctx,
4328+
const uint8_t *buf, size_t buf_len,
4329+
const char *module_name)
4330+
{
4331+
JSValue obj;
4332+
JSModuleDef *m;
4333+
4334+
obj = JS_ReadObject(ctx, buf, buf_len, 0);
4335+
if (JS_IsException(obj))
4336+
goto exception;
4337+
m = create_json_module(ctx, module_name, obj);
4338+
if (!m) {
4339+
exception:
4340+
js_std_dump_error(ctx);
4341+
exit(1);
4342+
}
4343+
}
4344+

quickjs-libc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@ void js_std_dump_error(JSContext *ctx);
4444
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
4545
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
4646
JS_BOOL use_realpath, JS_BOOL is_main);
47-
JS_BOOL js_module_test_json(JSContext *ctx, JSValueConst attributes);
47+
int js_module_test_json(JSContext *ctx, JSValueConst attributes);
4848
int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes);
4949
JSModuleDef *js_module_loader(JSContext *ctx,
5050
const char *module_name, void *opaque,
5151
JSValueConst attributes);
5252
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
5353
int flags);
54+
void js_std_eval_binary_json_module(JSContext *ctx,
55+
const uint8_t *buf, size_t buf_len,
56+
const char *module_name);
5457
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
5558
JSValueConst reason,
5659
JS_BOOL is_handled, void *opaque);

quickjs.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36326,6 +36326,8 @@ static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
3632636326
for(i = 0; i < m->req_module_entries_count; i++) {
3632736327
JSReqModuleEntry *rme = &m->req_module_entries[i];
3632836328
bc_put_atom(s, rme->module_name);
36329+
if (JS_WriteObjectRec(s, rme->attributes))
36330+
goto fail;
3632936331
}
3633036332

3633136333
bc_put_leb128(s, m->export_entries_count);
@@ -37325,8 +37327,13 @@ static JSValue JS_ReadModule(BCReaderState *s)
3732537327
goto fail;
3732637328
for(i = 0; i < m->req_module_entries_count; i++) {
3732737329
JSReqModuleEntry *rme = &m->req_module_entries[i];
37330+
JSValue val;
3732837331
if (bc_get_atom(s, &rme->module_name))
3732937332
goto fail;
37333+
val = JS_ReadObjectRec(s);
37334+
if (JS_IsException(val))
37335+
goto fail;
37336+
rme->attributes = val;
3733037337
}
3733137338
}
3733237339

run-test262.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ static JSModuleDef *js_module_loader_test(JSContext *ctx,
872872
return NULL;
873873
}
874874

875-
if (js_module_test_json(ctx, attributes)) {
875+
if (js_module_test_json(ctx, attributes) == 1) {
876876
/* compile as JSON */
877877
JSValue val;
878878
val = JS_ParseJSON(ctx, (char *)buf, buf_len, module_name);

0 commit comments

Comments
 (0)