fixed exception handling in AsyncFromSyncIterator and async for of

This commit is contained in:
Fabrice Bellard 2025-03-28 10:11:15 +01:00
parent 67de495254
commit 56c47f7d2a
3 changed files with 97 additions and 70 deletions

View File

@ -207,8 +207,9 @@ DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8)
DEF(for_await_of_next, 1, 3, 4, none) /* iter next catch_offset -> iter next catch_offset obj */
DEF(iterator_check_object, 1, 1, 1, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF(iterator_get_value_done, 1, 2, 3, none) /* catch_offset obj -> catch_offset value done */
DEF( iterator_close, 1, 3, 0, none)
DEF( iterator_next, 1, 4, 4, none)
DEF( iterator_call, 2, 4, 5, u8)

140
quickjs.c
View File

@ -15227,6 +15227,21 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
return 0;
}
static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp)
{
JSValue obj, iter, next;
sp[-1] = JS_UNDEFINED; /* disable the catch offset so that
exceptions do not close the iterator */
iter = sp[-3];
next = sp[-2];
obj = JS_Call(ctx, next, iter, 0, NULL);
if (JS_IsException(obj))
return -1;
sp[0] = obj;
return 0;
}
static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
BOOL *pdone)
{
@ -15259,6 +15274,9 @@ static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
if (JS_IsException(value))
return -1;
JS_FreeValue(ctx, obj);
/* put again the catch offset so that exceptions close the
iterator */
sp[-2] = JS_NewCatchOffset(ctx, 0);
sp[-1] = value;
sp[0] = JS_NewBool(ctx, done);
return 0;
@ -17214,6 +17232,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
sp += 2;
}
BREAK;
CASE(OP_for_await_of_next):
if (js_for_await_of_next(ctx, sp))
goto exception;
sp++;
BREAK;
CASE(OP_for_await_of_start):
if (js_for_of_start(ctx, sp, TRUE))
goto exception;
@ -26138,12 +26161,9 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
emit_label(s, label_cont);
if (is_for_of) {
if (is_async) {
/* call the next method */
/* stack: iter_obj next catch_offset */
emit_op(s, OP_dup3);
emit_op(s, OP_drop);
emit_op(s, OP_call_method);
emit_u16(s, 0);
/* call the next method */
emit_op(s, OP_for_await_of_next);
/* get the result of the promise */
emit_op(s, OP_await);
/* unwrap the value and done values */
@ -48426,25 +48446,6 @@ static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
};
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);
}
/* AsyncIteratorPrototype */
static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
@ -48506,6 +48507,41 @@ static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
return async_iter;
}
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_close_wrap(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv,
int magic, JSValue *func_data)
{
JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
JS_IteratorClose(ctx, func_data[0], TRUE);
return JS_EXCEPTION;
}
static JSValue js_async_from_sync_iterator_close_wrap_func_create(JSContext *ctx, JSValueConst sync_iter)
{
return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_close_wrap,
1, 0, 1, &sync_iter);
}
static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv,
int magic)
@ -48536,11 +48572,13 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
if (magic == GEN_MAGIC_RETURN) {
err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
is_reject = 0;
goto done_resolve;
} else {
err = JS_DupValue(ctx, argv[0]);
is_reject = 1;
if (JS_IteratorClose(ctx, s->sync_iter, FALSE))
goto reject;
JS_ThrowTypeError(ctx, "throw is not a method");
goto reject;
}
goto done_resolve;
}
}
value = JS_IteratorNext2(ctx, s->sync_iter, method,
@ -48555,21 +48593,9 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
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;
}
if (JS_IsException(value))
goto reject;
{
JSValue value_wrapper_promise, resolve_reject[2];
int res;
@ -48577,8 +48603,22 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
1, (JSValueConst *)&value, 0);
if (JS_IsException(value_wrapper_promise)) {
JSValue res2;
JS_FreeValue(ctx, value);
goto reject;
if (magic != GEN_MAGIC_RETURN && !done) {
JS_IteratorClose(ctx, s->sync_iter, TRUE);
}
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;
}
resolve_reject[0] =
@ -48587,13 +48627,23 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
JS_FreeValue(ctx, value_wrapper_promise);
goto fail;
}
if (done || magic == GEN_MAGIC_RETURN) {
resolve_reject[1] = JS_UNDEFINED;
} else {
resolve_reject[1] =
js_async_from_sync_iterator_close_wrap_func_create(ctx, s->sync_iter);
if (JS_IsException(resolve_reject[1])) {
JS_FreeValue(ctx, value_wrapper_promise);
JS_FreeValue(ctx, resolve_reject[0]);
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, resolve_reject[1]);
JS_FreeValue(ctx, value_wrapper_promise);
JS_FreeValue(ctx, resolving_funcs[0]);
JS_FreeValue(ctx, resolving_funcs[1]);

View File

@ -1,30 +1,6 @@
test262/test/annexB/language/comments/single-line-html-close-first-line-1.js:1: unexpected error type: SyntaxError: unexpected token in expression: '>'
test262/test/annexB/language/comments/single-line-html-close-first-line-2.js:1: unexpected error type: SyntaxError: unexpected token in expression: '>'
test262/test/annexB/language/comments/single-line-html-close-first-line-3.js:1: unexpected error type: SyntaxError: unexpected token in expression: '>'
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/iterator-result-poisoned-wrapper.js:64: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/iterator-result-poisoned-wrapper.js:64: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/next-result-poisoned-wrapper.js:69: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/next-result-poisoned-wrapper.js:69: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/yield-iterator-next-rejected-promise-close.js:59: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/yield-iterator-next-rejected-promise-close.js:59: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/yield-next-rejected-promise-close.js:64: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/next/yield-next-rejected-promise-close.js:64: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/iterator-result-rejected-promise-close.js:74: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/iterator-result-rejected-promise-close.js:74: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-null.js:52: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-null.js:52: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-result-poisoned-wrapper.js:81: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-result-poisoned-wrapper.js:81: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-get-return-undefined.js:64: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-get-return-undefined.js:64: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-poisoned-return.js:68: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-poisoned-return.js:68: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-return-not-object.js:72: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-return-not-object.js:72: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-return-object.js:66: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined-return-object.js:66: strict mode: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined.js:41: TypeError: $DONE() not called
test262/test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-undefined.js:41: strict mode: TypeError: $DONE() not called
test262/test/built-ins/Function/prototype/arguments/prop-desc.js:31: Test262Error: Function.prototype.arguments property getter/setter are the same function Expected SameValue(«function () {
[native code]
}», «function () {