Add top-level await support
Original author: zamfofex <zamfofex@twdb.moe>
This commit is contained in:
parent
f51616eac8
commit
a9ac7a07ff
5 changed files with 150 additions and 20 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,4 +16,5 @@ test262_*.txt
|
||||||
test_fib.c
|
test_fib.c
|
||||||
tests/bjson.so
|
tests/bjson.so
|
||||||
*.a
|
*.a
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
|
133
quickjs.c
133
quickjs.c
|
@ -785,6 +785,7 @@ struct JSModuleDef {
|
||||||
int import_entries_count;
|
int import_entries_count;
|
||||||
int import_entries_size;
|
int import_entries_size;
|
||||||
|
|
||||||
|
JSValue promise;
|
||||||
JSValue module_ns;
|
JSValue module_ns;
|
||||||
JSValue func_obj; /* only used for JS modules */
|
JSValue func_obj; /* only used for JS modules */
|
||||||
JSModuleInitFunc *init_func; /* only used for C modules */
|
JSModuleInitFunc *init_func; /* only used for C modules */
|
||||||
|
@ -857,6 +858,14 @@ struct JSShape {
|
||||||
JSShapeProperty prop[0]; /* prop_size elements */
|
JSShapeProperty prop[0]; /* prop_size elements */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct JSPromiseData {
|
||||||
|
JSPromiseStateEnum promise_state;
|
||||||
|
/* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
|
||||||
|
struct list_head promise_reactions[2];
|
||||||
|
BOOL is_handled; /* Note: only useful to debug */
|
||||||
|
JSValue promise_result;
|
||||||
|
} JSPromiseData;
|
||||||
|
|
||||||
struct JSObject {
|
struct JSObject {
|
||||||
union {
|
union {
|
||||||
JSGCObjectHeader header;
|
JSGCObjectHeader header;
|
||||||
|
@ -1095,6 +1104,12 @@ static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
|
||||||
static void gc_decref(JSRuntime *rt);
|
static void gc_decref(JSRuntime *rt);
|
||||||
static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
|
static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
|
||||||
const JSClassDef *class_def, JSAtom name);
|
const JSClassDef *class_def, JSAtom name);
|
||||||
|
static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv, int magic);
|
||||||
|
static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv);
|
||||||
|
static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv, int unshift);
|
||||||
|
|
||||||
typedef enum JSStrictEqModeEnum {
|
typedef enum JSStrictEqModeEnum {
|
||||||
JS_EQ_STRICT,
|
JS_EQ_STRICT,
|
||||||
|
@ -27033,6 +27048,7 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
|
||||||
}
|
}
|
||||||
m->header.ref_count = 1;
|
m->header.ref_count = 1;
|
||||||
m->module_name = name;
|
m->module_name = name;
|
||||||
|
m->promise = JS_UNDEFINED;
|
||||||
m->module_ns = JS_UNDEFINED;
|
m->module_ns = JS_UNDEFINED;
|
||||||
m->func_obj = JS_UNDEFINED;
|
m->func_obj = JS_UNDEFINED;
|
||||||
m->eval_exception = JS_UNDEFINED;
|
m->eval_exception = JS_UNDEFINED;
|
||||||
|
@ -27054,6 +27070,7 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_MarkValue(rt, m->promise, mark_func);
|
||||||
JS_MarkValue(rt, m->module_ns, mark_func);
|
JS_MarkValue(rt, m->module_ns, mark_func);
|
||||||
JS_MarkValue(rt, m->func_obj, mark_func);
|
JS_MarkValue(rt, m->func_obj, mark_func);
|
||||||
JS_MarkValue(rt, m->eval_exception, mark_func);
|
JS_MarkValue(rt, m->eval_exception, mark_func);
|
||||||
|
@ -27089,6 +27106,7 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
|
||||||
}
|
}
|
||||||
js_free(ctx, m->import_entries);
|
js_free(ctx, m->import_entries);
|
||||||
|
|
||||||
|
JS_FreeValue(ctx, m->promise);
|
||||||
JS_FreeValue(ctx, m->module_ns);
|
JS_FreeValue(ctx, m->module_ns);
|
||||||
JS_FreeValue(ctx, m->func_obj);
|
JS_FreeValue(ctx, m->func_obj);
|
||||||
JS_FreeValue(ctx, m->eval_exception);
|
JS_FreeValue(ctx, m->eval_exception);
|
||||||
|
@ -28200,6 +28218,18 @@ JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue js_dynamic_import_resolve(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv, int magic, JSValue *func_data)
|
||||||
|
{
|
||||||
|
return JS_Call(ctx, func_data[0], JS_UNDEFINED, 1, (JSValueConst *)&func_data[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_dynamic_import_reject(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv, int magic, JSValue *func_data)
|
||||||
|
{
|
||||||
|
return JS_Call(ctx, func_data[1], JS_UNDEFINED, 1, (JSValueConst *)&argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue js_dynamic_import_job(JSContext *ctx,
|
static JSValue js_dynamic_import_job(JSContext *ctx,
|
||||||
int argc, JSValueConst *argv)
|
int argc, JSValueConst *argv)
|
||||||
{
|
{
|
||||||
|
@ -28232,6 +28262,21 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
|
||||||
if (JS_IsException(ns))
|
if (JS_IsException(ns))
|
||||||
goto exception;
|
goto exception;
|
||||||
|
|
||||||
|
if (!JS_IsUndefined(m->promise)) {
|
||||||
|
JSValueConst args[] = {argv[0], argv[1], ns};
|
||||||
|
JSValueConst funcs[2];
|
||||||
|
funcs[0] = JS_NewCFunctionData(ctx, js_dynamic_import_resolve, 0, 0, 3, args);
|
||||||
|
funcs[1] = JS_NewCFunctionData(ctx, js_dynamic_import_reject, 0, 0, 3, args);
|
||||||
|
JS_FreeValue(ctx, js_promise_then(ctx, m->promise, 2, funcs));
|
||||||
|
|
||||||
|
JS_FreeValue(ctx, (JSValue)funcs[0]);
|
||||||
|
JS_FreeValue(ctx, (JSValue)funcs[1]);
|
||||||
|
JS_FreeValue(ctx, ns);
|
||||||
|
JS_FreeCString(ctx, basename);
|
||||||
|
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
|
ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
|
||||||
1, (JSValueConst *)&ns);
|
1, (JSValueConst *)&ns);
|
||||||
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
|
JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
|
||||||
|
@ -28283,6 +28328,12 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue js_async_function_call2(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv, int magic, JSValue *func_data)
|
||||||
|
{
|
||||||
|
return js_async_function_call(ctx, func_data[0], this_val, argc, argv, magic);
|
||||||
|
}
|
||||||
|
|
||||||
/* Run the <eval> function of the module and of all its requested
|
/* Run the <eval> function of the module and of all its requested
|
||||||
modules. */
|
modules. */
|
||||||
static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
|
static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
|
||||||
|
@ -28300,12 +28351,17 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
|
||||||
if (m->eval_has_exception) {
|
if (m->eval_has_exception) {
|
||||||
return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
|
return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
|
||||||
} else {
|
} else {
|
||||||
return JS_UNDEFINED;
|
return JS_DupValue(ctx, m->promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m->eval_mark = TRUE;
|
m->eval_mark = TRUE;
|
||||||
|
|
||||||
|
JSValueConst promises = JS_NewArray(ctx);
|
||||||
|
if (JS_IsException(promises))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
BOOL async = FALSE;
|
||||||
for(i = 0; i < m->req_module_entries_count; i++) {
|
for(i = 0; i < m->req_module_entries_count; i++) {
|
||||||
JSReqModuleEntry *rme = &m->req_module_entries[i];
|
JSReqModuleEntry *rme = &m->req_module_entries[i];
|
||||||
m1 = rme->module;
|
m1 = rme->module;
|
||||||
|
@ -28313,29 +28369,62 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
|
||||||
ret_val = js_evaluate_module(ctx, m1);
|
ret_val = js_evaluate_module(ctx, m1);
|
||||||
if (JS_IsException(ret_val)) {
|
if (JS_IsException(ret_val)) {
|
||||||
m->eval_mark = FALSE;
|
m->eval_mark = FALSE;
|
||||||
return ret_val;
|
goto clean;
|
||||||
|
}
|
||||||
|
if (!JS_IsUndefined(ret_val)) {
|
||||||
|
js_array_push(ctx, promises, 1, (JSValueConst *)&ret_val, 0);
|
||||||
|
JS_FreeValue(ctx, ret_val);
|
||||||
|
async = TRUE;
|
||||||
}
|
}
|
||||||
JS_FreeValue(ctx, ret_val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSValue promise = js_promise_all(ctx, ctx->promise_ctor, 1, &promises, 0);
|
||||||
|
if (JS_IsException(promise)) {
|
||||||
|
JS_FreeValue(ctx, (JSValue)promises);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
if (m->init_func) {
|
if (m->init_func) {
|
||||||
/* C module init */
|
/* C module init */
|
||||||
if (m->init_func(ctx, m) < 0)
|
if (m->init_func(ctx, m) < 0)
|
||||||
ret_val = JS_EXCEPTION;
|
ret_val = JS_EXCEPTION;
|
||||||
else
|
else
|
||||||
ret_val = JS_UNDEFINED;
|
ret_val = JS_UNDEFINED;
|
||||||
|
} else if (!async) {
|
||||||
|
ret_val = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
|
||||||
|
JS_FreeValue(ctx, m->func_obj);
|
||||||
|
m->func_obj = JS_UNDEFINED;
|
||||||
|
JSPromiseData *s = JS_GetOpaque(ret_val, JS_CLASS_PROMISE);
|
||||||
|
if (s->promise_state != JS_PROMISE_PENDING) {
|
||||||
|
JSValue ret_val2 = ret_val;
|
||||||
|
if (s->promise_state == JS_PROMISE_REJECTED)
|
||||||
|
ret_val = JS_Throw(ctx, JS_DupValue(ctx, s->promise_result));
|
||||||
|
else
|
||||||
|
ret_val = JS_DupValue(ctx, s->promise_result);
|
||||||
|
JS_FreeValue(ctx, ret_val2);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);
|
JSValueConst funcs[2];
|
||||||
|
funcs[0] = JS_NewCFunctionData(ctx, js_async_function_call2, 0, 0, 1, (JSValueConst *)&m->func_obj);
|
||||||
|
funcs[1] = JS_UNDEFINED;
|
||||||
|
ret_val = js_promise_then(ctx, promise, 2, funcs);
|
||||||
|
JS_FreeValue(ctx, (JSValue)funcs[0]);
|
||||||
|
JS_FreeValue(ctx, m->func_obj);
|
||||||
m->func_obj = JS_UNDEFINED;
|
m->func_obj = JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
if (JS_IsException(ret_val)) {
|
if (JS_IsException(ret_val)) {
|
||||||
/* save the thrown exception value */
|
/* save the thrown exception value */
|
||||||
m->eval_has_exception = TRUE;
|
m->eval_has_exception = TRUE;
|
||||||
m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
|
m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
|
||||||
|
} else if (!JS_IsUndefined(ret_val)) {
|
||||||
|
m->promise = JS_DupValue(ctx, ret_val);
|
||||||
}
|
}
|
||||||
m->eval_mark = FALSE;
|
m->eval_mark = FALSE;
|
||||||
m->evaluated = TRUE;
|
m->evaluated = TRUE;
|
||||||
|
clean:
|
||||||
|
JS_FreeValue(ctx, (JSValue)promises);
|
||||||
|
JS_FreeValue(ctx, promise);
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33487,7 +33576,7 @@ static __exception int js_parse_program(JSParseState *s)
|
||||||
|
|
||||||
emit_op(s, OP_return);
|
emit_op(s, OP_return);
|
||||||
} else {
|
} else {
|
||||||
emit_op(s, OP_return_undef);
|
emit_return(s, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -33621,6 +33710,10 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
|
||||||
fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
|
fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
|
||||||
if (!fd)
|
if (!fd)
|
||||||
goto fail1;
|
goto fail1;
|
||||||
|
if (m != NULL) {
|
||||||
|
fd->in_function_body = TRUE;
|
||||||
|
fd->func_kind = JS_FUNC_ASYNC;
|
||||||
|
}
|
||||||
s->cur_func = fd;
|
s->cur_func = fd;
|
||||||
fd->eval_type = eval_type;
|
fd->eval_type = eval_type;
|
||||||
fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
|
fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
|
||||||
|
@ -46268,20 +46361,6 @@ static const JSCFunctionListEntry js_generator_proto_funcs[] = {
|
||||||
|
|
||||||
/* Promise */
|
/* Promise */
|
||||||
|
|
||||||
typedef enum JSPromiseStateEnum {
|
|
||||||
JS_PROMISE_PENDING,
|
|
||||||
JS_PROMISE_FULFILLED,
|
|
||||||
JS_PROMISE_REJECTED,
|
|
||||||
} JSPromiseStateEnum;
|
|
||||||
|
|
||||||
typedef struct JSPromiseData {
|
|
||||||
JSPromiseStateEnum promise_state;
|
|
||||||
/* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
|
|
||||||
struct list_head promise_reactions[2];
|
|
||||||
BOOL is_handled; /* Note: only useful to debug */
|
|
||||||
JSValue promise_result;
|
|
||||||
} JSPromiseData;
|
|
||||||
|
|
||||||
typedef struct JSPromiseFunctionDataResolved {
|
typedef struct JSPromiseFunctionDataResolved {
|
||||||
int ref_count;
|
int ref_count;
|
||||||
BOOL already_resolved;
|
BOOL already_resolved;
|
||||||
|
@ -46298,6 +46377,22 @@ typedef struct JSPromiseReactionData {
|
||||||
JSValue handler;
|
JSValue handler;
|
||||||
} JSPromiseReactionData;
|
} JSPromiseReactionData;
|
||||||
|
|
||||||
|
JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise)
|
||||||
|
{
|
||||||
|
JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
|
||||||
|
if (!s)
|
||||||
|
return -1;
|
||||||
|
return s->promise_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue JS_PromiseResult(JSContext *ctx, JSValue promise)
|
||||||
|
{
|
||||||
|
JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
|
||||||
|
if (!s)
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
return JS_DupValue(ctx, s->promise_result);
|
||||||
|
}
|
||||||
|
|
||||||
static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
|
static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
|
||||||
JSValueConst promise);
|
JSValueConst promise);
|
||||||
|
|
||||||
|
|
11
quickjs.h
11
quickjs.h
|
@ -1039,6 +1039,17 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
|
||||||
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
|
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
|
||||||
const JSCFunctionListEntry *tab, int len);
|
const JSCFunctionListEntry *tab, int len);
|
||||||
|
|
||||||
|
/* Promise */
|
||||||
|
|
||||||
|
typedef enum JSPromiseStateEnum {
|
||||||
|
JS_PROMISE_PENDING,
|
||||||
|
JS_PROMISE_FULFILLED,
|
||||||
|
JS_PROMISE_REJECTED,
|
||||||
|
} JSPromiseStateEnum;
|
||||||
|
|
||||||
|
JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise);
|
||||||
|
JSValue JS_PromiseResult(JSContext *ctx, JSValue promise);
|
||||||
|
|
||||||
#undef js_unlikely
|
#undef js_unlikely
|
||||||
#undef js_force_inline
|
#undef js_force_inline
|
||||||
|
|
||||||
|
|
|
@ -1205,6 +1205,29 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if ((eval_flags & JS_EVAL_TYPE_MODULE) &&
|
||||||
|
!JS_IsUndefined(res_val) &&
|
||||||
|
!JS_IsException(res_val)) {
|
||||||
|
JSValue promise = res_val;
|
||||||
|
for(;;) {
|
||||||
|
JSContext *ctx1;
|
||||||
|
ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
|
||||||
|
if (ret < 0) {
|
||||||
|
res_val = JS_EXCEPTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret == 0) {
|
||||||
|
JSPromiseStateEnum s = JS_PromiseState(ctx, promise);
|
||||||
|
if (s == JS_PROMISE_FULFILLED)
|
||||||
|
res_val = JS_UNDEFINED;
|
||||||
|
else if (s == JS_PROMISE_REJECTED)
|
||||||
|
res_val = JS_Throw(ctx, JS_PromiseResult(ctx, promise));
|
||||||
|
else
|
||||||
|
res_val = JS_EXCEPTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JS_FreeValue(ctx, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JS_IsException(res_val)) {
|
if (JS_IsException(res_val)) {
|
||||||
|
|
|
@ -174,7 +174,7 @@ Symbol.unscopables
|
||||||
tail-call-optimization=skip
|
tail-call-optimization=skip
|
||||||
template
|
template
|
||||||
Temporal=skip
|
Temporal=skip
|
||||||
top-level-await=skip
|
top-level-await
|
||||||
TypedArray
|
TypedArray
|
||||||
TypedArray.prototype.at=skip
|
TypedArray.prototype.at=skip
|
||||||
u180e
|
u180e
|
||||||
|
|
Loading…
Reference in a new issue