fixed the garbage collection of async functions with closures (github issue #156)
This commit is contained in:
parent
399d916e66
commit
7414e5f67f
2 changed files with 302 additions and 247 deletions
525
quickjs.c
525
quickjs.c
|
@ -318,17 +318,18 @@ struct JSClass {
|
|||
#define JS_MODE_STRICT (1 << 0)
|
||||
#define JS_MODE_STRIP (1 << 1)
|
||||
#define JS_MODE_MATH (1 << 2)
|
||||
#define JS_MODE_ASYNC (1 << 3) /* async function */
|
||||
|
||||
typedef struct JSStackFrame {
|
||||
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
|
||||
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
|
||||
JSValue *arg_buf; /* arguments */
|
||||
JSValue *var_buf; /* variables */
|
||||
struct list_head var_ref_list; /* list of JSVarRef.link */
|
||||
struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */
|
||||
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
|
||||
instruction after the call */
|
||||
int arg_count;
|
||||
int js_mode; /* 0 or JS_MODE_MATH for C functions */
|
||||
int js_mode; /* for C functions, only JS_MODE_MATH may be set */
|
||||
/* only used in generators. Current stack pointer value. NULL if
|
||||
the function is running. */
|
||||
JSValue *cur_sp;
|
||||
|
@ -361,11 +362,6 @@ typedef struct JSVarRef {
|
|||
struct {
|
||||
int __gc_ref_count; /* corresponds to header.ref_count */
|
||||
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
|
||||
|
||||
/* 0 : the JSVarRef is on the stack. header.link is an element
|
||||
of JSStackFrame.var_ref_list.
|
||||
1 : the JSVarRef is detached. header.link has the normal meanning
|
||||
*/
|
||||
uint8_t is_detached : 1;
|
||||
uint8_t is_arg : 1;
|
||||
uint16_t var_idx; /* index of the corresponding function variable on
|
||||
|
@ -374,7 +370,13 @@ typedef struct JSVarRef {
|
|||
};
|
||||
JSValue *pvalue; /* pointer to the value, either on the stack or
|
||||
to 'value' */
|
||||
JSValue value; /* used when the variable is no longer on the stack */
|
||||
union {
|
||||
JSValue value; /* used when is_detached = TRUE */
|
||||
struct {
|
||||
struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
|
||||
struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
|
||||
}; /* used when is_detached = FALSE */
|
||||
};
|
||||
} JSVarRef;
|
||||
|
||||
/* the same structure is used for big integers and big floats. Big
|
||||
|
@ -671,21 +673,16 @@ typedef struct JSTypedArray {
|
|||
} JSTypedArray;
|
||||
|
||||
typedef struct JSAsyncFunctionState {
|
||||
JSValue this_val; /* 'this' generator argument */
|
||||
JSGCObjectHeader header;
|
||||
JSValue this_val; /* 'this' argument */
|
||||
int argc; /* number of function arguments */
|
||||
BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
|
||||
BOOL is_completed; /* TRUE if the function has returned. The stack
|
||||
frame is no longer valid */
|
||||
JSValue resolving_funcs[2]; /* only used in JS async functions */
|
||||
JSStackFrame frame;
|
||||
} JSAsyncFunctionState;
|
||||
|
||||
/* XXX: could use an object instead to avoid the
|
||||
JS_TAG_ASYNC_FUNCTION tag for the GC */
|
||||
typedef struct JSAsyncFunctionData {
|
||||
JSGCObjectHeader header; /* must come first */
|
||||
JSValue resolving_funcs[2];
|
||||
BOOL is_active; /* true if the async function state is valid */
|
||||
JSAsyncFunctionState func_state;
|
||||
} JSAsyncFunctionData;
|
||||
|
||||
typedef enum {
|
||||
/* binary operators */
|
||||
JS_OVOP_ADD,
|
||||
|
@ -925,7 +922,7 @@ struct JSObject {
|
|||
struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
|
||||
struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
|
||||
struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
|
||||
struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
|
||||
struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
|
||||
struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
|
||||
struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
|
||||
struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
|
||||
|
@ -1201,6 +1198,8 @@ static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
|
|||
static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
|
||||
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
|
||||
BOOL is_arg);
|
||||
static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
|
||||
static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
|
||||
static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
||||
JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv,
|
||||
|
@ -1239,8 +1238,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
|
|||
static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
|
||||
JSObject *p, JSAtom prop);
|
||||
static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
|
||||
static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
|
||||
JS_MarkFunc *mark_func);
|
||||
static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
|
||||
static void js_free_shape(JSRuntime *rt, JSShape *sh);
|
||||
static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
|
||||
|
@ -1268,7 +1265,6 @@ static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
|
|||
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
|
||||
JSGCObjectTypeEnum type);
|
||||
static void remove_gc_object(JSGCObjectHeader *h);
|
||||
static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
|
||||
static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
|
||||
static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
|
||||
void *opaque);
|
||||
|
@ -5254,10 +5250,12 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
|
|||
if (--var_ref->header.ref_count == 0) {
|
||||
if (var_ref->is_detached) {
|
||||
JS_FreeValueRT(rt, var_ref->value);
|
||||
remove_gc_object(&var_ref->header);
|
||||
} else {
|
||||
list_del(&var_ref->header.link); /* still on the stack */
|
||||
list_del(&var_ref->var_ref_link); /* still on the stack */
|
||||
if (var_ref->async_func)
|
||||
async_func_free(rt, var_ref->async_func);
|
||||
}
|
||||
remove_gc_object(&var_ref->header);
|
||||
js_free_rt(rt, var_ref);
|
||||
}
|
||||
}
|
||||
|
@ -5355,7 +5353,7 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
|
|||
if (var_refs) {
|
||||
for(i = 0; i < b->closure_var_count; i++) {
|
||||
JSVarRef *var_ref = var_refs[i];
|
||||
if (var_ref && var_ref->is_detached) {
|
||||
if (var_ref) {
|
||||
mark_func(rt, &var_ref->header);
|
||||
}
|
||||
}
|
||||
|
@ -5465,6 +5463,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
|
|||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||
free_function_bytecode(rt, (JSFunctionBytecode *)gp);
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||
__async_func_free(rt, (JSAsyncFunctionState *)gp);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -5623,11 +5624,9 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
|
|||
if (pr->u.getset.setter)
|
||||
mark_func(rt, &pr->u.getset.setter->header);
|
||||
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
|
||||
if (pr->u.var_ref->is_detached) {
|
||||
/* Note: the tag does not matter
|
||||
provided it is a GC object */
|
||||
mark_func(rt, &pr->u.var_ref->header);
|
||||
}
|
||||
/* Note: the tag does not matter
|
||||
provided it is a GC object */
|
||||
mark_func(rt, &pr->u.var_ref->header);
|
||||
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
|
||||
js_autoinit_mark(rt, pr, mark_func);
|
||||
}
|
||||
|
@ -5661,16 +5660,32 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
|
|||
case JS_GC_OBJ_TYPE_VAR_REF:
|
||||
{
|
||||
JSVarRef *var_ref = (JSVarRef *)gp;
|
||||
/* only detached variable referenced are taken into account */
|
||||
assert(var_ref->is_detached);
|
||||
JS_MarkValue(rt, *var_ref->pvalue, mark_func);
|
||||
if (var_ref->is_detached) {
|
||||
JS_MarkValue(rt, *var_ref->pvalue, mark_func);
|
||||
} else if (var_ref->async_func) {
|
||||
mark_func(rt, &var_ref->async_func->header);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||
{
|
||||
JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
|
||||
if (s->is_active)
|
||||
async_func_mark(rt, &s->func_state, mark_func);
|
||||
JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp;
|
||||
JSStackFrame *sf = &s->frame;
|
||||
JSValue *sp;
|
||||
|
||||
if (!s->is_completed) {
|
||||
JS_MarkValue(rt, sf->cur_func, mark_func);
|
||||
JS_MarkValue(rt, s->this_val, mark_func);
|
||||
/* sf->cur_sp = NULL if the function is running */
|
||||
if (sf->cur_sp) {
|
||||
/* if the function is running, cur_sp is not known so we
|
||||
cannot mark the stack. Marking the variables is not needed
|
||||
because a running function cannot be part of a removable
|
||||
cycle */
|
||||
for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
|
||||
JS_MarkValue(rt, *sp, mark_func);
|
||||
}
|
||||
}
|
||||
JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
|
||||
JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
|
||||
}
|
||||
|
@ -5778,12 +5793,13 @@ static void gc_free_cycles(JSRuntime *rt)
|
|||
if (el == &rt->tmp_obj_list)
|
||||
break;
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
/* Only need to free the GC object associated with JS
|
||||
values. The rest will be automatically removed because they
|
||||
must be referenced by them. */
|
||||
/* Only need to free the GC object associated with JS values
|
||||
or async functions. The rest will be automatically removed
|
||||
because they must be referenced by them. */
|
||||
switch(p->gc_obj_type) {
|
||||
case JS_GC_OBJ_TYPE_JS_OBJECT:
|
||||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||
#ifdef DUMP_GC_FREE
|
||||
if (!header_done) {
|
||||
printf("Freeing cycles:\n");
|
||||
|
@ -5805,7 +5821,8 @@ static void gc_free_cycles(JSRuntime *rt)
|
|||
list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
|
||||
p = list_entry(el, JSGCObjectHeader, link);
|
||||
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
|
||||
p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
|
||||
p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
|
||||
p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
|
||||
js_free_rt(rt, p);
|
||||
}
|
||||
|
||||
|
@ -15429,7 +15446,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
|
|||
struct list_head *el;
|
||||
|
||||
list_for_each(el, &sf->var_ref_list) {
|
||||
var_ref = list_entry(el, JSVarRef, header.link);
|
||||
var_ref = list_entry(el, JSVarRef, var_ref_link);
|
||||
if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
|
||||
var_ref->header.ref_count++;
|
||||
return var_ref;
|
||||
|
@ -15440,15 +15457,29 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
|
|||
if (!var_ref)
|
||||
return NULL;
|
||||
var_ref->header.ref_count = 1;
|
||||
add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
|
||||
var_ref->is_detached = FALSE;
|
||||
var_ref->is_arg = is_arg;
|
||||
var_ref->var_idx = var_idx;
|
||||
list_add_tail(&var_ref->header.link, &sf->var_ref_list);
|
||||
list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list);
|
||||
if (sf->js_mode & JS_MODE_ASYNC) {
|
||||
/* The stack frame is detached and may be destroyed at any
|
||||
time so its reference count must be increased. Calling
|
||||
close_var_refs() when destroying the stack frame is not
|
||||
possible because it would change the graph between the GC
|
||||
objects. Another solution could be to temporarily detach
|
||||
the JSVarRef of async functions during the GC. It would
|
||||
have the advantage of allowing the release of unused stack
|
||||
frames in a cycle. */
|
||||
var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame);
|
||||
var_ref->async_func->header.ref_count++;
|
||||
} else {
|
||||
var_ref->async_func = NULL;
|
||||
}
|
||||
if (is_arg)
|
||||
var_ref->pvalue = &sf->arg_buf[var_idx];
|
||||
else
|
||||
var_ref->pvalue = &sf->var_buf[var_idx];
|
||||
var_ref->value = JS_UNDEFINED;
|
||||
return var_ref;
|
||||
}
|
||||
|
||||
|
@ -15679,7 +15710,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
|
|||
int var_idx;
|
||||
|
||||
list_for_each_safe(el, el1, &sf->var_ref_list) {
|
||||
var_ref = list_entry(el, JSVarRef, header.link);
|
||||
var_ref = list_entry(el, JSVarRef, var_ref_link);
|
||||
/* no need to unlink var_ref->var_ref_link as the list is never used afterwards */
|
||||
if (var_ref->async_func)
|
||||
async_func_free(rt, var_ref->async_func);
|
||||
var_idx = var_ref->var_idx;
|
||||
if (var_ref->is_arg)
|
||||
var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
|
||||
|
@ -15688,7 +15722,6 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
|
|||
var_ref->pvalue = &var_ref->value;
|
||||
/* the reference is no longer to a local variable */
|
||||
var_ref->is_detached = TRUE;
|
||||
add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15699,14 +15732,15 @@ static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_
|
|||
int var_idx = idx;
|
||||
|
||||
list_for_each_safe(el, el1, &sf->var_ref_list) {
|
||||
var_ref = list_entry(el, JSVarRef, header.link);
|
||||
var_ref = list_entry(el, JSVarRef, var_ref_link);
|
||||
if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
|
||||
list_del(&var_ref->var_ref_link);
|
||||
if (var_ref->async_func)
|
||||
async_func_free(ctx->rt, var_ref->async_func);
|
||||
var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
|
||||
var_ref->pvalue = &var_ref->value;
|
||||
list_del(&var_ref->header.link);
|
||||
/* the reference is no longer to a local variable */
|
||||
var_ref->is_detached = TRUE;
|
||||
add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15897,9 +15931,10 @@ typedef enum {
|
|||
OP_SPECIAL_OBJECT_IMPORT_META,
|
||||
} OPSpecialObjectEnum;
|
||||
|
||||
#define FUNC_RET_AWAIT 0
|
||||
#define FUNC_RET_YIELD 1
|
||||
#define FUNC_RET_YIELD_STAR 2
|
||||
#define FUNC_RET_AWAIT 0
|
||||
#define FUNC_RET_YIELD 1
|
||||
#define FUNC_RET_YIELD_STAR 2
|
||||
#define FUNC_RET_INITIAL_YIELD 3
|
||||
|
||||
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
|
||||
static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
||||
|
@ -18303,9 +18338,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
|||
ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
|
||||
goto done_generator;
|
||||
CASE(OP_return_async):
|
||||
CASE(OP_initial_yield):
|
||||
ret_val = JS_UNDEFINED;
|
||||
goto done_generator;
|
||||
CASE(OP_initial_yield):
|
||||
ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD);
|
||||
goto done_generator;
|
||||
|
||||
CASE(OP_nop):
|
||||
BREAK;
|
||||
|
@ -18587,26 +18624,35 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
|
|||
}
|
||||
|
||||
/* JSAsyncFunctionState (used by generator and async functions) */
|
||||
static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
|
||||
JSValueConst func_obj, JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv)
|
||||
static JSAsyncFunctionState *async_func_init(JSContext *ctx,
|
||||
JSValueConst func_obj, JSValueConst this_obj,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSAsyncFunctionState *s;
|
||||
JSObject *p;
|
||||
JSFunctionBytecode *b;
|
||||
JSStackFrame *sf;
|
||||
int local_count, i, arg_buf_len, n;
|
||||
|
||||
s = js_mallocz(ctx, sizeof(*s));
|
||||
if (!s)
|
||||
return NULL;
|
||||
s->header.ref_count = 1;
|
||||
add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
|
||||
|
||||
sf = &s->frame;
|
||||
init_list_head(&sf->var_ref_list);
|
||||
p = JS_VALUE_GET_OBJ(func_obj);
|
||||
b = p->u.func.function_bytecode;
|
||||
sf->js_mode = b->js_mode;
|
||||
sf->js_mode = b->js_mode | JS_MODE_ASYNC;
|
||||
sf->cur_pc = b->byte_code_buf;
|
||||
arg_buf_len = max_int(b->arg_count, argc);
|
||||
local_count = arg_buf_len + b->var_count + b->stack_size;
|
||||
sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
|
||||
if (!sf->arg_buf)
|
||||
return -1;
|
||||
if (!sf->arg_buf) {
|
||||
js_free(ctx, s);
|
||||
return NULL;
|
||||
}
|
||||
sf->cur_func = JS_DupValue(ctx, func_obj);
|
||||
s->this_val = JS_DupValue(ctx, this_obj);
|
||||
s->argc = argc;
|
||||
|
@ -18618,38 +18664,17 @@ static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
|
|||
n = arg_buf_len + b->var_count;
|
||||
for(i = argc; i < n; i++)
|
||||
sf->arg_buf[i] = JS_UNDEFINED;
|
||||
return 0;
|
||||
s->resolving_funcs[0] = JS_UNDEFINED;
|
||||
s->resolving_funcs[1] = JS_UNDEFINED;
|
||||
s->is_completed = FALSE;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
|
||||
JS_MarkFunc *mark_func)
|
||||
static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
|
||||
{
|
||||
JSStackFrame *sf;
|
||||
JSStackFrame *sf = &s->frame;
|
||||
JSValue *sp;
|
||||
|
||||
sf = &s->frame;
|
||||
JS_MarkValue(rt, sf->cur_func, mark_func);
|
||||
JS_MarkValue(rt, s->this_val, mark_func);
|
||||
if (sf->cur_sp) {
|
||||
/* if the function is running, cur_sp is not known so we
|
||||
cannot mark the stack. Marking the variables is not needed
|
||||
because a running function cannot be part of a removable
|
||||
cycle */
|
||||
for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
|
||||
JS_MarkValue(rt, *sp, mark_func);
|
||||
}
|
||||
}
|
||||
|
||||
static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
||||
{
|
||||
JSStackFrame *sf;
|
||||
JSValue *sp;
|
||||
|
||||
sf = &s->frame;
|
||||
|
||||
/* close the closure variables. */
|
||||
close_var_refs(rt, sf);
|
||||
|
||||
if (sf->arg_buf) {
|
||||
/* cannot free the function if it is running */
|
||||
assert(sf->cur_sp != NULL);
|
||||
|
@ -18657,6 +18682,7 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
|||
JS_FreeValueRT(rt, *sp);
|
||||
}
|
||||
js_free_rt(rt, sf->arg_buf);
|
||||
sf->arg_buf = NULL;
|
||||
}
|
||||
JS_FreeValueRT(rt, sf->cur_func);
|
||||
JS_FreeValueRT(rt, s->this_val);
|
||||
|
@ -18664,17 +18690,66 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
|||
|
||||
static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
|
||||
{
|
||||
JSValue func_obj;
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSStackFrame *sf = &s->frame;
|
||||
JSValue func_obj, ret;
|
||||
|
||||
if (js_check_stack_overflow(ctx->rt, 0))
|
||||
return JS_ThrowStackOverflow(ctx);
|
||||
assert(!s->is_completed);
|
||||
if (js_check_stack_overflow(ctx->rt, 0)) {
|
||||
ret = JS_ThrowStackOverflow(ctx);
|
||||
} else {
|
||||
/* the tag does not matter provided it is not an object */
|
||||
func_obj = JS_MKPTR(JS_TAG_INT, s);
|
||||
ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
|
||||
s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
|
||||
}
|
||||
if (JS_IsException(ret) || JS_IsUndefined(ret)) {
|
||||
if (JS_IsUndefined(ret)) {
|
||||
ret = sf->cur_sp[-1];
|
||||
sf->cur_sp[-1] = JS_UNDEFINED;
|
||||
}
|
||||
/* end of execution */
|
||||
s->is_completed = TRUE;
|
||||
|
||||
/* the tag does not matter provided it is not an object */
|
||||
func_obj = JS_MKPTR(JS_TAG_INT, s);
|
||||
return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
|
||||
s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
|
||||
/* close the closure variables. */
|
||||
close_var_refs(rt, sf);
|
||||
|
||||
async_func_free_frame(rt, s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
||||
{
|
||||
/* cannot close the closure variables here because it would
|
||||
potentially modify the object graph */
|
||||
if (!s->is_completed) {
|
||||
async_func_free_frame(rt, s);
|
||||
}
|
||||
|
||||
JS_FreeValueRT(rt, s->resolving_funcs[0]);
|
||||
JS_FreeValueRT(rt, s->resolving_funcs[1]);
|
||||
|
||||
remove_gc_object(&s->header);
|
||||
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) {
|
||||
list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list);
|
||||
} else {
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
||||
{
|
||||
if (--s->header.ref_count == 0) {
|
||||
if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
|
||||
list_del(&s->header.link);
|
||||
list_add(&s->header.link, &rt->gc_zero_ref_count_list);
|
||||
if (rt->gc_phase == JS_GC_PHASE_NONE) {
|
||||
free_zero_refcount(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Generators */
|
||||
|
||||
|
@ -18688,14 +18763,17 @@ typedef enum JSGeneratorStateEnum {
|
|||
|
||||
typedef struct JSGeneratorData {
|
||||
JSGeneratorStateEnum state;
|
||||
JSAsyncFunctionState func_state;
|
||||
JSAsyncFunctionState *func_state;
|
||||
} JSGeneratorData;
|
||||
|
||||
static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
|
||||
{
|
||||
if (s->state == JS_GENERATOR_STATE_COMPLETED)
|
||||
return;
|
||||
async_func_free(rt, &s->func_state);
|
||||
if (s->func_state) {
|
||||
async_func_free(rt, s->func_state);
|
||||
s->func_state = NULL;
|
||||
}
|
||||
s->state = JS_GENERATOR_STATE_COMPLETED;
|
||||
}
|
||||
|
||||
|
@ -18720,9 +18798,9 @@ static void js_generator_mark(JSRuntime *rt, JSValueConst val,
|
|||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
JSGeneratorData *s = p->u.generator_data;
|
||||
|
||||
if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
|
||||
if (!s || !s->func_state)
|
||||
return;
|
||||
async_func_mark(rt, &s->func_state, mark_func);
|
||||
mark_func(rt, &s->func_state->header);
|
||||
}
|
||||
|
||||
/* XXX: use enum */
|
||||
|
@ -18741,7 +18819,7 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
|||
*pdone = TRUE;
|
||||
if (!s)
|
||||
return JS_ThrowTypeError(ctx, "not a generator");
|
||||
sf = &s->func_state.frame;
|
||||
sf = &s->func_state->frame;
|
||||
switch(s->state) {
|
||||
default:
|
||||
case JS_GENERATOR_STATE_SUSPENDED_START:
|
||||
|
@ -18759,23 +18837,23 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
|||
if (magic == GEN_MAGIC_THROW &&
|
||||
s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
|
||||
JS_Throw(ctx, ret);
|
||||
s->func_state.throw_flag = TRUE;
|
||||
s->func_state->throw_flag = TRUE;
|
||||
} else {
|
||||
sf->cur_sp[-1] = ret;
|
||||
sf->cur_sp[0] = JS_NewInt32(ctx, magic);
|
||||
sf->cur_sp++;
|
||||
exec_no_arg:
|
||||
s->func_state.throw_flag = FALSE;
|
||||
s->func_state->throw_flag = FALSE;
|
||||
}
|
||||
s->state = JS_GENERATOR_STATE_EXECUTING;
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
func_ret = async_func_resume(ctx, s->func_state);
|
||||
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
|
||||
if (JS_IsException(func_ret)) {
|
||||
/* finalize the execution in case of exception */
|
||||
if (s->func_state->is_completed) {
|
||||
/* finalize the execution in case of exception or normal return */
|
||||
free_generator_stack(ctx, s);
|
||||
return func_ret;
|
||||
}
|
||||
if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
||||
} else {
|
||||
assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
|
||||
/* get the returned yield value at the top of the stack */
|
||||
ret = sf->cur_sp[-1];
|
||||
sf->cur_sp[-1] = JS_UNDEFINED;
|
||||
|
@ -18786,12 +18864,6 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
|||
} else {
|
||||
*pdone = FALSE;
|
||||
}
|
||||
} else {
|
||||
/* end of iterator */
|
||||
ret = sf->cur_sp[-1];
|
||||
sf->cur_sp[-1] = JS_UNDEFINED;
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
free_generator_stack(ctx, s);
|
||||
}
|
||||
break;
|
||||
case JS_GENERATOR_STATE_COMPLETED:
|
||||
|
@ -18829,13 +18901,14 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
|||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
s->state = JS_GENERATOR_STATE_SUSPENDED_START;
|
||||
if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
|
||||
s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
|
||||
if (!s->func_state) {
|
||||
s->state = JS_GENERATOR_STATE_COMPLETED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* execute the function up to 'OP_initial_yield' */
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
func_ret = async_func_resume(ctx, s->func_state);
|
||||
if (JS_IsException(func_ret))
|
||||
goto fail;
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
|
@ -18853,36 +18926,12 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
|||
|
||||
/* AsyncFunction */
|
||||
|
||||
static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)
|
||||
{
|
||||
if (s->is_active) {
|
||||
async_func_free(rt, &s->func_state);
|
||||
s->is_active = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)
|
||||
{
|
||||
js_async_function_terminate(rt, s);
|
||||
JS_FreeValueRT(rt, s->resolving_funcs[0]);
|
||||
JS_FreeValueRT(rt, s->resolving_funcs[1]);
|
||||
remove_gc_object(&s->header);
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
|
||||
static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)
|
||||
{
|
||||
if (--s->header.ref_count == 0) {
|
||||
js_async_function_free0(rt, s);
|
||||
}
|
||||
}
|
||||
|
||||
static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
JSAsyncFunctionData *s = p->u.async_function_data;
|
||||
JSAsyncFunctionState *s = p->u.async_function_data;
|
||||
if (s) {
|
||||
js_async_function_free(rt, s);
|
||||
async_func_free(rt, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18890,14 +18939,14 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
|
|||
JS_MarkFunc *mark_func)
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
JSAsyncFunctionData *s = p->u.async_function_data;
|
||||
JSAsyncFunctionState *s = p->u.async_function_data;
|
||||
if (s) {
|
||||
mark_func(rt, &s->header);
|
||||
}
|
||||
}
|
||||
|
||||
static int js_async_function_resolve_create(JSContext *ctx,
|
||||
JSAsyncFunctionData *s,
|
||||
JSAsyncFunctionState *s,
|
||||
JSValue *resolving_funcs)
|
||||
{
|
||||
int i;
|
||||
|
@ -18919,60 +18968,58 @@ static int js_async_function_resolve_create(JSContext *ctx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
|
||||
static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s)
|
||||
{
|
||||
JSValue func_ret, ret2;
|
||||
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
if (JS_IsException(func_ret)) {
|
||||
JSValue error;
|
||||
fail:
|
||||
error = JS_GetException(ctx);
|
||||
ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
|
||||
1, (JSValueConst *)&error);
|
||||
JS_FreeValue(ctx, error);
|
||||
js_async_function_terminate(ctx->rt, s);
|
||||
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
|
||||
} else {
|
||||
JSValue value;
|
||||
value = s->func_state.frame.cur_sp[-1];
|
||||
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
if (JS_IsUndefined(func_ret)) {
|
||||
/* function returned */
|
||||
ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
|
||||
1, (JSValueConst *)&value);
|
||||
func_ret = async_func_resume(ctx, s);
|
||||
if (s->is_completed) {
|
||||
if (JS_IsException(func_ret)) {
|
||||
JSValue error;
|
||||
fail:
|
||||
error = JS_GetException(ctx);
|
||||
ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
|
||||
1, (JSValueConst *)&error);
|
||||
JS_FreeValue(ctx, error);
|
||||
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
|
||||
JS_FreeValue(ctx, value);
|
||||
js_async_function_terminate(ctx->rt, s);
|
||||
} else {
|
||||
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
|
||||
int i, res;
|
||||
|
||||
/* await */
|
||||
JS_FreeValue(ctx, func_ret); /* not used */
|
||||
promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
||||
1, (JSValueConst *)&value, 0);
|
||||
JS_FreeValue(ctx, value);
|
||||
if (JS_IsException(promise))
|
||||
goto fail;
|
||||
if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
|
||||
JS_FreeValue(ctx, promise);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Note: no need to create 'thrownawayCapability' as in
|
||||
the spec */
|
||||
for(i = 0; i < 2; i++)
|
||||
resolving_funcs1[i] = JS_UNDEFINED;
|
||||
res = perform_promise_then(ctx, promise,
|
||||
(JSValueConst *)resolving_funcs,
|
||||
(JSValueConst *)resolving_funcs1);
|
||||
JS_FreeValue(ctx, promise);
|
||||
for(i = 0; i < 2; i++)
|
||||
JS_FreeValue(ctx, resolving_funcs[i]);
|
||||
if (res)
|
||||
goto fail;
|
||||
/* normal return */
|
||||
ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
|
||||
1, (JSValueConst *)&func_ret);
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
|
||||
}
|
||||
} else {
|
||||
JSValue value, promise, resolving_funcs[2], resolving_funcs1[2];
|
||||
int i, res;
|
||||
|
||||
value = s->frame.cur_sp[-1];
|
||||
s->frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
|
||||
/* await */
|
||||
JS_FreeValue(ctx, func_ret); /* not used */
|
||||
promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
||||
1, (JSValueConst *)&value, 0);
|
||||
JS_FreeValue(ctx, value);
|
||||
if (JS_IsException(promise))
|
||||
goto fail;
|
||||
if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
|
||||
JS_FreeValue(ctx, promise);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Note: no need to create 'thrownawayCapability' as in
|
||||
the spec */
|
||||
for(i = 0; i < 2; i++)
|
||||
resolving_funcs1[i] = JS_UNDEFINED;
|
||||
res = perform_promise_then(ctx, promise,
|
||||
(JSValueConst *)resolving_funcs,
|
||||
(JSValueConst *)resolving_funcs1);
|
||||
JS_FreeValue(ctx, promise);
|
||||
for(i = 0; i < 2; i++)
|
||||
JS_FreeValue(ctx, resolving_funcs[i]);
|
||||
if (res)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18983,7 +19030,7 @@ static JSValue js_async_function_resolve_call(JSContext *ctx,
|
|||
int flags)
|
||||
{
|
||||
JSObject *p = JS_VALUE_GET_OBJ(func_obj);
|
||||
JSAsyncFunctionData *s = p->u.async_function_data;
|
||||
JSAsyncFunctionState *s = p->u.async_function_data;
|
||||
BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
|
||||
JSValueConst arg;
|
||||
|
||||
|
@ -18991,12 +19038,12 @@ static JSValue js_async_function_resolve_call(JSContext *ctx,
|
|||
arg = argv[0];
|
||||
else
|
||||
arg = JS_UNDEFINED;
|
||||
s->func_state.throw_flag = is_reject;
|
||||
s->throw_flag = is_reject;
|
||||
if (is_reject) {
|
||||
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
||||
} else {
|
||||
/* return value of await */
|
||||
s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
|
||||
s->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
|
||||
}
|
||||
js_async_function_resume(ctx, s);
|
||||
return JS_UNDEFINED;
|
||||
|
@ -19007,32 +19054,21 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
|
|||
int argc, JSValueConst *argv, int flags)
|
||||
{
|
||||
JSValue promise;
|
||||
JSAsyncFunctionData *s;
|
||||
JSAsyncFunctionState *s;
|
||||
|
||||
s = js_mallocz(ctx, sizeof(*s));
|
||||
s = async_func_init(ctx, func_obj, this_obj, argc, argv);
|
||||
if (!s)
|
||||
return JS_EXCEPTION;
|
||||
s->header.ref_count = 1;
|
||||
add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
|
||||
s->is_active = FALSE;
|
||||
s->resolving_funcs[0] = JS_UNDEFINED;
|
||||
s->resolving_funcs[1] = JS_UNDEFINED;
|
||||
|
||||
promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
|
||||
if (JS_IsException(promise))
|
||||
goto fail;
|
||||
|
||||
if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
|
||||
fail:
|
||||
JS_FreeValue(ctx, promise);
|
||||
js_async_function_free(ctx->rt, s);
|
||||
if (JS_IsException(promise)) {
|
||||
async_func_free(ctx->rt, s);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
s->is_active = TRUE;
|
||||
|
||||
js_async_function_resume(ctx, s);
|
||||
|
||||
js_async_function_free(ctx->rt, s);
|
||||
async_func_free(ctx->rt, s);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
@ -19061,7 +19097,8 @@ typedef struct JSAsyncGeneratorRequest {
|
|||
typedef struct JSAsyncGeneratorData {
|
||||
JSObject *generator; /* back pointer to the object (const) */
|
||||
JSAsyncGeneratorStateEnum state;
|
||||
JSAsyncFunctionState func_state;
|
||||
/* func_state is NULL is state AWAITING_RETURN and COMPLETED */
|
||||
JSAsyncFunctionState *func_state;
|
||||
struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
|
||||
} JSAsyncGeneratorData;
|
||||
|
||||
|
@ -19079,10 +19116,8 @@ static void js_async_generator_free(JSRuntime *rt,
|
|||
JS_FreeValueRT(rt, req->resolving_funcs[1]);
|
||||
js_free_rt(rt, req);
|
||||
}
|
||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
|
||||
s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
|
||||
async_func_free(rt, &s->func_state);
|
||||
}
|
||||
if (s->func_state)
|
||||
async_func_free(rt, s->func_state);
|
||||
js_free_rt(rt, s);
|
||||
}
|
||||
|
||||
|
@ -19109,9 +19144,8 @@ static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
|
|||
JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
|
||||
JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
|
||||
}
|
||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
|
||||
s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
|
||||
async_func_mark(rt, &s->func_state, mark_func);
|
||||
if (s->func_state) {
|
||||
mark_func(rt, &s->func_state->header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19221,7 +19255,8 @@ static void js_async_generator_complete(JSContext *ctx,
|
|||
{
|
||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
||||
async_func_free(ctx->rt, &s->func_state);
|
||||
async_func_free(ctx->rt, s->func_state);
|
||||
s->func_state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19302,30 +19337,38 @@ static void js_async_generator_resume_next(JSContext *ctx,
|
|||
if (next->completion_type == GEN_MAGIC_THROW &&
|
||||
s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
|
||||
JS_Throw(ctx, value);
|
||||
s->func_state.throw_flag = TRUE;
|
||||
s->func_state->throw_flag = TRUE;
|
||||
} else {
|
||||
/* 'yield' returns a value. 'yield *' also returns a value
|
||||
in case the 'throw' method is called */
|
||||
s->func_state.frame.cur_sp[-1] = value;
|
||||
s->func_state.frame.cur_sp[0] =
|
||||
s->func_state->frame.cur_sp[-1] = value;
|
||||
s->func_state->frame.cur_sp[0] =
|
||||
JS_NewInt32(ctx, next->completion_type);
|
||||
s->func_state.frame.cur_sp++;
|
||||
s->func_state->frame.cur_sp++;
|
||||
exec_no_arg:
|
||||
s->func_state.throw_flag = FALSE;
|
||||
s->func_state->throw_flag = FALSE;
|
||||
}
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
|
||||
resume_exec:
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
if (JS_IsException(func_ret)) {
|
||||
value = JS_GetException(ctx);
|
||||
js_async_generator_complete(ctx, s);
|
||||
js_async_generator_reject(ctx, s, value);
|
||||
JS_FreeValue(ctx, value);
|
||||
} else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
||||
func_ret = async_func_resume(ctx, s->func_state);
|
||||
if (s->func_state->is_completed) {
|
||||
if (JS_IsException(func_ret)) {
|
||||
value = JS_GetException(ctx);
|
||||
js_async_generator_complete(ctx, s);
|
||||
js_async_generator_reject(ctx, s, value);
|
||||
JS_FreeValue(ctx, value);
|
||||
} else {
|
||||
/* end of function */
|
||||
js_async_generator_complete(ctx, s);
|
||||
js_async_generator_resolve(ctx, s, func_ret, TRUE);
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
}
|
||||
} else {
|
||||
int func_ret_code, ret;
|
||||
value = s->func_state.frame.cur_sp[-1];
|
||||
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
|
||||
func_ret_code = JS_VALUE_GET_INT(func_ret);
|
||||
value = s->func_state->frame.cur_sp[-1];
|
||||
s->func_state->frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
switch(func_ret_code) {
|
||||
case FUNC_RET_YIELD:
|
||||
case FUNC_RET_YIELD_STAR:
|
||||
|
@ -19341,21 +19384,13 @@ static void js_async_generator_resume_next(JSContext *ctx,
|
|||
JS_FreeValue(ctx, value);
|
||||
if (ret < 0) {
|
||||
/* exception: throw it */
|
||||
s->func_state.throw_flag = TRUE;
|
||||
s->func_state->throw_flag = TRUE;
|
||||
goto resume_exec;
|
||||
}
|
||||
goto done;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
assert(JS_IsUndefined(func_ret));
|
||||
/* end of function */
|
||||
value = s->func_state.frame.cur_sp[-1];
|
||||
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
||||
js_async_generator_complete(ctx, s);
|
||||
js_async_generator_resolve(ctx, s, value, TRUE);
|
||||
JS_FreeValue(ctx, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -19389,12 +19424,12 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx,
|
|||
} else {
|
||||
/* restart function execution after await() */
|
||||
assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
|
||||
s->func_state.throw_flag = is_reject;
|
||||
s->func_state->throw_flag = is_reject;
|
||||
if (is_reject) {
|
||||
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
||||
} else {
|
||||
/* return value of await */
|
||||
s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
|
||||
s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
|
||||
}
|
||||
js_async_generator_resume_next(ctx, s);
|
||||
}
|
||||
|
@ -19458,14 +19493,12 @@ static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst fun
|
|||
return JS_EXCEPTION;
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
|
||||
init_list_head(&s->queue);
|
||||
if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
|
||||
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
||||
s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
|
||||
if (!s->func_state)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* execute the function up to 'OP_initial_yield' (no yield nor
|
||||
await are possible) */
|
||||
func_ret = async_func_resume(ctx, &s->func_state);
|
||||
func_ret = async_func_resume(ctx, s->func_state);
|
||||
if (JS_IsException(func_ret))
|
||||
goto fail;
|
||||
JS_FreeValue(ctx, func_ret);
|
||||
|
|
|
@ -270,6 +270,26 @@ function test_timer()
|
|||
os.clearTimeout(th[i]);
|
||||
}
|
||||
|
||||
/* test closure variable handling when freeing asynchronous
|
||||
function */
|
||||
function test_async_gc()
|
||||
{
|
||||
(async function run () {
|
||||
let obj = {}
|
||||
|
||||
let done = () => {
|
||||
obj
|
||||
std.gc();
|
||||
}
|
||||
|
||||
Promise.resolve().then(done)
|
||||
|
||||
const p = new Promise(() => {})
|
||||
|
||||
await p
|
||||
})();
|
||||
}
|
||||
|
||||
test_printf();
|
||||
test_file1();
|
||||
test_file2();
|
||||
|
@ -279,3 +299,5 @@ test_os();
|
|||
test_os_exec();
|
||||
test_timer();
|
||||
test_ext_json();
|
||||
test_async_gc();
|
||||
|
||||
|
|
Loading…
Reference in a new issue