mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
1569 lines
58 KiB
C
1569 lines
58 KiB
C
/*
|
|
* QuickJS Javascript Engine
|
|
*
|
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
* Copyright (c) 2017-2021 Charlie Gordon
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "libc/assert.h"
|
|
#include "third_party/quickjs/internal.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
QuickJS (MIT License)\\n\
|
|
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
|
Copyright (c) 2017-2021 Charlie Gordon\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
/* clang-format off */
|
|
|
|
typedef enum JSPromiseStateEnum {
|
|
JS_PROMISE_PENDING,
|
|
JS_PROMISE_FULFILLED,
|
|
JS_PROMISE_REJECTED,
|
|
} JSPromiseStateEnum;
|
|
|
|
typedef struct JSPromiseData {
|
|
JSPromiseStateEnum promise_state;
|
|
/* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
|
|
struct list_head promise_reactions[2];
|
|
BOOL is_handled; /* Note: only useful to debug */
|
|
JSValue promise_result;
|
|
} JSPromiseData;
|
|
|
|
typedef struct JSPromiseFunctionDataResolved {
|
|
int ref_count;
|
|
BOOL already_resolved;
|
|
} JSPromiseFunctionDataResolved;
|
|
|
|
typedef struct JSPromiseFunctionData {
|
|
JSValue promise;
|
|
JSPromiseFunctionDataResolved *presolved;
|
|
} JSPromiseFunctionData;
|
|
|
|
typedef struct JSPromiseReactionData {
|
|
struct list_head link; /* not used in promise_reaction_job */
|
|
JSValue resolving_funcs[2];
|
|
JSValue handler;
|
|
} JSPromiseReactionData;
|
|
|
|
static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
|
|
JSValueConst promise);
|
|
|
|
static void promise_reaction_data_free(JSRuntime *rt,
|
|
JSPromiseReactionData *rd)
|
|
{
|
|
JS_FreeValueRT(rt, rd->resolving_funcs[0]);
|
|
JS_FreeValueRT(rt, rd->resolving_funcs[1]);
|
|
JS_FreeValueRT(rt, rd->handler);
|
|
js_free_rt(rt, rd);
|
|
}
|
|
|
|
static JSValue promise_reaction_job(JSContext *ctx, int argc,
|
|
JSValueConst *argv)
|
|
{
|
|
JSValueConst handler, arg, func;
|
|
JSValue res, res2;
|
|
BOOL is_reject;
|
|
|
|
assert(argc == 5);
|
|
handler = argv[2];
|
|
is_reject = JS_ToBool(ctx, argv[3]);
|
|
arg = argv[4];
|
|
#ifdef DUMP_PROMISE
|
|
printf("promise_reaction_job: is_reject=%d\n", is_reject);
|
|
#endif
|
|
|
|
if (JS_IsUndefined(handler)) {
|
|
if (is_reject) {
|
|
res = JS_Throw(ctx, JS_DupValue(ctx, arg));
|
|
} else {
|
|
res = JS_DupValue(ctx, arg);
|
|
}
|
|
} else {
|
|
res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
|
|
}
|
|
is_reject = JS_IsException(res);
|
|
if (is_reject)
|
|
res = JS_GetException(ctx);
|
|
func = argv[is_reject];
|
|
/* as an extension, we support undefined as value to avoid
|
|
creating a dummy promise in the 'await' implementation of async
|
|
functions */
|
|
if (!JS_IsUndefined(func)) {
|
|
res2 = JS_Call(ctx, func, JS_UNDEFINED,
|
|
1, (JSValueConst *)&res);
|
|
} else {
|
|
res2 = JS_UNDEFINED;
|
|
}
|
|
JS_FreeValue(ctx, res);
|
|
|
|
return res2;
|
|
}
|
|
|
|
void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
|
|
JSHostPromiseRejectionTracker *cb,
|
|
void *opaque)
|
|
{
|
|
rt->host_promise_rejection_tracker = cb;
|
|
rt->host_promise_rejection_tracker_opaque = opaque;
|
|
}
|
|
|
|
static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
|
|
JSValueConst value, BOOL is_reject)
|
|
{
|
|
JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
|
|
struct list_head *el, *el1;
|
|
JSPromiseReactionData *rd;
|
|
JSValueConst args[5];
|
|
|
|
if (!s || s->promise_state != JS_PROMISE_PENDING)
|
|
return; /* should never happen */
|
|
set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
|
|
s->promise_state = JS_PROMISE_FULFILLED + is_reject;
|
|
#ifdef DUMP_PROMISE
|
|
printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
|
|
#endif
|
|
if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
|
|
JSRuntime *rt = ctx->rt;
|
|
if (rt->host_promise_rejection_tracker) {
|
|
rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
|
|
rt->host_promise_rejection_tracker_opaque);
|
|
}
|
|
}
|
|
|
|
list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
|
|
rd = list_entry(el, JSPromiseReactionData, link);
|
|
args[0] = rd->resolving_funcs[0];
|
|
args[1] = rd->resolving_funcs[1];
|
|
args[2] = rd->handler;
|
|
args[3] = JS_NewBool(ctx, is_reject);
|
|
args[4] = value;
|
|
JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
|
|
list_del(&rd->link);
|
|
promise_reaction_data_free(ctx->rt, rd);
|
|
}
|
|
|
|
list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
|
|
rd = list_entry(el, JSPromiseReactionData, link);
|
|
list_del(&rd->link);
|
|
promise_reaction_data_free(ctx->rt, rd);
|
|
}
|
|
}
|
|
|
|
static void reject_promise(JSContext *ctx, JSValueConst promise,
|
|
JSValueConst value)
|
|
{
|
|
fulfill_or_reject_promise(ctx, promise, value, TRUE);
|
|
}
|
|
|
|
static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValueConst promise, thenable, then;
|
|
JSValue args[2], res;
|
|
|
|
#ifdef DUMP_PROMISE
|
|
printf("js_promise_resolve_thenable_job\n");
|
|
#endif
|
|
assert(argc == 3);
|
|
promise = argv[0];
|
|
thenable = argv[1];
|
|
then = argv[2];
|
|
if (js_create_resolving_functions(ctx, args, promise) < 0)
|
|
return JS_EXCEPTION;
|
|
res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
|
|
if (JS_IsException(res)) {
|
|
JSValue error = JS_GetException(ctx);
|
|
res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
|
|
JS_FreeValue(ctx, error);
|
|
}
|
|
JS_FreeValue(ctx, args[0]);
|
|
JS_FreeValue(ctx, args[1]);
|
|
return res;
|
|
}
|
|
|
|
static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
|
|
JSPromiseFunctionDataResolved *sr)
|
|
{
|
|
if (--sr->ref_count == 0) {
|
|
js_free_rt(rt, sr);
|
|
}
|
|
}
|
|
|
|
static int js_create_resolving_functions(JSContext *ctx,
|
|
JSValue *resolving_funcs,
|
|
JSValueConst promise)
|
|
|
|
{
|
|
JSValue obj;
|
|
JSPromiseFunctionData *s;
|
|
JSPromiseFunctionDataResolved *sr;
|
|
int i, ret;
|
|
|
|
sr = js_malloc(ctx, sizeof(*sr));
|
|
if (!sr)
|
|
return -1;
|
|
sr->ref_count = 1;
|
|
sr->already_resolved = FALSE; /* must be shared between the two functions */
|
|
ret = 0;
|
|
for(i = 0; i < 2; i++) {
|
|
obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
|
|
JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
s = js_malloc(ctx, sizeof(*s));
|
|
if (!s) {
|
|
JS_FreeValue(ctx, obj);
|
|
fail:
|
|
|
|
if (i != 0)
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
sr->ref_count++;
|
|
s->presolved = sr;
|
|
s->promise = JS_DupValue(ctx, promise);
|
|
JS_SetOpaque(obj, s);
|
|
js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
|
|
resolving_funcs[i] = obj;
|
|
}
|
|
js_promise_resolve_function_free_resolved(ctx->rt, sr);
|
|
return ret;
|
|
}
|
|
|
|
static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
|
|
{
|
|
JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
|
|
if (s) {
|
|
js_promise_resolve_function_free_resolved(rt, s->presolved);
|
|
JS_FreeValueRT(rt, s->promise);
|
|
js_free_rt(rt, s);
|
|
}
|
|
}
|
|
|
|
static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
|
|
JS_MarkFunc *mark_func)
|
|
{
|
|
JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
|
|
if (s) {
|
|
JS_MarkValue(rt, s->promise, mark_func);
|
|
}
|
|
}
|
|
|
|
static JSValue js_promise_resolve_function_call(JSContext *ctx,
|
|
JSValueConst func_obj,
|
|
JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int flags)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(func_obj);
|
|
JSPromiseFunctionData *s;
|
|
JSValueConst resolution, args[3];
|
|
JSValue then;
|
|
BOOL is_reject;
|
|
|
|
s = p->u.promise_function_data;
|
|
if (!s || s->presolved->already_resolved)
|
|
return JS_UNDEFINED;
|
|
s->presolved->already_resolved = TRUE;
|
|
is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
|
|
if (argc > 0)
|
|
resolution = argv[0];
|
|
else
|
|
resolution = JS_UNDEFINED;
|
|
#ifdef DUMP_PROMISE
|
|
printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
|
|
JS_DumpValue(ctx, resolution);
|
|
printf("\n");
|
|
#endif
|
|
if (is_reject || !JS_IsObject(resolution)) {
|
|
goto done;
|
|
} else if (js_same_value(ctx, resolution, s->promise)) {
|
|
JS_ThrowTypeError(ctx, "promise self resolution");
|
|
goto fail_reject;
|
|
}
|
|
then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
|
|
if (JS_IsException(then)) {
|
|
JSValue error;
|
|
fail_reject:
|
|
error = JS_GetException(ctx);
|
|
reject_promise(ctx, s->promise, error);
|
|
JS_FreeValue(ctx, error);
|
|
} else if (!JS_IsFunction(ctx, then)) {
|
|
JS_FreeValue(ctx, then);
|
|
done:
|
|
fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
|
|
} else {
|
|
args[0] = s->promise;
|
|
args[1] = resolution;
|
|
args[2] = then;
|
|
JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
|
|
JS_FreeValue(ctx, then);
|
|
}
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
static void js_promise_finalizer(JSRuntime *rt, JSValue val)
|
|
{
|
|
JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
|
|
struct list_head *el, *el1;
|
|
int i;
|
|
|
|
if (!s)
|
|
return;
|
|
for(i = 0; i < 2; i++) {
|
|
list_for_each_safe(el, el1, &s->promise_reactions[i]) {
|
|
JSPromiseReactionData *rd =
|
|
list_entry(el, JSPromiseReactionData, link);
|
|
promise_reaction_data_free(rt, rd);
|
|
}
|
|
}
|
|
JS_FreeValueRT(rt, s->promise_result);
|
|
js_free_rt(rt, s);
|
|
}
|
|
|
|
static void js_promise_mark(JSRuntime *rt, JSValueConst val,
|
|
JS_MarkFunc *mark_func)
|
|
{
|
|
JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
|
|
struct list_head *el;
|
|
int i;
|
|
|
|
if (!s)
|
|
return;
|
|
for(i = 0; i < 2; i++) {
|
|
list_for_each(el, &s->promise_reactions[i]) {
|
|
JSPromiseReactionData *rd =
|
|
list_entry(el, JSPromiseReactionData, link);
|
|
JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
|
|
JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
|
|
JS_MarkValue(rt, rd->handler, mark_func);
|
|
}
|
|
}
|
|
JS_MarkValue(rt, s->promise_result, mark_func);
|
|
}
|
|
|
|
static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValueConst executor;
|
|
JSValue obj;
|
|
JSPromiseData *s;
|
|
JSValue args[2], ret;
|
|
int i;
|
|
|
|
executor = argv[0];
|
|
if (check_function(ctx, executor))
|
|
return JS_EXCEPTION;
|
|
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
|
|
if (JS_IsException(obj))
|
|
return JS_EXCEPTION;
|
|
s = js_mallocz(ctx, sizeof(*s));
|
|
if (!s)
|
|
goto fail;
|
|
s->promise_state = JS_PROMISE_PENDING;
|
|
s->is_handled = FALSE;
|
|
for(i = 0; i < 2; i++)
|
|
init_list_head(&s->promise_reactions[i]);
|
|
s->promise_result = JS_UNDEFINED;
|
|
JS_SetOpaque(obj, s);
|
|
if (js_create_resolving_functions(ctx, args, obj))
|
|
goto fail;
|
|
ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
|
|
if (JS_IsException(ret)) {
|
|
JSValue ret2, error;
|
|
error = JS_GetException(ctx);
|
|
ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
|
|
JS_FreeValue(ctx, error);
|
|
if (JS_IsException(ret2))
|
|
goto fail1;
|
|
JS_FreeValue(ctx, ret2);
|
|
}
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeValue(ctx, args[0]);
|
|
JS_FreeValue(ctx, args[1]);
|
|
return obj;
|
|
fail1:
|
|
JS_FreeValue(ctx, args[0]);
|
|
JS_FreeValue(ctx, args[1]);
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_promise_executor(JSContext *ctx,
|
|
JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < 2; i++) {
|
|
if (!JS_IsUndefined(func_data[i]))
|
|
return JS_ThrowTypeError(ctx, "resolving function already set");
|
|
func_data[i] = JS_DupValue(ctx, argv[i]);
|
|
}
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
static JSValue js_promise_executor_new(JSContext *ctx)
|
|
{
|
|
JSValueConst func_data[2];
|
|
|
|
func_data[0] = JS_UNDEFINED;
|
|
func_data[1] = JS_UNDEFINED;
|
|
return JS_NewCFunctionData(ctx, js_promise_executor, 2,
|
|
0, 2, func_data);
|
|
}
|
|
|
|
static JSValue js_new_promise_capability(JSContext *ctx,
|
|
JSValue *resolving_funcs,
|
|
JSValueConst ctor)
|
|
{
|
|
JSValue executor, result_promise;
|
|
JSCFunctionDataRecord *s;
|
|
int i;
|
|
|
|
executor = js_promise_executor_new(ctx);
|
|
if (JS_IsException(executor))
|
|
return executor;
|
|
|
|
if (JS_IsUndefined(ctor)) {
|
|
result_promise = js_promise_constructor(ctx, ctor, 1,
|
|
(JSValueConst *)&executor);
|
|
} else {
|
|
result_promise = JS_CallConstructor(ctx, ctor, 1,
|
|
(JSValueConst *)&executor);
|
|
}
|
|
if (JS_IsException(result_promise))
|
|
goto fail;
|
|
s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
|
|
for(i = 0; i < 2; i++) {
|
|
if (check_function(ctx, s->data[i]))
|
|
goto fail;
|
|
}
|
|
for(i = 0; i < 2; i++)
|
|
resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
|
|
JS_FreeValue(ctx, executor);
|
|
return result_promise;
|
|
fail:
|
|
JS_FreeValue(ctx, executor);
|
|
JS_FreeValue(ctx, result_promise);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
|
|
{
|
|
return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
|
|
}
|
|
|
|
JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSValue result_promise, resolving_funcs[2], ret;
|
|
BOOL is_reject = magic;
|
|
|
|
if (!JS_IsObject(this_val))
|
|
return JS_ThrowTypeErrorNotAnObject(ctx);
|
|
if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
|
|
JSValue ctor;
|
|
BOOL is_same;
|
|
ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
|
|
if (JS_IsException(ctor))
|
|
return ctor;
|
|
is_same = js_same_value(ctx, ctor, this_val);
|
|
JS_FreeValue(ctx, ctor);
|
|
if (is_same)
|
|
return JS_DupValue(ctx, argv[0]);
|
|
}
|
|
result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
|
|
if (JS_IsException(result_promise))
|
|
return result_promise;
|
|
ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
if (JS_IsException(ret)) {
|
|
JS_FreeValue(ctx, result_promise);
|
|
return ret;
|
|
}
|
|
JS_FreeValue(ctx, ret);
|
|
return result_promise;
|
|
}
|
|
|
|
#if 0
|
|
static JSValue js_promise___newPromiseCapability(JSContext *ctx,
|
|
JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue result_promise, resolving_funcs[2], obj;
|
|
JSValueConst ctor;
|
|
ctor = argv[0];
|
|
if (!JS_IsObject(ctor))
|
|
return JS_ThrowTypeErrorNotAnObject(ctx);
|
|
result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
|
|
if (JS_IsException(result_promise))
|
|
return result_promise;
|
|
obj = JS_NewObject(ctx);
|
|
if (JS_IsException(obj)) {
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
JS_FreeValue(ctx, result_promise);
|
|
return JS_EXCEPTION;
|
|
}
|
|
JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
|
|
JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
|
|
JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
|
|
return obj;
|
|
}
|
|
#endif
|
|
|
|
static __exception int remainingElementsCount_add(JSContext *ctx,
|
|
JSValueConst resolve_element_env,
|
|
int addend)
|
|
{
|
|
JSValue val;
|
|
int remainingElementsCount;
|
|
|
|
val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
|
|
if (JS_IsException(val))
|
|
return -1;
|
|
if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
|
|
return -1;
|
|
remainingElementsCount += addend;
|
|
if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
|
|
JS_NewInt32(ctx, remainingElementsCount)) < 0)
|
|
return -1;
|
|
return (remainingElementsCount == 0);
|
|
}
|
|
|
|
#define PROMISE_MAGIC_all 0
|
|
#define PROMISE_MAGIC_allSettled 1
|
|
#define PROMISE_MAGIC_any 2
|
|
|
|
/* used by C code. */
|
|
static JSValue js_aggregate_error_constructor(JSContext *ctx,
|
|
JSValueConst errors)
|
|
{
|
|
JSValue obj;
|
|
obj = JS_NewObjectProtoClass(ctx,
|
|
ctx->native_error_proto[JS_AGGREGATE_ERROR],
|
|
JS_CLASS_ERROR);
|
|
if (JS_IsException(obj))
|
|
return obj;
|
|
JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
|
|
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
|
return obj;
|
|
}
|
|
|
|
static JSValue js_promise_all_resolve_element(JSContext *ctx,
|
|
JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic,
|
|
JSValue *func_data)
|
|
{
|
|
int resolve_type = magic & 3;
|
|
int is_reject = magic & 4;
|
|
BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
|
|
JSValueConst values = func_data[2];
|
|
JSValueConst resolve = func_data[3];
|
|
JSValueConst resolve_element_env = func_data[4];
|
|
JSValue ret, obj;
|
|
int is_zero, index;
|
|
if (JS_ToInt32(ctx, &index, func_data[1]))
|
|
return JS_EXCEPTION;
|
|
if (alreadyCalled)
|
|
return JS_UNDEFINED;
|
|
func_data[0] = JS_NewBool(ctx, TRUE);
|
|
if (resolve_type == PROMISE_MAGIC_allSettled) {
|
|
JSValue str;
|
|
obj = JS_NewObject(ctx);
|
|
if (JS_IsException(obj))
|
|
return JS_EXCEPTION;
|
|
str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");
|
|
if (JS_IsException(str))
|
|
goto fail1;
|
|
if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
|
|
str,
|
|
JS_PROP_C_W_E) < 0)
|
|
goto fail1;
|
|
if (JS_DefinePropertyValue(ctx, obj,
|
|
is_reject ? JS_ATOM_reason : JS_ATOM_value,
|
|
JS_DupValue(ctx, argv[0]),
|
|
JS_PROP_C_W_E) < 0) {
|
|
fail1:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
} else {
|
|
obj = JS_DupValue(ctx, argv[0]);
|
|
}
|
|
if (JS_DefinePropertyValueUint32(ctx, values, index,
|
|
obj, JS_PROP_C_W_E) < 0)
|
|
return JS_EXCEPTION;
|
|
is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
|
|
if (is_zero < 0)
|
|
return JS_EXCEPTION;
|
|
if (is_zero) {
|
|
if (resolve_type == PROMISE_MAGIC_any) {
|
|
JSValue error;
|
|
error = js_aggregate_error_constructor(ctx, values);
|
|
if (JS_IsException(error))
|
|
return JS_EXCEPTION;
|
|
ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
|
|
JS_FreeValue(ctx, error);
|
|
} else {
|
|
ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
|
|
}
|
|
if (JS_IsException(ret))
|
|
return ret;
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
/* magic = 0: Promise.all 1: Promise.allSettled */
|
|
static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
|
|
JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
|
|
JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
|
|
JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
|
|
JSValueConst then_args[2], resolve_element_data[5];
|
|
BOOL done;
|
|
int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
|
|
|
|
if (!JS_IsObject(this_val))
|
|
return JS_ThrowTypeErrorNotAnObject(ctx);
|
|
result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
|
|
if (JS_IsException(result_promise))
|
|
return result_promise;
|
|
promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
|
|
if (JS_IsException(promise_resolve) ||
|
|
check_function(ctx, promise_resolve))
|
|
goto fail_reject;
|
|
iter = JS_GetIterator(ctx, argv[0], FALSE);
|
|
if (JS_IsException(iter)) {
|
|
JSValue error;
|
|
fail_reject:
|
|
error = JS_GetException(ctx);
|
|
ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
|
|
(JSValueConst *)&error);
|
|
JS_FreeValue(ctx, error);
|
|
if (JS_IsException(ret))
|
|
goto fail;
|
|
JS_FreeValue(ctx, ret);
|
|
} else {
|
|
next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
|
|
if (JS_IsException(next_method))
|
|
goto fail_reject;
|
|
values = JS_NewArray(ctx);
|
|
if (JS_IsException(values))
|
|
goto fail_reject;
|
|
resolve_element_env = JS_NewArray(ctx);
|
|
if (JS_IsException(resolve_element_env))
|
|
goto fail_reject;
|
|
/* remainingElementsCount field */
|
|
if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
|
|
JS_NewInt32(ctx, 1),
|
|
JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
|
|
goto fail_reject;
|
|
|
|
index = 0;
|
|
for(;;) {
|
|
/* XXX: conformance: should close the iterator if error on 'done'
|
|
access, but not on 'value' access */
|
|
item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
|
|
if (JS_IsException(item))
|
|
goto fail_reject;
|
|
if (done)
|
|
break;
|
|
next_promise = JS_Call(ctx, promise_resolve,
|
|
this_val, 1, (JSValueConst *)&item);
|
|
JS_FreeValue(ctx, item);
|
|
if (JS_IsException(next_promise)) {
|
|
fail_reject1:
|
|
JS_IteratorClose(ctx, iter, TRUE);
|
|
goto fail_reject;
|
|
}
|
|
resolve_element_data[0] = JS_NewBool(ctx, FALSE);
|
|
resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
|
|
resolve_element_data[2] = values;
|
|
resolve_element_data[3] = resolving_funcs[is_promise_any];
|
|
resolve_element_data[4] = resolve_element_env;
|
|
resolve_element =
|
|
JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
|
|
magic, 5, resolve_element_data);
|
|
if (JS_IsException(resolve_element)) {
|
|
JS_FreeValue(ctx, next_promise);
|
|
goto fail_reject1;
|
|
}
|
|
|
|
if (magic == PROMISE_MAGIC_allSettled) {
|
|
reject_element =
|
|
JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
|
|
magic | 4, 5, resolve_element_data);
|
|
if (JS_IsException(reject_element)) {
|
|
JS_FreeValue(ctx, next_promise);
|
|
goto fail_reject1;
|
|
}
|
|
} else if (magic == PROMISE_MAGIC_any) {
|
|
if (JS_DefinePropertyValueUint32(ctx, values, index,
|
|
JS_UNDEFINED, JS_PROP_C_W_E) < 0)
|
|
goto fail_reject1;
|
|
reject_element = resolve_element;
|
|
resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
|
|
} else {
|
|
reject_element = JS_DupValue(ctx, resolving_funcs[1]);
|
|
}
|
|
|
|
if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
|
|
JS_FreeValue(ctx, next_promise);
|
|
JS_FreeValue(ctx, resolve_element);
|
|
JS_FreeValue(ctx, reject_element);
|
|
goto fail_reject1;
|
|
}
|
|
|
|
then_args[0] = resolve_element;
|
|
then_args[1] = reject_element;
|
|
ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
|
|
JS_FreeValue(ctx, resolve_element);
|
|
JS_FreeValue(ctx, reject_element);
|
|
if (check_exception_free(ctx, ret))
|
|
goto fail_reject1;
|
|
index++;
|
|
}
|
|
|
|
is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
|
|
if (is_zero < 0)
|
|
goto fail_reject;
|
|
if (is_zero) {
|
|
if (magic == PROMISE_MAGIC_any) {
|
|
JSValue error;
|
|
error = js_aggregate_error_constructor(ctx, values);
|
|
if (JS_IsException(error))
|
|
goto fail_reject;
|
|
JS_FreeValue(ctx, values);
|
|
values = error;
|
|
}
|
|
ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
|
|
1, (JSValueConst *)&values);
|
|
if (check_exception_free(ctx, ret))
|
|
goto fail_reject;
|
|
}
|
|
}
|
|
done:
|
|
JS_FreeValue(ctx, promise_resolve);
|
|
JS_FreeValue(ctx, resolve_element_env);
|
|
JS_FreeValue(ctx, values);
|
|
JS_FreeValue(ctx, next_method);
|
|
JS_FreeValue(ctx, iter);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
return result_promise;
|
|
fail:
|
|
JS_FreeValue(ctx, result_promise);
|
|
result_promise = JS_EXCEPTION;
|
|
goto done;
|
|
}
|
|
|
|
static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
|
|
JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
|
|
JSValue promise_resolve = JS_UNDEFINED;
|
|
BOOL done;
|
|
|
|
if (!JS_IsObject(this_val))
|
|
return JS_ThrowTypeErrorNotAnObject(ctx);
|
|
result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
|
|
if (JS_IsException(result_promise))
|
|
return result_promise;
|
|
promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
|
|
if (JS_IsException(promise_resolve) ||
|
|
check_function(ctx, promise_resolve))
|
|
goto fail_reject;
|
|
iter = JS_GetIterator(ctx, argv[0], FALSE);
|
|
if (JS_IsException(iter)) {
|
|
JSValue error;
|
|
fail_reject:
|
|
error = JS_GetException(ctx);
|
|
ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
|
|
(JSValueConst *)&error);
|
|
JS_FreeValue(ctx, error);
|
|
if (JS_IsException(ret))
|
|
goto fail;
|
|
JS_FreeValue(ctx, ret);
|
|
} else {
|
|
next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
|
|
if (JS_IsException(next_method))
|
|
goto fail_reject;
|
|
|
|
for(;;) {
|
|
/* XXX: conformance: should close the iterator if error on 'done'
|
|
access, but not on 'value' access */
|
|
item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
|
|
if (JS_IsException(item))
|
|
goto fail_reject;
|
|
if (done)
|
|
break;
|
|
next_promise = JS_Call(ctx, promise_resolve,
|
|
this_val, 1, (JSValueConst *)&item);
|
|
JS_FreeValue(ctx, item);
|
|
if (JS_IsException(next_promise)) {
|
|
fail_reject1:
|
|
JS_IteratorClose(ctx, iter, TRUE);
|
|
goto fail_reject;
|
|
}
|
|
ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
|
|
(JSValueConst *)resolving_funcs);
|
|
if (check_exception_free(ctx, ret))
|
|
goto fail_reject1;
|
|
}
|
|
}
|
|
done:
|
|
JS_FreeValue(ctx, promise_resolve);
|
|
JS_FreeValue(ctx, next_method);
|
|
JS_FreeValue(ctx, iter);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
return result_promise;
|
|
fail:
|
|
//JS_FreeValue(ctx, next_method); // why not???
|
|
JS_FreeValue(ctx, result_promise);
|
|
result_promise = JS_EXCEPTION;
|
|
goto done;
|
|
}
|
|
|
|
int perform_promise_then(JSContext *ctx,
|
|
JSValueConst promise,
|
|
JSValueConst *resolve_reject,
|
|
JSValueConst *cap_resolving_funcs)
|
|
{
|
|
JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
|
|
JSPromiseReactionData *rd_array[2], *rd;
|
|
int i, j;
|
|
rd_array[0] = NULL;
|
|
rd_array[1] = NULL;
|
|
for(i = 0; i < 2; i++) {
|
|
JSValueConst handler;
|
|
rd = js_mallocz(ctx, sizeof(*rd));
|
|
if (!rd) {
|
|
if (i == 1)
|
|
promise_reaction_data_free(ctx->rt, rd_array[0]);
|
|
return -1;
|
|
}
|
|
for(j = 0; j < 2; j++)
|
|
rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
|
|
handler = resolve_reject[i];
|
|
if (!JS_IsFunction(ctx, handler))
|
|
handler = JS_UNDEFINED;
|
|
rd->handler = JS_DupValue(ctx, handler);
|
|
rd_array[i] = rd;
|
|
}
|
|
if (s->promise_state == JS_PROMISE_PENDING) {
|
|
for(i = 0; i < 2; i++)
|
|
list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
|
|
} else {
|
|
JSValueConst args[5];
|
|
if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
|
|
JSRuntime *rt = ctx->rt;
|
|
if (rt->host_promise_rejection_tracker) {
|
|
rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
|
|
TRUE, rt->host_promise_rejection_tracker_opaque);
|
|
}
|
|
}
|
|
i = s->promise_state - JS_PROMISE_FULFILLED;
|
|
rd = rd_array[i];
|
|
args[0] = rd->resolving_funcs[0];
|
|
args[1] = rd->resolving_funcs[1];
|
|
args[2] = rd->handler;
|
|
args[3] = JS_NewBool(ctx, i);
|
|
args[4] = s->promise_result;
|
|
JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
|
|
for(i = 0; i < 2; i++)
|
|
promise_reaction_data_free(ctx->rt, rd_array[i]);
|
|
}
|
|
s->is_handled = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue ctor, result_promise, resolving_funcs[2];
|
|
JSPromiseData *s;
|
|
int i, ret;
|
|
|
|
s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
|
|
if (!s)
|
|
return JS_EXCEPTION;
|
|
|
|
ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
|
|
if (JS_IsException(ctor))
|
|
return ctor;
|
|
result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
|
|
JS_FreeValue(ctx, ctor);
|
|
if (JS_IsException(result_promise))
|
|
return result_promise;
|
|
ret = perform_promise_then(ctx, this_val, argv,
|
|
(JSValueConst *)resolving_funcs);
|
|
for(i = 0; i < 2; i++)
|
|
JS_FreeValue(ctx, resolving_funcs[i]);
|
|
if (ret) {
|
|
JS_FreeValue(ctx, result_promise);
|
|
return JS_EXCEPTION;
|
|
}
|
|
return result_promise;
|
|
}
|
|
|
|
static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValueConst args[2];
|
|
args[0] = JS_UNDEFINED;
|
|
args[1] = argv[0];
|
|
return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
|
|
}
|
|
|
|
static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data)
|
|
{
|
|
return JS_DupValue(ctx, func_data[0]);
|
|
}
|
|
|
|
static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data)
|
|
{
|
|
return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
|
|
}
|
|
|
|
static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data)
|
|
{
|
|
JSValueConst ctor = func_data[0];
|
|
JSValueConst onFinally = func_data[1];
|
|
JSValue res, promise, ret, then_func;
|
|
|
|
res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
|
|
if (JS_IsException(res))
|
|
return res;
|
|
promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
|
|
JS_FreeValue(ctx, res);
|
|
if (JS_IsException(promise))
|
|
return promise;
|
|
if (magic == 0) {
|
|
then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
|
|
0, 1, argv);
|
|
} else {
|
|
then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
|
|
0, 1, argv);
|
|
}
|
|
if (JS_IsException(then_func)) {
|
|
JS_FreeValue(ctx, promise);
|
|
return then_func;
|
|
}
|
|
ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
|
|
JS_FreeValue(ctx, then_func);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValueConst onFinally = argv[0];
|
|
JSValue ctor, ret;
|
|
JSValue then_funcs[2];
|
|
JSValueConst func_data[2];
|
|
int i;
|
|
|
|
ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
|
|
if (JS_IsException(ctor))
|
|
return ctor;
|
|
if (!JS_IsFunction(ctx, onFinally)) {
|
|
then_funcs[0] = JS_DupValue(ctx, onFinally);
|
|
then_funcs[1] = JS_DupValue(ctx, onFinally);
|
|
} else {
|
|
func_data[0] = ctor;
|
|
func_data[1] = onFinally;
|
|
for(i = 0; i < 2; i++) {
|
|
then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
|
|
if (JS_IsException(then_funcs[i])) {
|
|
if (i == 1)
|
|
JS_FreeValue(ctx, then_funcs[0]);
|
|
JS_FreeValue(ctx, ctor);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, ctor);
|
|
ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
|
|
JS_FreeValue(ctx, then_funcs[0]);
|
|
JS_FreeValue(ctx, then_funcs[1]);
|
|
return ret;
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_promise_funcs[] = {
|
|
JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
|
|
JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
|
|
JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
|
|
JS_CFUNC_DEF("race", 1, js_promise_race ),
|
|
//JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),
|
|
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_promise_proto_funcs[] = {
|
|
JS_CFUNC_DEF("then", 2, js_promise_then ),
|
|
JS_CFUNC_DEF("catch", 1, js_promise_catch ),
|
|
JS_CFUNC_DEF("finally", 1, js_promise_finally ),
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
|
|
};
|
|
|
|
/* magic = GEN_MAGIC_x */
|
|
static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic)
|
|
{
|
|
JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
|
|
JSValue promise, resolving_funcs[2];
|
|
JSAsyncGeneratorRequest *req;
|
|
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
|
if (JS_IsException(promise))
|
|
return JS_EXCEPTION;
|
|
if (!s) {
|
|
JSValue err, res2;
|
|
JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
|
|
err = JS_GetException(ctx);
|
|
res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
|
|
1, (JSValueConst *)&err);
|
|
JS_FreeValue(ctx, err);
|
|
JS_FreeValue(ctx, res2);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
return promise;
|
|
}
|
|
req = js_mallocz(ctx, sizeof(*req));
|
|
if (!req)
|
|
goto fail;
|
|
req->completion_type = magic;
|
|
req->result = JS_DupValue(ctx, argv[0]);
|
|
req->promise = JS_DupValue(ctx, promise);
|
|
req->resolving_funcs[0] = resolving_funcs[0];
|
|
req->resolving_funcs[1] = resolving_funcs[1];
|
|
list_add_tail(&req->link, &s->queue);
|
|
if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
|
|
js_async_generator_resume_next(ctx, s);
|
|
}
|
|
return promise;
|
|
fail:
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
JS_FreeValue(ctx, promise);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
|
|
JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
|
|
JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
|
|
JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
|
|
};
|
|
|
|
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;
|
|
if (s) {
|
|
js_async_function_free(rt, s);
|
|
}
|
|
}
|
|
|
|
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;
|
|
if (s) {
|
|
mark_func(rt, &s->header);
|
|
}
|
|
}
|
|
|
|
static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
|
|
{
|
|
JSAsyncFromSyncIteratorData *s =
|
|
JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
|
|
if (s) {
|
|
JS_FreeValueRT(rt, s->sync_iter);
|
|
JS_FreeValueRT(rt, s->next_method);
|
|
js_free_rt(rt, s);
|
|
}
|
|
}
|
|
|
|
static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
|
|
JS_MarkFunc *mark_func)
|
|
{
|
|
JSAsyncFromSyncIteratorData *s =
|
|
JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
|
|
if (s) {
|
|
JS_MarkValue(rt, s->sync_iter, mark_func);
|
|
JS_MarkValue(rt, s->next_method, mark_func);
|
|
}
|
|
}
|
|
|
|
static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
|
|
{
|
|
JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
|
|
if (s) {
|
|
js_async_generator_free(rt, s);
|
|
}
|
|
}
|
|
|
|
static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
|
|
JS_MarkFunc *mark_func)
|
|
{
|
|
JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
|
|
struct list_head *el;
|
|
JSAsyncGeneratorRequest *req;
|
|
if (s) {
|
|
list_for_each(el, &s->queue) {
|
|
req = list_entry(el, JSAsyncGeneratorRequest, link);
|
|
JS_MarkValue(rt, req->result, mark_func);
|
|
JS_MarkValue(rt, req->promise, mark_func);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static JSClassShortDef const js_async_class_def[] = {
|
|
{ JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */
|
|
{ JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
|
|
{ JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
|
|
{ JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */
|
|
{ JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
|
|
{ JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
|
|
{ JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
|
|
{ JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
|
|
{ JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */
|
|
};
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 int js_async_function_resolve_create(JSContext *ctx,
|
|
JSAsyncFunctionData *s,
|
|
JSValue *resolving_funcs)
|
|
{
|
|
int i;
|
|
JSObject *p;
|
|
for(i = 0; i < 2; i++) {
|
|
resolving_funcs[i] =
|
|
JS_NewObjectProtoClass(ctx, ctx->function_proto,
|
|
JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
|
|
if (JS_IsException(resolving_funcs[i])) {
|
|
if (i == 1)
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
return -1;
|
|
}
|
|
p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
|
|
s->header.ref_count++;
|
|
p->u.async_function_data = s;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *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);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
static JSValue js_async_function_resolve_call(JSContext *ctx,
|
|
JSValueConst func_obj,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv,
|
|
int flags)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(func_obj);
|
|
JSAsyncFunctionData *s = p->u.async_function_data;
|
|
BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
|
|
JSValueConst arg;
|
|
if (argc > 0)
|
|
arg = argv[0];
|
|
else
|
|
arg = JS_UNDEFINED;
|
|
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);
|
|
}
|
|
js_async_function_resume(ctx, s);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv, int flags)
|
|
{
|
|
JSValue promise;
|
|
JSAsyncFunctionData *s;
|
|
s = js_mallocz(ctx, sizeof(*s));
|
|
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);
|
|
return JS_EXCEPTION;
|
|
}
|
|
s->is_active = TRUE;
|
|
js_async_function_resume(ctx, s);
|
|
js_async_function_free(ctx->rt, s);
|
|
return promise;
|
|
}
|
|
|
|
static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv,
|
|
int flags)
|
|
{
|
|
JSValue obj, func_ret;
|
|
JSAsyncGeneratorData *s;
|
|
s = js_mallocz(ctx, sizeof(*s));
|
|
if (!s)
|
|
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;
|
|
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);
|
|
if (JS_IsException(func_ret))
|
|
goto fail;
|
|
JS_FreeValue(ctx, func_ret);
|
|
obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
s->generator = JS_VALUE_GET_OBJ(obj);
|
|
JS_SetOpaque(obj, s);
|
|
return obj;
|
|
fail:
|
|
js_async_generator_free(ctx->rt, s);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
/* AsyncFunction */
|
|
static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
|
|
JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
|
|
};
|
|
|
|
static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
|
|
JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data)
|
|
{
|
|
return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
|
|
JS_ToBool(ctx, func_data[0]));
|
|
}
|
|
|
|
static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
|
|
BOOL done)
|
|
{
|
|
JSValueConst func_data[1];
|
|
func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
|
|
return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
|
|
1, 0, 1, func_data);
|
|
}
|
|
|
|
static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic)
|
|
{
|
|
JSValue promise, resolving_funcs[2], value, err, method;
|
|
JSAsyncFromSyncIteratorData *s;
|
|
int done;
|
|
int is_reject;
|
|
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
|
if (JS_IsException(promise))
|
|
return JS_EXCEPTION;
|
|
s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
|
|
if (!s) {
|
|
JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
|
|
goto reject;
|
|
}
|
|
if (magic == GEN_MAGIC_NEXT) {
|
|
method = JS_DupValue(ctx, s->next_method);
|
|
} else {
|
|
method = JS_GetProperty(ctx, s->sync_iter,
|
|
magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
|
|
JS_ATOM_throw);
|
|
if (JS_IsException(method))
|
|
goto reject;
|
|
if (JS_IsUndefined(method) || JS_IsNull(method)) {
|
|
if (magic == GEN_MAGIC_RETURN) {
|
|
err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
|
|
is_reject = 0;
|
|
} else {
|
|
err = JS_DupValue(ctx, argv[0]);
|
|
is_reject = 1;
|
|
}
|
|
goto done_resolve;
|
|
}
|
|
}
|
|
value = JS_IteratorNext2(ctx, s->sync_iter, method,
|
|
argc >= 1 ? 1 : 0, argv, &done);
|
|
JS_FreeValue(ctx, method);
|
|
if (JS_IsException(value))
|
|
goto reject;
|
|
if (done == 2) {
|
|
JSValue obj = value;
|
|
value = JS_IteratorGetCompleteValue(ctx, obj, &done);
|
|
JS_FreeValue(ctx, obj);
|
|
if (JS_IsException(value))
|
|
goto reject;
|
|
}
|
|
if (JS_IsException(value)) {
|
|
JSValue res2;
|
|
reject:
|
|
err = JS_GetException(ctx);
|
|
is_reject = 1;
|
|
done_resolve:
|
|
res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
|
|
1, (JSValueConst *)&err);
|
|
JS_FreeValue(ctx, err);
|
|
JS_FreeValue(ctx, res2);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
return promise;
|
|
}
|
|
{
|
|
JSValue value_wrapper_promise, resolve_reject[2];
|
|
int res;
|
|
value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
|
1, (JSValueConst *)&value, 0);
|
|
if (JS_IsException(value_wrapper_promise)) {
|
|
JS_FreeValue(ctx, value);
|
|
goto reject;
|
|
}
|
|
resolve_reject[0] =
|
|
js_async_from_sync_iterator_unwrap_func_create(ctx, done);
|
|
if (JS_IsException(resolve_reject[0])) {
|
|
JS_FreeValue(ctx, value_wrapper_promise);
|
|
goto fail;
|
|
}
|
|
JS_FreeValue(ctx, value);
|
|
resolve_reject[1] = JS_UNDEFINED;
|
|
res = perform_promise_then(ctx, value_wrapper_promise,
|
|
(JSValueConst *)resolve_reject,
|
|
(JSValueConst *)resolving_funcs);
|
|
JS_FreeValue(ctx, resolve_reject[0]);
|
|
JS_FreeValue(ctx, value_wrapper_promise);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
if (res) {
|
|
JS_FreeValue(ctx, promise);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
return promise;
|
|
fail:
|
|
JS_FreeValue(ctx, value);
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
|
JS_FreeValue(ctx, promise);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
|
|
JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
|
|
JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
|
|
JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
|
|
};
|
|
|
|
void JS_AddIntrinsicPromise(JSContext *ctx)
|
|
{
|
|
JSRuntime *rt = ctx->rt;
|
|
JSValue obj1;
|
|
if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
|
|
init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
|
|
countof(js_async_class_def));
|
|
rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
|
|
rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
|
|
rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
|
|
rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
|
|
rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
|
|
rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
|
|
}
|
|
/* Promise */
|
|
ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
|
|
js_promise_proto_funcs,
|
|
countof(js_promise_proto_funcs));
|
|
obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
|
|
JS_CFUNC_constructor, 0);
|
|
ctx->promise_ctor = JS_DupValue(ctx, obj1);
|
|
JS_SetPropertyFunctionList(ctx, obj1,
|
|
js_promise_funcs,
|
|
countof(js_promise_funcs));
|
|
JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
|
|
ctx->class_proto[JS_CLASS_PROMISE]);
|
|
/* AsyncFunction */
|
|
ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
|
|
obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
|
|
"AsyncFunction", 1,
|
|
JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
|
|
ctx->function_ctor);
|
|
JS_SetPropertyFunctionList(ctx,
|
|
ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
|
|
js_async_function_proto_funcs,
|
|
countof(js_async_function_proto_funcs));
|
|
JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
|
|
0, JS_PROP_CONFIGURABLE);
|
|
JS_FreeValue(ctx, obj1);
|
|
/* AsyncIteratorPrototype */
|
|
ctx->async_iterator_proto = JS_NewObject(ctx);
|
|
JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
|
|
js_async_iterator_proto_funcs,
|
|
countof(js_async_iterator_proto_funcs));
|
|
/* AsyncFromSyncIteratorPrototype */
|
|
ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
|
|
JS_NewObjectProto(ctx, ctx->async_iterator_proto);
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
|
|
js_async_from_sync_iterator_proto_funcs,
|
|
countof(js_async_from_sync_iterator_proto_funcs));
|
|
/* AsyncGeneratorPrototype */
|
|
ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
|
|
JS_NewObjectProto(ctx, ctx->async_iterator_proto);
|
|
JS_SetPropertyFunctionList(ctx,
|
|
ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
|
|
js_async_generator_proto_funcs,
|
|
countof(js_async_generator_proto_funcs));
|
|
/* AsyncGeneratorFunction */
|
|
ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
|
|
JS_NewObjectProto(ctx, ctx->function_proto);
|
|
obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
|
|
"AsyncGeneratorFunction", 1,
|
|
JS_CFUNC_constructor_or_func_magic,
|
|
JS_FUNC_ASYNC_GENERATOR,
|
|
ctx->function_ctor);
|
|
JS_SetPropertyFunctionList(ctx,
|
|
ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
|
|
js_async_generator_function_proto_funcs,
|
|
countof(js_async_generator_function_proto_funcs));
|
|
JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
|
|
ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
|
|
JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
|
|
JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
|
|
0, JS_PROP_CONFIGURABLE);
|
|
JS_FreeValue(ctx, obj1);
|
|
}
|