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
527
quickjs.c
527
quickjs.c
|
@ -318,17 +318,18 @@ struct JSClass {
|
||||||
#define JS_MODE_STRICT (1 << 0)
|
#define JS_MODE_STRICT (1 << 0)
|
||||||
#define JS_MODE_STRIP (1 << 1)
|
#define JS_MODE_STRIP (1 << 1)
|
||||||
#define JS_MODE_MATH (1 << 2)
|
#define JS_MODE_MATH (1 << 2)
|
||||||
|
#define JS_MODE_ASYNC (1 << 3) /* async function */
|
||||||
|
|
||||||
typedef struct JSStackFrame {
|
typedef struct JSStackFrame {
|
||||||
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
|
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
|
||||||
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
|
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
|
||||||
JSValue *arg_buf; /* arguments */
|
JSValue *arg_buf; /* arguments */
|
||||||
JSValue *var_buf; /* variables */
|
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
|
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
|
||||||
instruction after the call */
|
instruction after the call */
|
||||||
int arg_count;
|
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
|
/* only used in generators. Current stack pointer value. NULL if
|
||||||
the function is running. */
|
the function is running. */
|
||||||
JSValue *cur_sp;
|
JSValue *cur_sp;
|
||||||
|
@ -361,11 +362,6 @@ typedef struct JSVarRef {
|
||||||
struct {
|
struct {
|
||||||
int __gc_ref_count; /* corresponds to header.ref_count */
|
int __gc_ref_count; /* corresponds to header.ref_count */
|
||||||
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
|
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_detached : 1;
|
||||||
uint8_t is_arg : 1;
|
uint8_t is_arg : 1;
|
||||||
uint16_t var_idx; /* index of the corresponding function variable on
|
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
|
JSValue *pvalue; /* pointer to the value, either on the stack or
|
||||||
to 'value' */
|
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;
|
} JSVarRef;
|
||||||
|
|
||||||
/* the same structure is used for big integers and big floats. Big
|
/* the same structure is used for big integers and big floats. Big
|
||||||
|
@ -671,21 +673,16 @@ typedef struct JSTypedArray {
|
||||||
} JSTypedArray;
|
} JSTypedArray;
|
||||||
|
|
||||||
typedef struct JSAsyncFunctionState {
|
typedef struct JSAsyncFunctionState {
|
||||||
JSValue this_val; /* 'this' generator argument */
|
JSGCObjectHeader header;
|
||||||
|
JSValue this_val; /* 'this' argument */
|
||||||
int argc; /* number of function arguments */
|
int argc; /* number of function arguments */
|
||||||
BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
|
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;
|
JSStackFrame frame;
|
||||||
} JSAsyncFunctionState;
|
} 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 {
|
typedef enum {
|
||||||
/* binary operators */
|
/* binary operators */
|
||||||
JS_OVOP_ADD,
|
JS_OVOP_ADD,
|
||||||
|
@ -925,7 +922,7 @@ struct JSObject {
|
||||||
struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
|
struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
|
||||||
struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
|
struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
|
||||||
struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
|
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 JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
|
||||||
struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
|
struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
|
||||||
struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
|
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 JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
|
||||||
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
|
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
|
||||||
BOOL is_arg);
|
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,
|
static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
||||||
JSValueConst this_obj,
|
JSValueConst this_obj,
|
||||||
int argc, JSValueConst *argv,
|
int argc, JSValueConst *argv,
|
||||||
|
@ -1239,8 +1238,6 @@ static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
|
||||||
static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
|
static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
|
||||||
JSObject *p, JSAtom prop);
|
JSObject *p, JSAtom prop);
|
||||||
static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
|
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_AddIntrinsicBasicObjects(JSContext *ctx);
|
||||||
static void js_free_shape(JSRuntime *rt, JSShape *sh);
|
static void js_free_shape(JSRuntime *rt, JSShape *sh);
|
||||||
static void js_free_shape_null(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,
|
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
|
||||||
JSGCObjectTypeEnum type);
|
JSGCObjectTypeEnum type);
|
||||||
static void remove_gc_object(JSGCObjectHeader *h);
|
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_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
|
||||||
static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
|
static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
|
||||||
void *opaque);
|
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->header.ref_count == 0) {
|
||||||
if (var_ref->is_detached) {
|
if (var_ref->is_detached) {
|
||||||
JS_FreeValueRT(rt, var_ref->value);
|
JS_FreeValueRT(rt, var_ref->value);
|
||||||
remove_gc_object(&var_ref->header);
|
|
||||||
} else {
|
} 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);
|
js_free_rt(rt, var_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5355,7 +5353,7 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
|
||||||
if (var_refs) {
|
if (var_refs) {
|
||||||
for(i = 0; i < b->closure_var_count; i++) {
|
for(i = 0; i < b->closure_var_count; i++) {
|
||||||
JSVarRef *var_ref = var_refs[i];
|
JSVarRef *var_ref = var_refs[i];
|
||||||
if (var_ref && var_ref->is_detached) {
|
if (var_ref) {
|
||||||
mark_func(rt, &var_ref->header);
|
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:
|
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||||
free_function_bytecode(rt, (JSFunctionBytecode *)gp);
|
free_function_bytecode(rt, (JSFunctionBytecode *)gp);
|
||||||
break;
|
break;
|
||||||
|
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||||
|
__async_func_free(rt, (JSAsyncFunctionState *)gp);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
@ -5623,11 +5624,9 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
|
||||||
if (pr->u.getset.setter)
|
if (pr->u.getset.setter)
|
||||||
mark_func(rt, &pr->u.getset.setter->header);
|
mark_func(rt, &pr->u.getset.setter->header);
|
||||||
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
|
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
|
||||||
if (pr->u.var_ref->is_detached) {
|
/* Note: the tag does not matter
|
||||||
/* Note: the tag does not matter
|
provided it is a GC object */
|
||||||
provided it is a GC object */
|
mark_func(rt, &pr->u.var_ref->header);
|
||||||
mark_func(rt, &pr->u.var_ref->header);
|
|
||||||
}
|
|
||||||
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
|
} else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
|
||||||
js_autoinit_mark(rt, pr, mark_func);
|
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:
|
case JS_GC_OBJ_TYPE_VAR_REF:
|
||||||
{
|
{
|
||||||
JSVarRef *var_ref = (JSVarRef *)gp;
|
JSVarRef *var_ref = (JSVarRef *)gp;
|
||||||
/* only detached variable referenced are taken into account */
|
if (var_ref->is_detached) {
|
||||||
assert(var_ref->is_detached);
|
JS_MarkValue(rt, *var_ref->pvalue, mark_func);
|
||||||
JS_MarkValue(rt, *var_ref->pvalue, mark_func);
|
} else if (var_ref->async_func) {
|
||||||
|
mark_func(rt, &var_ref->async_func->header);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||||
{
|
{
|
||||||
JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
|
JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp;
|
||||||
if (s->is_active)
|
JSStackFrame *sf = &s->frame;
|
||||||
async_func_mark(rt, &s->func_state, mark_func);
|
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[0], mark_func);
|
||||||
JS_MarkValue(rt, s->resolving_funcs[1], 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)
|
if (el == &rt->tmp_obj_list)
|
||||||
break;
|
break;
|
||||||
p = list_entry(el, JSGCObjectHeader, link);
|
p = list_entry(el, JSGCObjectHeader, link);
|
||||||
/* Only need to free the GC object associated with JS
|
/* Only need to free the GC object associated with JS values
|
||||||
values. The rest will be automatically removed because they
|
or async functions. The rest will be automatically removed
|
||||||
must be referenced by them. */
|
because they must be referenced by them. */
|
||||||
switch(p->gc_obj_type) {
|
switch(p->gc_obj_type) {
|
||||||
case JS_GC_OBJ_TYPE_JS_OBJECT:
|
case JS_GC_OBJ_TYPE_JS_OBJECT:
|
||||||
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
|
||||||
|
case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
|
||||||
#ifdef DUMP_GC_FREE
|
#ifdef DUMP_GC_FREE
|
||||||
if (!header_done) {
|
if (!header_done) {
|
||||||
printf("Freeing cycles:\n");
|
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) {
|
list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
|
||||||
p = list_entry(el, JSGCObjectHeader, link);
|
p = list_entry(el, JSGCObjectHeader, link);
|
||||||
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
|
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);
|
js_free_rt(rt, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15429,7 +15446,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
|
||||||
struct list_head *el;
|
struct list_head *el;
|
||||||
|
|
||||||
list_for_each(el, &sf->var_ref_list) {
|
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) {
|
if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
|
||||||
var_ref->header.ref_count++;
|
var_ref->header.ref_count++;
|
||||||
return var_ref;
|
return var_ref;
|
||||||
|
@ -15440,15 +15457,29 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
|
||||||
if (!var_ref)
|
if (!var_ref)
|
||||||
return NULL;
|
return NULL;
|
||||||
var_ref->header.ref_count = 1;
|
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_detached = FALSE;
|
||||||
var_ref->is_arg = is_arg;
|
var_ref->is_arg = is_arg;
|
||||||
var_ref->var_idx = var_idx;
|
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)
|
if (is_arg)
|
||||||
var_ref->pvalue = &sf->arg_buf[var_idx];
|
var_ref->pvalue = &sf->arg_buf[var_idx];
|
||||||
else
|
else
|
||||||
var_ref->pvalue = &sf->var_buf[var_idx];
|
var_ref->pvalue = &sf->var_buf[var_idx];
|
||||||
var_ref->value = JS_UNDEFINED;
|
|
||||||
return var_ref;
|
return var_ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15679,7 +15710,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
|
||||||
int var_idx;
|
int var_idx;
|
||||||
|
|
||||||
list_for_each_safe(el, el1, &sf->var_ref_list) {
|
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;
|
var_idx = var_ref->var_idx;
|
||||||
if (var_ref->is_arg)
|
if (var_ref->is_arg)
|
||||||
var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
|
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;
|
var_ref->pvalue = &var_ref->value;
|
||||||
/* the reference is no longer to a local variable */
|
/* the reference is no longer to a local variable */
|
||||||
var_ref->is_detached = TRUE;
|
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;
|
int var_idx = idx;
|
||||||
|
|
||||||
list_for_each_safe(el, el1, &sf->var_ref_list) {
|
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) {
|
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->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
|
||||||
var_ref->pvalue = &var_ref->value;
|
var_ref->pvalue = &var_ref->value;
|
||||||
list_del(&var_ref->header.link);
|
|
||||||
/* the reference is no longer to a local variable */
|
/* the reference is no longer to a local variable */
|
||||||
var_ref->is_detached = TRUE;
|
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,
|
OP_SPECIAL_OBJECT_IMPORT_META,
|
||||||
} OPSpecialObjectEnum;
|
} OPSpecialObjectEnum;
|
||||||
|
|
||||||
#define FUNC_RET_AWAIT 0
|
#define FUNC_RET_AWAIT 0
|
||||||
#define FUNC_RET_YIELD 1
|
#define FUNC_RET_YIELD 1
|
||||||
#define FUNC_RET_YIELD_STAR 2
|
#define FUNC_RET_YIELD_STAR 2
|
||||||
|
#define FUNC_RET_INITIAL_YIELD 3
|
||||||
|
|
||||||
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
|
/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
|
||||||
static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
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);
|
ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
|
||||||
goto done_generator;
|
goto done_generator;
|
||||||
CASE(OP_return_async):
|
CASE(OP_return_async):
|
||||||
CASE(OP_initial_yield):
|
|
||||||
ret_val = JS_UNDEFINED;
|
ret_val = JS_UNDEFINED;
|
||||||
goto done_generator;
|
goto done_generator;
|
||||||
|
CASE(OP_initial_yield):
|
||||||
|
ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD);
|
||||||
|
goto done_generator;
|
||||||
|
|
||||||
CASE(OP_nop):
|
CASE(OP_nop):
|
||||||
BREAK;
|
BREAK;
|
||||||
|
@ -18587,26 +18624,35 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* JSAsyncFunctionState (used by generator and async functions) */
|
/* JSAsyncFunctionState (used by generator and async functions) */
|
||||||
static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
|
static JSAsyncFunctionState *async_func_init(JSContext *ctx,
|
||||||
JSValueConst func_obj, JSValueConst this_obj,
|
JSValueConst func_obj, JSValueConst this_obj,
|
||||||
int argc, JSValueConst *argv)
|
int argc, JSValueConst *argv)
|
||||||
{
|
{
|
||||||
|
JSAsyncFunctionState *s;
|
||||||
JSObject *p;
|
JSObject *p;
|
||||||
JSFunctionBytecode *b;
|
JSFunctionBytecode *b;
|
||||||
JSStackFrame *sf;
|
JSStackFrame *sf;
|
||||||
int local_count, i, arg_buf_len, n;
|
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;
|
sf = &s->frame;
|
||||||
init_list_head(&sf->var_ref_list);
|
init_list_head(&sf->var_ref_list);
|
||||||
p = JS_VALUE_GET_OBJ(func_obj);
|
p = JS_VALUE_GET_OBJ(func_obj);
|
||||||
b = p->u.func.function_bytecode;
|
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;
|
sf->cur_pc = b->byte_code_buf;
|
||||||
arg_buf_len = max_int(b->arg_count, argc);
|
arg_buf_len = max_int(b->arg_count, argc);
|
||||||
local_count = arg_buf_len + b->var_count + b->stack_size;
|
local_count = arg_buf_len + b->var_count + b->stack_size;
|
||||||
sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
|
sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
|
||||||
if (!sf->arg_buf)
|
if (!sf->arg_buf) {
|
||||||
return -1;
|
js_free(ctx, s);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
sf->cur_func = JS_DupValue(ctx, func_obj);
|
sf->cur_func = JS_DupValue(ctx, func_obj);
|
||||||
s->this_val = JS_DupValue(ctx, this_obj);
|
s->this_val = JS_DupValue(ctx, this_obj);
|
||||||
s->argc = argc;
|
s->argc = argc;
|
||||||
|
@ -18618,38 +18664,17 @@ static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
|
||||||
n = arg_buf_len + b->var_count;
|
n = arg_buf_len + b->var_count;
|
||||||
for(i = argc; i < n; i++)
|
for(i = argc; i < n; i++)
|
||||||
sf->arg_buf[i] = JS_UNDEFINED;
|
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,
|
static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
|
||||||
JS_MarkFunc *mark_func)
|
|
||||||
{
|
{
|
||||||
JSStackFrame *sf;
|
JSStackFrame *sf = &s->frame;
|
||||||
JSValue *sp;
|
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) {
|
if (sf->arg_buf) {
|
||||||
/* cannot free the function if it is running */
|
/* cannot free the function if it is running */
|
||||||
assert(sf->cur_sp != NULL);
|
assert(sf->cur_sp != NULL);
|
||||||
|
@ -18657,6 +18682,7 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
|
||||||
JS_FreeValueRT(rt, *sp);
|
JS_FreeValueRT(rt, *sp);
|
||||||
}
|
}
|
||||||
js_free_rt(rt, sf->arg_buf);
|
js_free_rt(rt, sf->arg_buf);
|
||||||
|
sf->arg_buf = NULL;
|
||||||
}
|
}
|
||||||
JS_FreeValueRT(rt, sf->cur_func);
|
JS_FreeValueRT(rt, sf->cur_func);
|
||||||
JS_FreeValueRT(rt, s->this_val);
|
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)
|
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))
|
assert(!s->is_completed);
|
||||||
return JS_ThrowStackOverflow(ctx);
|
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 */
|
/* close the closure variables. */
|
||||||
func_obj = JS_MKPTR(JS_TAG_INT, s);
|
close_var_refs(rt, sf);
|
||||||
return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
|
|
||||||
s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
|
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 */
|
/* Generators */
|
||||||
|
|
||||||
|
@ -18688,14 +18763,17 @@ typedef enum JSGeneratorStateEnum {
|
||||||
|
|
||||||
typedef struct JSGeneratorData {
|
typedef struct JSGeneratorData {
|
||||||
JSGeneratorStateEnum state;
|
JSGeneratorStateEnum state;
|
||||||
JSAsyncFunctionState func_state;
|
JSAsyncFunctionState *func_state;
|
||||||
} JSGeneratorData;
|
} JSGeneratorData;
|
||||||
|
|
||||||
static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
|
static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
|
||||||
{
|
{
|
||||||
if (s->state == JS_GENERATOR_STATE_COMPLETED)
|
if (s->state == JS_GENERATOR_STATE_COMPLETED)
|
||||||
return;
|
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;
|
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);
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||||
JSGeneratorData *s = p->u.generator_data;
|
JSGeneratorData *s = p->u.generator_data;
|
||||||
|
|
||||||
if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
|
if (!s || !s->func_state)
|
||||||
return;
|
return;
|
||||||
async_func_mark(rt, &s->func_state, mark_func);
|
mark_func(rt, &s->func_state->header);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: use enum */
|
/* XXX: use enum */
|
||||||
|
@ -18741,7 +18819,7 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
||||||
*pdone = TRUE;
|
*pdone = TRUE;
|
||||||
if (!s)
|
if (!s)
|
||||||
return JS_ThrowTypeError(ctx, "not a generator");
|
return JS_ThrowTypeError(ctx, "not a generator");
|
||||||
sf = &s->func_state.frame;
|
sf = &s->func_state->frame;
|
||||||
switch(s->state) {
|
switch(s->state) {
|
||||||
default:
|
default:
|
||||||
case JS_GENERATOR_STATE_SUSPENDED_START:
|
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 &&
|
if (magic == GEN_MAGIC_THROW &&
|
||||||
s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
|
s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
|
||||||
JS_Throw(ctx, ret);
|
JS_Throw(ctx, ret);
|
||||||
s->func_state.throw_flag = TRUE;
|
s->func_state->throw_flag = TRUE;
|
||||||
} else {
|
} else {
|
||||||
sf->cur_sp[-1] = ret;
|
sf->cur_sp[-1] = ret;
|
||||||
sf->cur_sp[0] = JS_NewInt32(ctx, magic);
|
sf->cur_sp[0] = JS_NewInt32(ctx, magic);
|
||||||
sf->cur_sp++;
|
sf->cur_sp++;
|
||||||
exec_no_arg:
|
exec_no_arg:
|
||||||
s->func_state.throw_flag = FALSE;
|
s->func_state->throw_flag = FALSE;
|
||||||
}
|
}
|
||||||
s->state = JS_GENERATOR_STATE_EXECUTING;
|
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;
|
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
|
||||||
if (JS_IsException(func_ret)) {
|
if (s->func_state->is_completed) {
|
||||||
/* finalize the execution in case of exception */
|
/* finalize the execution in case of exception or normal return */
|
||||||
free_generator_stack(ctx, s);
|
free_generator_stack(ctx, s);
|
||||||
return func_ret;
|
return func_ret;
|
||||||
}
|
} else {
|
||||||
if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
|
||||||
/* get the returned yield value at the top of the stack */
|
/* get the returned yield value at the top of the stack */
|
||||||
ret = sf->cur_sp[-1];
|
ret = sf->cur_sp[-1];
|
||||||
sf->cur_sp[-1] = JS_UNDEFINED;
|
sf->cur_sp[-1] = JS_UNDEFINED;
|
||||||
|
@ -18786,12 +18864,6 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
||||||
} else {
|
} else {
|
||||||
*pdone = FALSE;
|
*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;
|
break;
|
||||||
case JS_GENERATOR_STATE_COMPLETED:
|
case JS_GENERATOR_STATE_COMPLETED:
|
||||||
|
@ -18829,13 +18901,14 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
||||||
if (!s)
|
if (!s)
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
s->state = JS_GENERATOR_STATE_SUSPENDED_START;
|
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;
|
s->state = JS_GENERATOR_STATE_COMPLETED;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* execute the function up to 'OP_initial_yield' */
|
/* 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))
|
if (JS_IsException(func_ret))
|
||||||
goto fail;
|
goto fail;
|
||||||
JS_FreeValue(ctx, func_ret);
|
JS_FreeValue(ctx, func_ret);
|
||||||
|
@ -18853,36 +18926,12 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
||||||
|
|
||||||
/* AsyncFunction */
|
/* 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)
|
static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
|
||||||
{
|
{
|
||||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||||
JSAsyncFunctionData *s = p->u.async_function_data;
|
JSAsyncFunctionState *s = p->u.async_function_data;
|
||||||
if (s) {
|
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)
|
JS_MarkFunc *mark_func)
|
||||||
{
|
{
|
||||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||||
JSAsyncFunctionData *s = p->u.async_function_data;
|
JSAsyncFunctionState *s = p->u.async_function_data;
|
||||||
if (s) {
|
if (s) {
|
||||||
mark_func(rt, &s->header);
|
mark_func(rt, &s->header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int js_async_function_resolve_create(JSContext *ctx,
|
static int js_async_function_resolve_create(JSContext *ctx,
|
||||||
JSAsyncFunctionData *s,
|
JSAsyncFunctionState *s,
|
||||||
JSValue *resolving_funcs)
|
JSValue *resolving_funcs)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -18919,60 +18968,58 @@ static int js_async_function_resolve_create(JSContext *ctx,
|
||||||
return 0;
|
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;
|
JSValue func_ret, ret2;
|
||||||
|
|
||||||
func_ret = async_func_resume(ctx, &s->func_state);
|
func_ret = async_func_resume(ctx, s);
|
||||||
if (JS_IsException(func_ret)) {
|
if (s->is_completed) {
|
||||||
JSValue error;
|
if (JS_IsException(func_ret)) {
|
||||||
fail:
|
JSValue error;
|
||||||
error = JS_GetException(ctx);
|
fail:
|
||||||
ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
|
error = JS_GetException(ctx);
|
||||||
1, (JSValueConst *)&error);
|
ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
|
||||||
JS_FreeValue(ctx, error);
|
1, (JSValueConst *)&error);
|
||||||
js_async_function_terminate(ctx->rt, s);
|
JS_FreeValue(ctx, error);
|
||||||
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);
|
|
||||||
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
|
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
|
||||||
JS_FreeValue(ctx, value);
|
|
||||||
js_async_function_terminate(ctx->rt, s);
|
|
||||||
} else {
|
} else {
|
||||||
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
|
/* normal return */
|
||||||
int i, res;
|
ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
|
||||||
|
1, (JSValueConst *)&func_ret);
|
||||||
/* await */
|
JS_FreeValue(ctx, func_ret);
|
||||||
JS_FreeValue(ctx, func_ret); /* not used */
|
JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
} 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)
|
int flags)
|
||||||
{
|
{
|
||||||
JSObject *p = JS_VALUE_GET_OBJ(func_obj);
|
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;
|
BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
|
||||||
JSValueConst arg;
|
JSValueConst arg;
|
||||||
|
|
||||||
|
@ -18991,12 +19038,12 @@ static JSValue js_async_function_resolve_call(JSContext *ctx,
|
||||||
arg = argv[0];
|
arg = argv[0];
|
||||||
else
|
else
|
||||||
arg = JS_UNDEFINED;
|
arg = JS_UNDEFINED;
|
||||||
s->func_state.throw_flag = is_reject;
|
s->throw_flag = is_reject;
|
||||||
if (is_reject) {
|
if (is_reject) {
|
||||||
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
||||||
} else {
|
} else {
|
||||||
/* return value of await */
|
/* 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);
|
js_async_function_resume(ctx, s);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
|
@ -19007,32 +19054,21 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
|
||||||
int argc, JSValueConst *argv, int flags)
|
int argc, JSValueConst *argv, int flags)
|
||||||
{
|
{
|
||||||
JSValue promise;
|
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)
|
if (!s)
|
||||||
return JS_EXCEPTION;
|
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);
|
promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
|
||||||
if (JS_IsException(promise))
|
if (JS_IsException(promise)) {
|
||||||
goto fail;
|
async_func_free(ctx->rt, s);
|
||||||
|
|
||||||
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);
|
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
s->is_active = TRUE;
|
|
||||||
|
|
||||||
js_async_function_resume(ctx, s);
|
js_async_function_resume(ctx, s);
|
||||||
|
|
||||||
js_async_function_free(ctx->rt, s);
|
async_func_free(ctx->rt, s);
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -19061,7 +19097,8 @@ typedef struct JSAsyncGeneratorRequest {
|
||||||
typedef struct JSAsyncGeneratorData {
|
typedef struct JSAsyncGeneratorData {
|
||||||
JSObject *generator; /* back pointer to the object (const) */
|
JSObject *generator; /* back pointer to the object (const) */
|
||||||
JSAsyncGeneratorStateEnum state;
|
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 */
|
struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
|
||||||
} JSAsyncGeneratorData;
|
} JSAsyncGeneratorData;
|
||||||
|
|
||||||
|
@ -19079,10 +19116,8 @@ static void js_async_generator_free(JSRuntime *rt,
|
||||||
JS_FreeValueRT(rt, req->resolving_funcs[1]);
|
JS_FreeValueRT(rt, req->resolving_funcs[1]);
|
||||||
js_free_rt(rt, req);
|
js_free_rt(rt, req);
|
||||||
}
|
}
|
||||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
|
if (s->func_state)
|
||||||
s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
|
async_func_free(rt, s->func_state);
|
||||||
async_func_free(rt, &s->func_state);
|
|
||||||
}
|
|
||||||
js_free_rt(rt, s);
|
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[0], mark_func);
|
||||||
JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
|
JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
|
||||||
}
|
}
|
||||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
|
if (s->func_state) {
|
||||||
s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
|
mark_func(rt, &s->func_state->header);
|
||||||
async_func_mark(rt, &s->func_state, mark_func);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19221,7 +19255,8 @@ static void js_async_generator_complete(JSContext *ctx,
|
||||||
{
|
{
|
||||||
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
|
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
|
||||||
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 &&
|
if (next->completion_type == GEN_MAGIC_THROW &&
|
||||||
s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
|
s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
|
||||||
JS_Throw(ctx, value);
|
JS_Throw(ctx, value);
|
||||||
s->func_state.throw_flag = TRUE;
|
s->func_state->throw_flag = TRUE;
|
||||||
} else {
|
} else {
|
||||||
/* 'yield' returns a value. 'yield *' also returns a value
|
/* 'yield' returns a value. 'yield *' also returns a value
|
||||||
in case the 'throw' method is called */
|
in case the 'throw' method is called */
|
||||||
s->func_state.frame.cur_sp[-1] = value;
|
s->func_state->frame.cur_sp[-1] = value;
|
||||||
s->func_state.frame.cur_sp[0] =
|
s->func_state->frame.cur_sp[0] =
|
||||||
JS_NewInt32(ctx, next->completion_type);
|
JS_NewInt32(ctx, next->completion_type);
|
||||||
s->func_state.frame.cur_sp++;
|
s->func_state->frame.cur_sp++;
|
||||||
exec_no_arg:
|
exec_no_arg:
|
||||||
s->func_state.throw_flag = FALSE;
|
s->func_state->throw_flag = FALSE;
|
||||||
}
|
}
|
||||||
s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
|
s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
|
||||||
resume_exec:
|
resume_exec:
|
||||||
func_ret = async_func_resume(ctx, &s->func_state);
|
func_ret = async_func_resume(ctx, s->func_state);
|
||||||
if (JS_IsException(func_ret)) {
|
if (s->func_state->is_completed) {
|
||||||
value = JS_GetException(ctx);
|
if (JS_IsException(func_ret)) {
|
||||||
js_async_generator_complete(ctx, s);
|
value = JS_GetException(ctx);
|
||||||
js_async_generator_reject(ctx, s, value);
|
js_async_generator_complete(ctx, s);
|
||||||
JS_FreeValue(ctx, value);
|
js_async_generator_reject(ctx, s, value);
|
||||||
} else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
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;
|
int func_ret_code, ret;
|
||||||
value = s->func_state.frame.cur_sp[-1];
|
assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
|
||||||
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
|
||||||
func_ret_code = JS_VALUE_GET_INT(func_ret);
|
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) {
|
switch(func_ret_code) {
|
||||||
case FUNC_RET_YIELD:
|
case FUNC_RET_YIELD:
|
||||||
case FUNC_RET_YIELD_STAR:
|
case FUNC_RET_YIELD_STAR:
|
||||||
|
@ -19341,21 +19384,13 @@ static void js_async_generator_resume_next(JSContext *ctx,
|
||||||
JS_FreeValue(ctx, value);
|
JS_FreeValue(ctx, value);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* exception: throw it */
|
/* exception: throw it */
|
||||||
s->func_state.throw_flag = TRUE;
|
s->func_state->throw_flag = TRUE;
|
||||||
goto resume_exec;
|
goto resume_exec;
|
||||||
}
|
}
|
||||||
goto done;
|
goto done;
|
||||||
default:
|
default:
|
||||||
abort();
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -19389,12 +19424,12 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx,
|
||||||
} else {
|
} else {
|
||||||
/* restart function execution after await() */
|
/* restart function execution after await() */
|
||||||
assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
|
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) {
|
if (is_reject) {
|
||||||
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
JS_Throw(ctx, JS_DupValue(ctx, arg));
|
||||||
} else {
|
} else {
|
||||||
/* return value of await */
|
/* 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);
|
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;
|
return JS_EXCEPTION;
|
||||||
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
|
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
|
||||||
init_list_head(&s->queue);
|
init_list_head(&s->queue);
|
||||||
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);
|
||||||
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
if (!s->func_state)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
/* execute the function up to 'OP_initial_yield' (no yield nor
|
/* execute the function up to 'OP_initial_yield' (no yield nor
|
||||||
await are possible) */
|
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))
|
if (JS_IsException(func_ret))
|
||||||
goto fail;
|
goto fail;
|
||||||
JS_FreeValue(ctx, func_ret);
|
JS_FreeValue(ctx, func_ret);
|
||||||
|
|
|
@ -270,6 +270,26 @@ function test_timer()
|
||||||
os.clearTimeout(th[i]);
|
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_printf();
|
||||||
test_file1();
|
test_file1();
|
||||||
test_file2();
|
test_file2();
|
||||||
|
@ -279,3 +299,5 @@ test_os();
|
||||||
test_os_exec();
|
test_os_exec();
|
||||||
test_timer();
|
test_timer();
|
||||||
test_ext_json();
|
test_ext_json();
|
||||||
|
test_async_gc();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue