Skip to content

WIP: Implement Import assertions and JSON modules #1039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 91 additions & 11 deletions quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,35 @@ int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
return 0;
}

static int js_module_loader_json(JSContext *ctx, JSModuleDef *m)
{
size_t buf_len;
uint8_t *buf;
JSValue parsed;
JSPropertyEnum *props;
uint32_t len;
JSAtom module_name = JS_GetModuleName(ctx, m);
const char *module_name_cstr = JS_AtomToCString(ctx, module_name);

buf = js_load_file(ctx, &buf_len, module_name_cstr);

/* XXX: Not ideal to parse the file twice, but didn't want to introduce
extra state to JSModuleDef like opaque */
parsed = JS_ParseJSON(ctx, (const char*) buf, buf_len, module_name_cstr);

JS_GetOwnPropertyNames(ctx, &props, &len, parsed, JS_GPN_STRING_MASK);

for (uint32_t i = 0; i < len; i++) {
JSValue val = JS_GetProperty(ctx, parsed, props[i].atom);
JS_SetModuleExport(ctx, m, JS_AtomToCString(ctx, props[i].atom), val);
}

JS_FreePropertyEnum(ctx, props, len);
JS_FreeValue(ctx, parsed);
js_free(ctx, buf);
return 0;
}

JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque)
{
Expand All @@ -788,19 +817,70 @@ JSModuleDef *js_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;
if (js_module_set_import_meta(ctx, func_val, true, false) < 0) {
JSValueConst with_clause = JS_GetImportAssertion(ctx);
bool is_json = false;
if (JS_IsArray(with_clause)) {
int64_t array_len;
JS_GetLength(ctx, with_clause, &array_len);
for (int64_t i = 0; i < array_len; i += 2) {
JSValue prop = JS_GetPropertyInt64(ctx, with_clause, i);
const char *name = JS_ToCString(ctx, prop);
if (strcmp(name, "type") == 0) {
JSValue key = JS_GetPropertyInt64(ctx, with_clause, i + 1);
if (!JS_IsString(key)) {
JS_ThrowTypeError(ctx, "value of 'type' is expecting string");
return NULL;
}

const char *str = JS_ToCString(ctx, key);
if (strcmp(str, "json") != 0) {
JS_FreeValue(ctx, key);
JS_FreeCString(ctx, str);
JS_ThrowTypeError(ctx, "'type' is not 'json'");
return NULL;
}
JS_FreeValue(ctx, key);
JS_FreeCString(ctx, str);
is_json = true;
break;
}
}
}

if (is_json) {
m = JS_NewCModule(ctx, module_name, js_module_loader_json);

if (!m)
return NULL;

JSValue parsed = JS_ParseJSON(ctx, (const char*) buf, buf_len, module_name);

JSPropertyEnum *props;
uint32_t len;

JS_GetOwnPropertyNames(ctx, &props, &len, parsed, JS_GPN_STRING_MASK);

for (uint32_t i = 0; i < len; i++) {
JS_AddModuleExport(ctx, m, JS_AtomToCString(ctx, props[i].atom));
}

JS_FreePropertyEnum(ctx, props, len);
JS_FreeValue(ctx, parsed);
} else {
/* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
if (JS_IsException(func_val))
return NULL;
if (js_module_set_import_meta(ctx, func_val, true, false) < 0) {
JS_FreeValue(ctx, func_val);
return NULL;
}
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
return NULL;
}
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
js_free(ctx, buf);
}
return m;
}
Expand Down
2 changes: 1 addition & 1 deletion quickjs-opcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
DEF( get_super, 1, 1, 1, none)
DEF( import, 1, 1, 1, none) /* dynamic module import */
DEF( import, 1, 2, 1, none) /* dynamic module import */

DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
Expand Down
Loading
Loading