-
Notifications
You must be signed in to change notification settings - Fork 181
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
bc9e115
716c808
455ed06
939829c
15d4571
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -472,6 +472,10 @@ struct JSContext { | |||||||
|
||||||||
struct list_head loaded_modules; /* list of JSModuleDef.link */ | ||||||||
|
||||||||
/* To minimize creating breaking changes, I have instead decided | ||||||||
to carry the parsed import_assertion instead */ | ||||||||
JSValue import_assertion; | ||||||||
|
||||||||
/* if NULL, RegExp compilation is not supported */ | ||||||||
JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, | ||||||||
JSValueConst flags); | ||||||||
|
@@ -855,6 +859,9 @@ struct JSModuleDef { | |||||||
bool eval_has_exception; | ||||||||
JSValue eval_exception; | ||||||||
JSValue meta_obj; /* for import.meta */ | ||||||||
|
||||||||
/* a list of key/value strings - [key, value, key, value] */ | ||||||||
JSValue import_assertion; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mark/free this in js_mark_module_def & js_free_module_def. |
||||||||
}; | ||||||||
|
||||||||
typedef struct JSJobEntry { | ||||||||
|
@@ -1247,7 +1254,7 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m); | |||||||
static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, | ||||||||
JS_MarkFunc *mark_func); | ||||||||
static JSValue js_import_meta(JSContext *ctx); | ||||||||
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); | ||||||||
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst import_assertion); | ||||||||
static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); | ||||||||
static JSValue js_new_promise_capability(JSContext *ctx, | ||||||||
JSValue *resolving_funcs, | ||||||||
|
@@ -2307,6 +2314,7 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) | |||||||
ctx->error_back_trace = JS_UNDEFINED; | ||||||||
ctx->error_prepare_stack = JS_UNDEFINED; | ||||||||
ctx->error_stack_trace_limit = js_int32(10); | ||||||||
ctx->import_assertion = JS_UNDEFINED; | ||||||||
init_list_head(&ctx->loaded_modules); | ||||||||
|
||||||||
JS_AddIntrinsicBasicObjects(ctx); | ||||||||
|
@@ -17119,10 +17127,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, | |||||||
{ | ||||||||
JSValue val; | ||||||||
sf->cur_pc = pc; | ||||||||
val = js_dynamic_import(ctx, sp[-1]); | ||||||||
val = js_dynamic_import(ctx, sp[-2], sp[-1]); | ||||||||
if (JS_IsException(val)) | ||||||||
goto exception; | ||||||||
JS_FreeValue(ctx, sp[-1]); | ||||||||
JS_FreeValue(ctx, sp[-2]); | ||||||||
sp[-1] = val; | ||||||||
} | ||||||||
BREAK; | ||||||||
|
@@ -25145,6 +25154,14 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) | |||||||
return js_parse_error(s, "invalid use of 'import()'"); | ||||||||
if (js_parse_assign_expr(s)) | ||||||||
return -1; | ||||||||
if (s->token.val == ',') { | ||||||||
if (next_token(s)) | ||||||||
return -1; | ||||||||
if (js_parse_object_literal(s)) | ||||||||
return -1; | ||||||||
} else | ||||||||
emit_op(s, OP_undefined); | ||||||||
|
||||||||
if (js_parse_expect(s, ')')) | ||||||||
return -1; | ||||||||
emit_op(s, OP_import); | ||||||||
|
@@ -27684,6 +27701,7 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) | |||||||
m->eval_exception = JS_UNDEFINED; | ||||||||
m->meta_obj = JS_UNDEFINED; | ||||||||
m->promise = JS_UNDEFINED; | ||||||||
m->import_assertion = JS_NewArray(ctx); | ||||||||
m->resolving_funcs[0] = JS_UNDEFINED; | ||||||||
m->resolving_funcs[1] = JS_UNDEFINED; | ||||||||
list_add_tail(&m->link, &ctx->loaded_modules); | ||||||||
|
@@ -27707,6 +27725,7 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, | |||||||
JS_MarkValue(rt, m->func_obj, mark_func); | ||||||||
JS_MarkValue(rt, m->eval_exception, mark_func); | ||||||||
JS_MarkValue(rt, m->meta_obj, mark_func); | ||||||||
JS_MarkValue(rt, m->import_assertion, mark_func); | ||||||||
JS_MarkValue(rt, m->promise, mark_func); | ||||||||
JS_MarkValue(rt, m->resolving_funcs[0], mark_func); | ||||||||
JS_MarkValue(rt, m->resolving_funcs[1], mark_func); | ||||||||
|
@@ -28479,6 +28498,11 @@ JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) | |||||||
return js_dup(m->module_ns); | ||||||||
} | ||||||||
|
||||||||
JSValue JS_GetImportAssertion(JSContext *ctx) | ||||||||
{ | ||||||||
return ctx->import_assertion; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Or change the return type to JSValueConst but that's much less idiomatic (even internally we only have three functions that do that.) |
||||||||
} | ||||||||
|
||||||||
#ifdef ENABLE_DUMPS // JS_DUMP_MODULE_RESOLVE | ||||||||
#define module_trace(ctx, ...) \ | ||||||||
do { \ | ||||||||
|
@@ -28504,6 +28528,7 @@ static int js_resolve_module(JSContext *ctx, JSModuleDef *m) | |||||||
} | ||||||||
#endif | ||||||||
m->resolved = true; | ||||||||
ctx->import_assertion = m->import_assertion; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
/* resolve each requested module */ | ||||||||
for(i = 0; i < m->req_module_entries_count; i++) { | ||||||||
JSReqModuleEntry *rme = &m->req_module_entries[i]; | ||||||||
|
@@ -28517,6 +28542,7 @@ static int js_resolve_module(JSContext *ctx, JSModuleDef *m) | |||||||
if (js_resolve_module(ctx, m1) < 0) | ||||||||
return -1; | ||||||||
} | ||||||||
ctx->import_assertion = JS_UNDEFINED; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
return 0; | ||||||||
} | ||||||||
|
||||||||
|
@@ -29042,6 +29068,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx, | |||||||
JSValueConst *resolving_funcs = argv; | ||||||||
JSValueConst basename_val = argv[2]; | ||||||||
JSValueConst specifier = argv[3]; | ||||||||
ctx->import_assertion = argv[4]; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like it should either js_dup or the JS_FreeValue further down shouldn't be there. argv is JSValueConst, meaning the caller doesn't transfer ownership. |
||||||||
const char *basename = NULL, *filename; | ||||||||
JSValue ret, err; | ||||||||
|
||||||||
|
@@ -29068,14 +29095,15 @@ static JSValue js_dynamic_import_job(JSContext *ctx, | |||||||
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ | ||||||||
JS_FreeValue(ctx, err); | ||||||||
JS_FreeCString(ctx, basename); | ||||||||
JS_FreeValue(ctx, argv[4]); | ||||||||
return JS_UNDEFINED; | ||||||||
} | ||||||||
|
||||||||
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) | ||||||||
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst import_assertion) | ||||||||
{ | ||||||||
JSAtom basename; | ||||||||
JSValue promise, resolving_funcs[2], basename_val; | ||||||||
JSValue args[4]; | ||||||||
JSValue promise, resolving_funcs[2], basename_val, assertion; | ||||||||
JSValue args[5]; | ||||||||
|
||||||||
basename = JS_GetScriptOrModuleName(ctx, 0); | ||||||||
if (basename == JS_ATOM_NULL) | ||||||||
|
@@ -29092,14 +29120,46 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) | |||||||
return promise; | ||||||||
} | ||||||||
|
||||||||
assertion = JS_NewArray(ctx); | ||||||||
|
||||||||
if (JS_IsObject(import_assertion)) { | ||||||||
if (!JS_HasProperty(ctx, import_assertion, JS_NewAtom(ctx, "with"))) | ||||||||
return JS_ThrowTypeError(ctx, "expected 'with' property"); | ||||||||
|
||||||||
JSValue obj = JS_GetPropertyStr(ctx, import_assertion, "with"); | ||||||||
if (!JS_IsObject(obj) || JS_IsArray(obj)) | ||||||||
return JS_ThrowTypeError(ctx, "expected object"); | ||||||||
|
||||||||
JSPropertyEnum *props; | ||||||||
uint32_t index, len; | ||||||||
index = 0; | ||||||||
|
||||||||
JS_GetOwnPropertyNames(ctx, &props, &len, obj, JS_GPN_STRING_MASK); | ||||||||
|
||||||||
for (uint32_t i = 0; i < len; i++) { | ||||||||
JSValue key = JS_AtomToString(ctx, props[i].atom); | ||||||||
JSValue val = JS_GetProperty(ctx, obj, props[i].atom); | ||||||||
|
||||||||
JS_DefinePropertyValueUint32(ctx, assertion, index, | ||||||||
key, JS_PROP_HAS_ENUMERABLE); | ||||||||
index++; | ||||||||
JS_DefinePropertyValueUint32(ctx, assertion, index, | ||||||||
val, JS_PROP_HAS_ENUMERABLE); | ||||||||
index++; | ||||||||
} | ||||||||
|
||||||||
JS_FreePropertyEnum(ctx, props, len); | ||||||||
} | ||||||||
|
||||||||
args[0] = resolving_funcs[0]; | ||||||||
args[1] = resolving_funcs[1]; | ||||||||
args[2] = basename_val; | ||||||||
args[3] = unsafe_unconst(specifier); | ||||||||
args[4] = assertion; | ||||||||
|
||||||||
/* cannot run JS_LoadModuleInternal synchronously because it would | ||||||||
cause an unexpected recursion in js_evaluate_module() */ | ||||||||
JS_EnqueueJob(ctx, js_dynamic_import_job, 4, vc(args)); | ||||||||
JS_EnqueueJob(ctx, js_dynamic_import_job, 5, vc(args)); | ||||||||
|
||||||||
JS_FreeValue(ctx, basename_val); | ||||||||
JS_FreeValue(ctx, resolving_funcs[0]); | ||||||||
|
@@ -29732,6 +29792,65 @@ static int add_import(JSParseState *s, JSModuleDef *m, | |||||||
return 0; | ||||||||
} | ||||||||
|
||||||||
static __exception int js_parse_import_assertion(JSParseState *s, JSModuleDef *m) | ||||||||
{ | ||||||||
uint32_t index = 0; | ||||||||
|
||||||||
if (next_token(s)) | ||||||||
return -1; | ||||||||
|
||||||||
if (js_parse_expect(s, '{')) | ||||||||
return -1; | ||||||||
|
||||||||
while (s->token.val != '}') { | ||||||||
JSValue key, value; | ||||||||
|
||||||||
if (!token_is_ident(s->token.val) && s->token.val != TOK_STRING) { | ||||||||
js_parse_error(s, "identifier or string expected"); | ||||||||
return -1; | ||||||||
} | ||||||||
|
||||||||
if (token_is_ident(s->token.val)) | ||||||||
key = JS_AtomToValue(s->ctx, s->token.u.ident.atom); | ||||||||
else | ||||||||
key = js_dup(s->token.u.str.str); | ||||||||
|
||||||||
if (next_token(s)) | ||||||||
goto fail; | ||||||||
|
||||||||
if (js_parse_expect(s, ':')) | ||||||||
goto fail; | ||||||||
|
||||||||
if (s->token.val != TOK_STRING) { | ||||||||
js_parse_error(s, "string expected"); | ||||||||
goto fail; | ||||||||
} | ||||||||
|
||||||||
value = js_dup(s->token.u.str.str); | ||||||||
|
||||||||
JS_DefinePropertyValueUint32(s->ctx, m->import_assertion, index, | ||||||||
key, JS_PROP_HAS_ENUMERABLE); | ||||||||
index++; | ||||||||
JS_DefinePropertyValueUint32(s->ctx, m->import_assertion, index, | ||||||||
value, JS_PROP_HAS_ENUMERABLE); | ||||||||
index++; | ||||||||
|
||||||||
if (next_token(s)) | ||||||||
goto fail; | ||||||||
|
||||||||
continue; | ||||||||
fail: | ||||||||
JS_FreeValue(s->ctx, key); | ||||||||
JS_FreeValue(s->ctx, value); | ||||||||
return -1; | ||||||||
} | ||||||||
|
||||||||
if (next_token(s)) | ||||||||
return -1; | ||||||||
|
||||||||
return 0; | ||||||||
} | ||||||||
|
||||||||
static __exception int js_parse_import(JSParseState *s) | ||||||||
{ | ||||||||
JSContext *ctx = s->ctx; | ||||||||
|
@@ -29836,6 +29955,10 @@ static __exception int js_parse_import(JSParseState *s) | |||||||
module_name = js_parse_from_clause(s); | ||||||||
if (module_name == JS_ATOM_NULL) | ||||||||
return -1; | ||||||||
|
||||||||
if (s->token.val == TOK_WITH) | ||||||||
if (js_parse_import_assertion(s, m) < 0) | ||||||||
return -1; | ||||||||
} | ||||||||
idx = add_req_module_entry(ctx, m, module_name); | ||||||||
JS_FreeAtom(ctx, module_name); | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark/free this in JS_MarkContext & JS_FreeContext.