mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
10fd8bdb70
This change resurrects ae5d06dc53
510 lines
19 KiB
C
510 lines
19 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 "libc/runtime/runtime.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 JSGeneratorStateEnum {
|
|
JS_GENERATOR_STATE_SUSPENDED_START,
|
|
JS_GENERATOR_STATE_SUSPENDED_YIELD,
|
|
JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
|
|
JS_GENERATOR_STATE_EXECUTING,
|
|
JS_GENERATOR_STATE_COMPLETED,
|
|
} JSGeneratorStateEnum;
|
|
|
|
typedef struct JSGeneratorData {
|
|
JSGeneratorStateEnum state;
|
|
JSAsyncFunctionState func_state;
|
|
} JSGeneratorData;
|
|
|
|
static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
|
|
{
|
|
if (s->state == JS_GENERATOR_STATE_COMPLETED)
|
|
return;
|
|
async_func_free(rt, &s->func_state);
|
|
s->state = JS_GENERATOR_STATE_COMPLETED;
|
|
}
|
|
|
|
void js_generator_finalizer(JSRuntime *rt, JSValue obj)
|
|
{
|
|
JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
|
|
if (s) {
|
|
free_generator_stack_rt(rt, s);
|
|
js_free_rt(rt, s);
|
|
}
|
|
}
|
|
|
|
static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
|
|
{
|
|
free_generator_stack_rt(ctx->rt, s);
|
|
}
|
|
|
|
void js_generator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
|
JSGeneratorData *s = p->u.generator_data;
|
|
if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
|
|
return;
|
|
async_func_mark(rt, &s->func_state, mark_func);
|
|
}
|
|
|
|
static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
BOOL *pdone, int magic)
|
|
{
|
|
JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
|
|
JSStackFrame *sf;
|
|
JSValue ret, func_ret;
|
|
*pdone = TRUE;
|
|
if (!s)
|
|
return JS_ThrowTypeError(ctx, "not a generator");
|
|
sf = &s->func_state.frame;
|
|
switch(s->state) {
|
|
default:
|
|
case JS_GENERATOR_STATE_SUSPENDED_START:
|
|
if (magic == GEN_MAGIC_NEXT) {
|
|
goto exec_no_arg;
|
|
} else {
|
|
free_generator_stack(ctx, s);
|
|
goto done;
|
|
}
|
|
break;
|
|
case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
|
|
case JS_GENERATOR_STATE_SUSPENDED_YIELD:
|
|
/* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
|
|
ret = JS_DupValue(ctx, argv[0]);
|
|
if (magic == GEN_MAGIC_THROW &&
|
|
s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
|
|
JS_Throw(ctx, ret);
|
|
s->func_state.throw_flag = TRUE;
|
|
} else {
|
|
sf->cur_sp[-1] = ret;
|
|
sf->cur_sp[0] = JS_NewInt32(ctx, magic);
|
|
sf->cur_sp++;
|
|
exec_no_arg:
|
|
s->func_state.throw_flag = FALSE;
|
|
}
|
|
s->state = JS_GENERATOR_STATE_EXECUTING;
|
|
func_ret = async_func_resume(ctx, &s->func_state);
|
|
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
|
|
if (JS_IsException(func_ret)) {
|
|
/* finalize the execution in case of exception */
|
|
free_generator_stack(ctx, s);
|
|
return func_ret;
|
|
}
|
|
if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
|
/* get the returned yield value at the top of the stack */
|
|
ret = sf->cur_sp[-1];
|
|
sf->cur_sp[-1] = JS_UNDEFINED;
|
|
if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
|
|
s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
|
|
/* return (value, done) object */
|
|
*pdone = 2;
|
|
} else {
|
|
*pdone = FALSE;
|
|
}
|
|
} else {
|
|
/* end of iterator */
|
|
ret = sf->cur_sp[-1];
|
|
sf->cur_sp[-1] = JS_UNDEFINED;
|
|
JS_FreeValue(ctx, func_ret);
|
|
free_generator_stack(ctx, s);
|
|
}
|
|
break;
|
|
case JS_GENERATOR_STATE_COMPLETED:
|
|
done:
|
|
/* execution is finished */
|
|
switch(magic) {
|
|
default:
|
|
case GEN_MAGIC_NEXT:
|
|
ret = JS_UNDEFINED;
|
|
break;
|
|
case GEN_MAGIC_RETURN:
|
|
ret = JS_DupValue(ctx, argv[0]);
|
|
break;
|
|
case GEN_MAGIC_THROW:
|
|
ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
|
|
break;
|
|
}
|
|
break;
|
|
case JS_GENERATOR_STATE_EXECUTING:
|
|
ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
|
|
JSValueConst this_obj, int argc,
|
|
JSValueConst *argv, int flags)
|
|
{
|
|
JSValue obj, func_ret;
|
|
JSGeneratorData *s;
|
|
s = js_mallocz(ctx, sizeof(*s));
|
|
if (!s)
|
|
return JS_EXCEPTION;
|
|
s->state = JS_GENERATOR_STATE_SUSPENDED_START;
|
|
if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
|
|
s->state = JS_GENERATOR_STATE_COMPLETED;
|
|
goto fail;
|
|
}
|
|
/* execute the function up to 'OP_initial_yield' */
|
|
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_GENERATOR);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
JS_SetOpaque(obj, s);
|
|
return obj;
|
|
fail:
|
|
free_generator_stack_rt(ctx->rt, s);
|
|
js_free(ctx, s);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_async_generator_resolve_function(JSContext *ctx,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data);
|
|
|
|
static int js_async_generator_resolve_function_create(JSContext *ctx,
|
|
JSValueConst generator,
|
|
JSValue *resolving_funcs,
|
|
BOOL is_resume_next)
|
|
{
|
|
int i;
|
|
JSValue func;
|
|
for(i = 0; i < 2; i++) {
|
|
func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
|
|
i + is_resume_next * 2, 1, &generator);
|
|
if (JS_IsException(func)) {
|
|
if (i == 1)
|
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
|
return -1;
|
|
}
|
|
resolving_funcs[i] = func;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int js_async_generator_await(JSContext *ctx,
|
|
JSAsyncGeneratorData *s,
|
|
JSValueConst value)
|
|
{
|
|
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
|
|
int i, res;
|
|
promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
|
1, &value, 0);
|
|
if (JS_IsException(promise))
|
|
goto fail;
|
|
if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
|
|
resolving_funcs, FALSE)) {
|
|
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;
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
static void js_async_generator_resolve_or_reject(JSContext *ctx,
|
|
JSAsyncGeneratorData *s,
|
|
JSValueConst result,
|
|
int is_reject)
|
|
{
|
|
JSAsyncGeneratorRequest *next;
|
|
JSValue ret;
|
|
next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
|
|
list_del(&next->link);
|
|
ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
|
|
&result);
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeValue(ctx, next->result);
|
|
JS_FreeValue(ctx, next->promise);
|
|
JS_FreeValue(ctx, next->resolving_funcs[0]);
|
|
JS_FreeValue(ctx, next->resolving_funcs[1]);
|
|
js_free(ctx, next);
|
|
}
|
|
|
|
static void js_async_generator_resolve(JSContext *ctx,
|
|
JSAsyncGeneratorData *s,
|
|
JSValueConst value,
|
|
BOOL done)
|
|
{
|
|
JSValue result;
|
|
result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
|
|
/* XXX: better exception handling ? */
|
|
js_async_generator_resolve_or_reject(ctx, s, result, 0);
|
|
JS_FreeValue(ctx, result);
|
|
}
|
|
|
|
static void js_async_generator_reject(JSContext *ctx,
|
|
JSAsyncGeneratorData *s,
|
|
JSValueConst exception)
|
|
{
|
|
js_async_generator_resolve_or_reject(ctx, s, exception, 1);
|
|
}
|
|
|
|
static void js_async_generator_complete(JSContext *ctx,
|
|
JSAsyncGeneratorData *s)
|
|
{
|
|
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
|
|
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
|
async_func_free(ctx->rt, &s->func_state);
|
|
}
|
|
}
|
|
|
|
static int js_async_generator_completed_return(JSContext *ctx,
|
|
JSAsyncGeneratorData *s,
|
|
JSValueConst value)
|
|
{
|
|
JSValue promise, resolving_funcs[2], resolving_funcs1[2];
|
|
int res;
|
|
|
|
promise = js_promise_resolve(ctx, ctx->promise_ctor,
|
|
1, (JSValueConst *)&value, 0);
|
|
if (JS_IsException(promise))
|
|
return -1;
|
|
if (js_async_generator_resolve_function_create(ctx,
|
|
JS_MKPTR(JS_TAG_OBJECT, s->generator),
|
|
resolving_funcs1,
|
|
TRUE)) {
|
|
JS_FreeValue(ctx, promise);
|
|
return -1;
|
|
}
|
|
resolving_funcs[0] = JS_UNDEFINED;
|
|
resolving_funcs[1] = JS_UNDEFINED;
|
|
res = perform_promise_then(ctx, promise,
|
|
(JSValueConst *)resolving_funcs1,
|
|
(JSValueConst *)resolving_funcs);
|
|
JS_FreeValue(ctx, resolving_funcs1[0]);
|
|
JS_FreeValue(ctx, resolving_funcs1[1]);
|
|
JS_FreeValue(ctx, promise);
|
|
return res;
|
|
}
|
|
|
|
void js_async_generator_resume_next(JSContext *ctx, JSAsyncGeneratorData *s)
|
|
{
|
|
JSAsyncGeneratorRequest *next;
|
|
JSValue func_ret, value;
|
|
for(;;) {
|
|
if (list_empty(&s->queue))
|
|
break;
|
|
next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
|
|
switch(s->state) {
|
|
case JS_ASYNC_GENERATOR_STATE_EXECUTING:
|
|
/* only happens when restarting execution after await() */
|
|
goto resume_exec;
|
|
case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
|
|
goto done;
|
|
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
|
|
if (next->completion_type == GEN_MAGIC_NEXT) {
|
|
goto exec_no_arg;
|
|
} else {
|
|
js_async_generator_complete(ctx, s);
|
|
}
|
|
break;
|
|
case JS_ASYNC_GENERATOR_STATE_COMPLETED:
|
|
if (next->completion_type == GEN_MAGIC_NEXT) {
|
|
js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
|
|
} else if (next->completion_type == GEN_MAGIC_RETURN) {
|
|
s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
|
|
js_async_generator_completed_return(ctx, s, next->result);
|
|
goto done;
|
|
} else {
|
|
js_async_generator_reject(ctx, s, next->result);
|
|
}
|
|
goto done;
|
|
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
|
|
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
|
|
value = JS_DupValue(ctx, next->result);
|
|
if (next->completion_type == GEN_MAGIC_THROW &&
|
|
s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
|
|
JS_Throw(ctx, value);
|
|
s->func_state.throw_flag = TRUE;
|
|
} else {
|
|
/* 'yield' returns a value. 'yield *' also returns a value
|
|
in case the 'throw' method is called */
|
|
s->func_state.frame.cur_sp[-1] = value;
|
|
s->func_state.frame.cur_sp[0] =
|
|
JS_NewInt32(ctx, next->completion_type);
|
|
s->func_state.frame.cur_sp++;
|
|
exec_no_arg:
|
|
s->func_state.throw_flag = FALSE;
|
|
}
|
|
s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
|
|
resume_exec:
|
|
func_ret = async_func_resume(ctx, &s->func_state);
|
|
if (JS_IsException(func_ret)) {
|
|
value = JS_GetException(ctx);
|
|
js_async_generator_complete(ctx, s);
|
|
js_async_generator_reject(ctx, s, value);
|
|
JS_FreeValue(ctx, value);
|
|
} else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
|
|
int func_ret_code;
|
|
value = s->func_state.frame.cur_sp[-1];
|
|
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
|
func_ret_code = JS_VALUE_GET_INT(func_ret);
|
|
switch(func_ret_code) {
|
|
case FUNC_RET_YIELD:
|
|
case FUNC_RET_YIELD_STAR:
|
|
if (func_ret_code == FUNC_RET_YIELD_STAR)
|
|
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
|
|
else
|
|
s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
|
|
js_async_generator_resolve(ctx, s, value, FALSE);
|
|
JS_FreeValue(ctx, value);
|
|
break;
|
|
case FUNC_RET_AWAIT:
|
|
js_async_generator_await(ctx, s, value);
|
|
JS_FreeValue(ctx, value);
|
|
goto done;
|
|
default:
|
|
abort();
|
|
}
|
|
} else {
|
|
assert(JS_IsUndefined(func_ret));
|
|
/* end of function */
|
|
value = s->func_state.frame.cur_sp[-1];
|
|
s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
|
|
js_async_generator_complete(ctx, s);
|
|
js_async_generator_resolve(ctx, s, value, TRUE);
|
|
JS_FreeValue(ctx, value);
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
done: ;
|
|
}
|
|
|
|
static JSValue js_async_generator_resolve_function(JSContext *ctx,
|
|
JSValueConst this_obj,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValue *func_data)
|
|
{
|
|
BOOL is_reject = magic & 1;
|
|
JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
|
|
JSValueConst arg = argv[0];
|
|
/* XXX: what if s == NULL */
|
|
if (magic >= 2) {
|
|
/* resume next case in AWAITING_RETURN state */
|
|
assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
|
|
s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
|
|
s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
|
|
if (is_reject) {
|
|
js_async_generator_reject(ctx, s, arg);
|
|
} else {
|
|
js_async_generator_resolve(ctx, s, arg, TRUE);
|
|
}
|
|
} else {
|
|
/* restart function execution after await() */
|
|
assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
|
|
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_generator_resume_next(ctx, s);
|
|
}
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
void js_async_generator_free(JSRuntime *rt, JSAsyncGeneratorData *s)
|
|
{
|
|
struct list_head *el, *el1;
|
|
JSAsyncGeneratorRequest *req;
|
|
list_for_each_safe(el, el1, &s->queue) {
|
|
req = list_entry(el, JSAsyncGeneratorRequest, link);
|
|
JS_FreeValueRT(rt, req->result);
|
|
JS_FreeValueRT(rt, req->promise);
|
|
JS_FreeValueRT(rt, req->resolving_funcs[0]);
|
|
JS_FreeValueRT(rt, req->resolving_funcs[1]);
|
|
js_free_rt(rt, req);
|
|
}
|
|
if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
|
|
s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
|
|
async_func_free(rt, &s->func_state);
|
|
}
|
|
js_free_rt(rt, s);
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_generator_proto_funcs[] = {
|
|
JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
|
|
JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
|
|
JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
|
|
};
|
|
|
|
void JS_AddIntrinsicGenerator(JSContext *ctx)
|
|
{
|
|
JSValue obj1;
|
|
ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
|
|
js_generator_proto_funcs,
|
|
countof(js_generator_proto_funcs));
|
|
ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
|
|
obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
|
|
"GeneratorFunction", 1,
|
|
JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
|
|
JS_SetPropertyFunctionList(ctx,
|
|
ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
|
|
js_generator_function_proto_funcs,
|
|
countof(js_generator_function_proto_funcs));
|
|
JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
|
|
ctx->class_proto[JS_CLASS_GENERATOR],
|
|
JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
|
|
JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
|
|
0, JS_PROP_CONFIGURABLE);
|
|
JS_FreeValue(ctx, obj1);
|
|
}
|