Reland "[async] Optimize await and AsyncFromSyncIterator"

This is a reland of 21c0d77e15

Original change's description:
> [async] Optimize await and AsyncFromSyncIterator
> 
> Simplify the promise wrapping in await and
> %AsyncFromSyncIteratorPrototype%.next/return/throw to reuse the PromiseResolve
> primitive. Now await takes 1 tick instead of 3 on the microtask queue.
> 
> Change-Id: I7e99b8689eb8fcb09c48915b11c1e06684dc0f1a
> Reviewed-on: https://chromium-review.googlesource.com/1090272
> Commit-Queue: Maya Lekova <mslekova@chromium.org>
> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
> Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Reviewed-by: Mathias Bynens <mathias@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#53853}

Cq-Include-Trybots: luci.chromium.try:linux_chromium_headless_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ifa5b2fb8b2fb84b635b2dc1b6455d6aaf154cbfd
Reviewed-on: https://chromium-review.googlesource.com/1106977
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53900}
This commit is contained in:
Maya Lekova 2018-06-20 14:12:53 -07:00 committed by Commit Bot
parent 5e57f660ae
commit ef8c18613a
14 changed files with 1104 additions and 8 deletions

View File

@ -4543,6 +4543,57 @@ void Genesis::InitializeGlobal_harmony_bigint() {
Builtins::kDataViewPrototypeSetBigUint64, 2, false);
}
void Genesis::InitializeGlobal_harmony_await_optimization() {
if (!FLAG_harmony_await_optimization) return;
// async/await
Handle<JSFunction> await_caught_function = SimpleCreateFunction(
isolate(), factory()->empty_string(),
Builtins::kAsyncFunctionAwaitCaughtOptimized, 2, false);
native_context()->set_async_function_await_caught(*await_caught_function);
Handle<JSFunction> await_uncaught_function = SimpleCreateFunction(
isolate(), factory()->empty_string(),
Builtins::kAsyncFunctionAwaitUncaughtOptimized, 2, false);
native_context()->set_async_function_await_uncaught(*await_uncaught_function);
// async generators
Handle<JSObject> async_iterator_prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
SimpleInstallFunction(
isolate(), async_iterator_prototype, factory()->async_iterator_symbol(),
"[Symbol.asyncIterator]", Builtins::kReturnReceiver, 0, true);
Handle<JSObject> async_from_sync_iterator_prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
SimpleInstallFunction(
isolate(), async_from_sync_iterator_prototype, factory()->next_string(),
Builtins::kAsyncFromSyncIteratorPrototypeNextOptimized, 1, true);
SimpleInstallFunction(
isolate(), async_from_sync_iterator_prototype, factory()->return_string(),
Builtins::kAsyncFromSyncIteratorPrototypeReturnOptimized, 1, true);
SimpleInstallFunction(
isolate(), async_from_sync_iterator_prototype, factory()->throw_string(),
Builtins::kAsyncFromSyncIteratorPrototypeThrowOptimized, 1, true);
JSObject::AddProperty(
isolate(), async_from_sync_iterator_prototype,
factory()->to_string_tag_symbol(),
factory()->NewStringFromAsciiChecked("Async-from-Sync Iterator"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
JSObject::ForceSetPrototype(async_from_sync_iterator_prototype,
async_iterator_prototype);
Handle<Map> async_from_sync_iterator_map = factory()->NewMap(
JS_ASYNC_FROM_SYNC_ITERATOR_TYPE, JSAsyncFromSyncIterator::kSize);
Map::SetPrototype(async_from_sync_iterator_map,
async_from_sync_iterator_prototype);
native_context()->set_async_from_sync_iterator_map(
*async_from_sync_iterator_map);
}
#ifdef V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_locale() {

View File

@ -20,6 +20,10 @@ class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
void AsyncFunctionAwait(Node* const context, Node* const generator,
Node* const awaited, Node* const outer_promise,
const bool is_predicted_as_caught);
void AsyncFunctionAwaitOptimized(Node* const context, Node* const generator,
Node* const awaited,
Node* const outer_promise,
const bool is_predicted_as_caught);
void AsyncFunctionAwaitResumeClosure(
Node* const context, Node* const sent_value,
@ -139,6 +143,43 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
Goto(&after_debug_hook);
}
void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitOptimized(
Node* const context, Node* const generator, Node* const awaited,
Node* const outer_promise, const bool is_predicted_as_caught) {
CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE));
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
ContextInitializer init_closure_context = [&](Node* context) {
StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
generator);
};
// TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse
// the awaited promise if it is already a promise. Reuse is non-spec compliant
// but part of our old behavior gives us a couple of percent
// performance boost.
// TODO(jgruber): Use a faster specialized version of
// InternalPerformPromiseThen.
Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
GotoIf(HasAsyncEventDelegate(), &call_debug_hook);
Goto(&after_debug_hook);
BIND(&after_debug_hook);
AwaitOptimized(
context, generator, awaited, outer_promise, AwaitContext::kLength,
init_closure_context, Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught);
// Return outer promise to avoid adding an load of the outer promise before
// suspending in BytecodeGenerator.
Return(outer_promise);
BIND(&call_debug_hook);
CallRuntime(Runtime::kDebugAsyncFunctionSuspended, context, outer_promise);
Goto(&after_debug_hook);
}
// Called by the parser from the desugaring of 'await' when catch
// prediction indicates that there is a locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
@ -154,6 +195,19 @@ TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
kIsPredictedAsCaught);
}
TF_BUILTIN(AsyncFunctionAwaitCaughtOptimized, AsyncFunctionBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 3);
Node* const generator = Parameter(Descriptor::kGenerator);
Node* const awaited = Parameter(Descriptor::kAwaited);
Node* const outer_promise = Parameter(Descriptor::kOuterPromise);
Node* const context = Parameter(Descriptor::kContext);
static const bool kIsPredictedAsCaught = true;
AsyncFunctionAwaitOptimized(context, generator, awaited, outer_promise,
kIsPredictedAsCaught);
}
// Called by the parser from the desugaring of 'await' when catch
// prediction indicates no locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
@ -169,6 +223,20 @@ TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
kIsPredictedAsCaught);
}
TF_BUILTIN(AsyncFunctionAwaitUncaughtOptimized,
AsyncFunctionBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 3);
Node* const generator = Parameter(Descriptor::kGenerator);
Node* const awaited = Parameter(Descriptor::kAwaited);
Node* const outer_promise = Parameter(Descriptor::kOuterPromise);
Node* const context = Parameter(Descriptor::kContext);
static const bool kIsPredictedAsCaught = false;
AsyncFunctionAwaitOptimized(context, generator, awaited, outer_promise,
kIsPredictedAsCaught);
}
TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 0);
Node* const context = Parameter(Descriptor::kContext);

View File

@ -147,6 +147,120 @@ Node* AsyncBuiltinsAssembler::Await(
on_resolve, on_reject, throwaway);
}
Node* AsyncBuiltinsAssembler::AwaitOptimized(
Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length, const ContextInitializer& init_closure_context,
Node* on_resolve_context_index, Node* on_reject_context_index,
Node* is_predicted_as_caught) {
DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
CSA_ASSERT(this, IsConstructor(promise_fun));
static const int kThrowawayPromiseOffset =
FixedArray::SizeFor(context_length);
static const int kResolveClosureOffset =
kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
static const int kRejectClosureOffset =
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
static const int kTotalSize =
kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
// 2. Let promise be ? PromiseResolve(« promise »).
Node* const promise =
CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
Node* const base = AllocateInNewSpace(kTotalSize);
Node* const closure_context = base;
{
// Initialize closure context
InitializeFunctionContext(native_context, closure_context, context_length);
init_closure_context(closure_context);
}
Node* const promise_map =
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
// Assert that the JSPromise map has an instance size is
// JSPromise::kSizeWithEmbedderFields.
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
kPointerSize)));
Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
{
// Initialize throwawayPromise
StoreMapNoWriteBarrier(throwaway, promise_map);
InitializeJSObjectFromMap(
throwaway, promise_map,
IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
PromiseInit(throwaway);
}
Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
{
// Initialize resolve handler
InitializeNativeClosure(closure_context, native_context, on_resolve,
on_resolve_context_index);
}
Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
{
// Initialize reject handler
InitializeNativeClosure(closure_context, native_context, on_reject,
on_reject_context_index);
}
{
// Add PromiseHooks if needed
Label next(this);
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise,
throwaway);
Goto(&next);
BIND(&next);
}
// The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done
PromiseSetHasHandler(throwaway);
Label do_perform_promise_then(this);
GotoIfNot(IsDebugActive(), &do_perform_promise_then);
{
Label common(this);
GotoIf(TaggedIsSmi(value), &common);
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
{
// Mark the reject handler callback to be a forwarding edge, rather
// than a meaningful catch handler
Node* const key =
HeapConstant(factory()->promise_forwarding_handler_symbol());
CallRuntime(Runtime::kSetProperty, context, on_reject, key,
TrueConstant(), SmiConstant(LanguageMode::kStrict));
GotoIf(IsFalse(is_predicted_as_caught), &common);
PromiseSetHandledHint(value);
}
Goto(&common);
BIND(&common);
// Mark the dependency to outer Promise in case the throwaway Promise is
// found on the Promise stack
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise,
SmiConstant(LanguageMode::kStrict));
}
Goto(&do_perform_promise_then);
BIND(&do_perform_promise_then);
return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
on_resolve, on_reject, throwaway);
}
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
Node* native_context,
Node* function,

View File

@ -28,6 +28,12 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
const ContextInitializer& init_closure_context,
Node* on_resolve_context_index, Node* on_reject_context_index,
Node* is_predicted_as_caught);
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
Node* outer_promise, int context_length,
const ContextInitializer& init_closure_context,
Node* on_resolve_context_index,
Node* on_reject_context_index,
Node* is_predicted_as_caught);
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
@ -38,6 +44,17 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
IntPtrConstant(on_reject_context_index),
is_predicted_as_caught);
}
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
Node* outer_promise, int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index,
int on_reject_context_index,
Node* is_predicted_as_caught) {
return AwaitOptimized(
context, generator, value, outer_promise, context_length,
init_closure_context, IntPtrConstant(on_resolve_context_index),
IntPtrConstant(on_reject_context_index), is_predicted_as_caught);
}
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
int context_length,
const ContextInitializer& init_closure_context,
@ -48,6 +65,17 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
on_reject_context_index,
BooleanConstant(is_predicted_as_caught));
}
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
Node* outer_promise, int context_length,
const ContextInitializer& init_closure_context,
int on_resolve_context_index,
int on_reject_context_index,
bool is_predicted_as_caught) {
return AwaitOptimized(context, generator, value, outer_promise,
context_length, init_closure_context,
on_resolve_context_index, on_reject_context_index,
BooleanConstant(is_predicted_as_caught));
}
// Return a new built-in function object as defined in
// Async Iterator Value Unwrap Functions

View File

@ -36,6 +36,13 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
Node* const initial_exception_value = nullptr);
void Generate_AsyncFromSyncIteratorMethodOptimized(
Node* const context, Node* const iterator, Node* const sent_value,
const SyncIteratorNodeGenerator& get_method,
const UndefinedMethodHandler& if_method_undefined,
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
Node* const initial_exception_value = nullptr);
void Generate_AsyncFromSyncIteratorMethod(
Node* const context, Node* const iterator, Node* const sent_value,
@ -50,6 +57,19 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
context, iterator, sent_value, get_method, if_method_undefined,
operation_name, reject_label_type, initial_exception_value);
}
void Generate_AsyncFromSyncIteratorMethodOptimized(
Node* const context, Node* const iterator, Node* const sent_value,
Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
Node* const initial_exception_value = nullptr) {
auto get_method = [=](Node* const sync_iterator) {
return GetProperty(context, sync_iterator, name);
};
return Generate_AsyncFromSyncIteratorMethodOptimized(
context, iterator, sent_value, get_method, if_method_undefined,
operation_name, reject_label_type, initial_exception_value);
}
// Load "value" and "done" from an iterator result object. If an exception
// is thrown at any point, jumps to te `if_exception` label with exception
@ -157,6 +177,73 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
}
}
void AsyncFromSyncBuiltinsAssembler::
Generate_AsyncFromSyncIteratorMethodOptimized(
Node* const context, Node* const iterator, Node* const sent_value,
const SyncIteratorNodeGenerator& get_method,
const UndefinedMethodHandler& if_method_undefined,
const char* operation_name, Label::Type reject_label_type,
Node* const initial_exception_value) {
Node* const native_context = LoadNativeContext(context);
Node* const promise = AllocateAndInitJSPromise(context);
VARIABLE(var_exception, MachineRepresentation::kTagged,
initial_exception_value == nullptr ? UndefinedConstant()
: initial_exception_value);
Label reject_promise(this, reject_label_type);
ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
&var_exception, operation_name);
Node* const sync_iterator =
LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
Node* const method = get_method(sync_iterator);
if (if_method_undefined) {
Label if_isnotundefined(this);
GotoIfNot(IsUndefined(method), &if_isnotundefined);
if_method_undefined(native_context, promise, &reject_promise);
BIND(&if_isnotundefined);
}
Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
method, sync_iterator, sent_value);
GotoIfException(iter_result, &reject_promise, &var_exception);
Node* value;
Node* done;
std::tie(value, done) = LoadIteratorResult(
context, native_context, iter_result, &reject_promise, &var_exception);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
CSA_ASSERT(this, IsConstructor(promise_fun));
// Let valueWrapper be ? PromiseResolve(« value »).
Node* const valueWrapper = CallBuiltin(Builtins::kPromiseResolve,
native_context, promise_fun, value);
// Let onFulfilled be a new built-in function object as defined in
// Async Iterator Value Unwrap Functions.
// Set onFulfilled.[[Done]] to throwDone.
Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
// Perform ! PerformPromiseThen(valueWrapper,
// onFulfilled, undefined, promiseCapability).
Return(CallBuiltin(Builtins::kPerformPromiseThen, context, valueWrapper,
on_fulfilled, UndefinedConstant(), promise));
BIND(&reject_promise);
{
Node* const exception = var_exception.value();
CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
TrueConstant());
Return(promise);
}
}
std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
Node* const context, Node* const native_context, Node* const iter_result,
Label* if_exception, Variable* var_exception) {
@ -246,6 +333,20 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
"[Async-from-Sync Iterator].prototype.next");
}
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNextOptimized,
AsyncFromSyncBuiltinsAssembler) {
Node* const iterator = Parameter(Descriptor::kReceiver);
Node* const value = Parameter(Descriptor::kValue);
Node* const context = Parameter(Descriptor::kContext);
auto get_method = [=](Node* const unused) {
return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
};
Generate_AsyncFromSyncIteratorMethodOptimized(
context, iterator, value, get_method, UndefinedMethodHandler(),
"[Async-from-Sync Iterator].prototype.next");
}
// https://tc39.github.io/proposal-async-iteration/
// Section #sec-%asyncfromsynciteratorprototype%.return
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
@ -273,6 +374,31 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
"[Async-from-Sync Iterator].prototype.return");
}
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturnOptimized,
AsyncFromSyncBuiltinsAssembler) {
Node* const iterator = Parameter(Descriptor::kReceiver);
Node* const value = Parameter(Descriptor::kValue);
Node* const context = Parameter(Descriptor::kContext);
auto if_return_undefined = [=](Node* const native_context,
Node* const promise, Label* if_exception) {
// If return is undefined, then
// Let iterResult be ! CreateIterResultObject(value, true)
Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
context, value, TrueConstant());
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
// IfAbruptRejectPromise(nextDone, promiseCapability).
// Return promiseCapability.[[Promise]].
CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
Return(promise);
};
Generate_AsyncFromSyncIteratorMethodOptimized(
context, iterator, value, factory()->return_string(), if_return_undefined,
"[Async-from-Sync Iterator].prototype.return");
}
// https://tc39.github.io/proposal-async-iteration/
// Section #sec-%asyncfromsynciteratorprototype%.throw
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
@ -290,5 +416,20 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
reason);
}
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrowOptimized,
AsyncFromSyncBuiltinsAssembler) {
Node* const iterator = Parameter(Descriptor::kReceiver);
Node* const reason = Parameter(Descriptor::kReason);
Node* const context = Parameter(Descriptor::kContext);
auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
Label* if_exception) { Goto(if_exception); };
Generate_AsyncFromSyncIteratorMethodOptimized(
context, iterator, reason, factory()->throw_string(), if_throw_undefined,
"[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
reason);
}
} // namespace internal
} // namespace v8

View File

@ -420,8 +420,12 @@ namespace internal {
/* AsyncFunction */ \
TFJ(AsyncFunctionAwaitCaught, 3, kReceiver, kGenerator, kAwaited, \
kOuterPromise) \
TFJ(AsyncFunctionAwaitCaughtOptimized, 3, kReceiver, kGenerator, kAwaited, \
kOuterPromise) \
TFJ(AsyncFunctionAwaitUncaught, 3, kReceiver, kGenerator, kAwaited, \
kOuterPromise) \
TFJ(AsyncFunctionAwaitUncaughtOptimized, 3, kReceiver, kGenerator, kAwaited, \
kOuterPromise) \
TFJ(AsyncFunctionAwaitRejectClosure, 1, kReceiver, kSentError) \
TFJ(AsyncFunctionAwaitResolveClosure, 1, kReceiver, kSentValue) \
TFJ(AsyncFunctionPromiseCreate, 0, kReceiver) \
@ -1271,10 +1275,13 @@ namespace internal {
/* See tc39.github.io/proposal-async-iteration/ */ \
/* #sec-%asyncfromsynciteratorprototype%-object) */ \
TFJ(AsyncFromSyncIteratorPrototypeNext, 1, kReceiver, kValue) \
TFJ(AsyncFromSyncIteratorPrototypeNextOptimized, 1, kReceiver, kValue) \
/* #sec-%asyncfromsynciteratorprototype%.throw */ \
TFJ(AsyncFromSyncIteratorPrototypeThrow, 1, kReceiver, kReason) \
TFJ(AsyncFromSyncIteratorPrototypeThrowOptimized, 1, kReceiver, kReason) \
/* #sec-%asyncfromsynciteratorprototype%.return */ \
TFJ(AsyncFromSyncIteratorPrototypeReturn, 1, kReceiver, kValue) \
TFJ(AsyncFromSyncIteratorPrototypeReturnOptimized, 1, kReceiver, kValue) \
/* #sec-async-iterator-value-unwrap-functions */ \
TFJ(AsyncIteratorValueUnwrap, 1, kReceiver, kValue) \
\
@ -1355,9 +1362,14 @@ namespace internal {
#define BUILTIN_PROMISE_REJECTION_PREDICTION_LIST(V) \
V(AsyncFromSyncIteratorPrototypeNext) \
V(AsyncFromSyncIteratorPrototypeReturn) \
V(AsyncFromSyncIteratorPrototypeNextOptimized) \
V(AsyncFromSyncIteratorPrototypeThrowOptimized) \
V(AsyncFromSyncIteratorPrototypeReturnOptimized) \
V(AsyncFromSyncIteratorPrototypeThrow) \
V(AsyncFunctionAwaitCaught) \
V(AsyncFunctionAwaitCaughtOptimized) \
V(AsyncFunctionAwaitUncaught) \
V(AsyncFunctionAwaitUncaughtOptimized) \
V(AsyncGeneratorResolve) \
V(AsyncGeneratorAwaitCaught) \
V(AsyncGeneratorAwaitUncaught) \

View File

@ -208,10 +208,11 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
// Update bootstrapper.cc whenever adding a new feature flag.
// Features that are still work in progress (behind individual flags).
#define HARMONY_INPROGRESS_BASE(V) \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_static_fields, "harmony static fields in class literals")
#define HARMONY_INPROGRESS_BASE(V) \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_static_fields, "harmony static fields in class literals") \
V(harmony_await_optimization, "harmony await taking 1 tick")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \

View File

@ -207,4 +207,3 @@ floodWithTimeouts (testStepIntoAtReturn.js:137:10)
floodWithTimeouts (testStepIntoAtReturn.js:137:10)
test (testStepIntoAtReturn.js:142:8)
(anonymous) (:0:0)

View File

@ -0,0 +1,215 @@
stepOut async function
Running test: testTrivial
Check that we have proper async stack at return
bar (testTrivial.js:30:8)
-- async function --
bar (testTrivial.js:29:22)
foo (testTrivial.js:25:14)
-- async function --
foo (testTrivial.js:24:22)
test (testTrivial.js:20:14)
-- async function --
test (testTrivial.js:19:22)
(anonymous) (:0:0)
foo (testTrivial.js:26:6)
-- async function --
foo (testTrivial.js:24:22)
test (testTrivial.js:20:14)
-- async function --
test (testTrivial.js:19:22)
(anonymous) (:0:0)
test (testTrivial.js:21:6)
-- async function --
test (testTrivial.js:19:22)
(anonymous) (:0:0)
Running test: testStepOutPrecision
Check that stepOut go to resumed outer generator
bar (testStepOutPrecision.js:63:8)
-- async function --
bar (testStepOutPrecision.js:62:22)
foo (testStepOutPrecision.js:57:14)
-- async function --
foo (testStepOutPrecision.js:56:22)
test (testStepOutPrecision.js:50:14)
-- async function --
test (testStepOutPrecision.js:49:14)
(anonymous) (:0:0)
foo (testStepOutPrecision.js:58:8)
-- async function --
foo (testStepOutPrecision.js:56:22)
test (testStepOutPrecision.js:50:14)
-- async function --
test (testStepOutPrecision.js:49:14)
(anonymous) (:0:0)
test (testStepOutPrecision.js:51:8)
-- async function --
test (testStepOutPrecision.js:49:14)
(anonymous) (:0:0)
floodWithTimeouts (testStepOutPrecision.js:42:15)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
test (testStepOutPrecision.js:48:8)
(anonymous) (:0:0)
test (testStepOutPrecision.js:52:8)
-- async function --
test (testStepOutPrecision.js:49:14)
(anonymous) (:0:0)
floodWithTimeouts (testStepOutPrecision.js:42:15)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
-- setTimeout --
floodWithTimeouts (testStepOutPrecision.js:43:10)
test (testStepOutPrecision.js:48:8)
(anonymous) (:0:0)
Running test: testStepIntoAtReturn
Check that stepInto at return go to resumed outer generator
bar (testStepIntoAtReturn.js:95:8)
-- async function --
bar (testStepIntoAtReturn.js:94:22)
foo (testStepIntoAtReturn.js:90:14)
-- async function --
foo (testStepIntoAtReturn.js:89:22)
test (testStepIntoAtReturn.js:84:14)
-- async function --
test (testStepIntoAtReturn.js:83:14)
(anonymous) (:0:0)
bar (testStepIntoAtReturn.js:96:6)
-- async function --
bar (testStepIntoAtReturn.js:94:22)
foo (testStepIntoAtReturn.js:90:14)
-- async function --
foo (testStepIntoAtReturn.js:89:22)
test (testStepIntoAtReturn.js:84:14)
-- async function --
test (testStepIntoAtReturn.js:83:14)
(anonymous) (:0:0)
foo (testStepIntoAtReturn.js:91:6)
-- async function --
foo (testStepIntoAtReturn.js:89:22)
test (testStepIntoAtReturn.js:84:14)
-- async function --
test (testStepIntoAtReturn.js:83:14)
(anonymous) (:0:0)
test (testStepIntoAtReturn.js:85:8)
-- async function --
test (testStepIntoAtReturn.js:83:14)
(anonymous) (:0:0)
test (testStepIntoAtReturn.js:86:6)
-- async function --
test (testStepIntoAtReturn.js:83:14)
(anonymous) (:0:0)
floodWithTimeouts (testStepIntoAtReturn.js:76:15)
-- setTimeout --
floodWithTimeouts (testStepIntoAtReturn.js:77:10)
-- setTimeout --
floodWithTimeouts (testStepIntoAtReturn.js:77:10)
test (testStepIntoAtReturn.js:82:8)
(anonymous) (:0:0)
Running test: testStepOverAtReturn
Check that stepOver at return go to resumed outer generator
bar (testStepIntoAtReturn.js:126:8)
-- async function --
bar (testStepIntoAtReturn.js:125:22)
foo (testStepIntoAtReturn.js:121:14)
-- async function --
foo (testStepIntoAtReturn.js:120:22)
test (testStepIntoAtReturn.js:115:14)
-- async function --
test (testStepIntoAtReturn.js:114:14)
(anonymous) (:0:0)
bar (testStepIntoAtReturn.js:127:6)
-- async function --
bar (testStepIntoAtReturn.js:125:22)
foo (testStepIntoAtReturn.js:121:14)
-- async function --
foo (testStepIntoAtReturn.js:120:22)
test (testStepIntoAtReturn.js:115:14)
-- async function --
test (testStepIntoAtReturn.js:114:14)
(anonymous) (:0:0)
foo (testStepIntoAtReturn.js:122:6)
-- async function --
foo (testStepIntoAtReturn.js:120:22)
test (testStepIntoAtReturn.js:115:14)
-- async function --
test (testStepIntoAtReturn.js:114:14)
(anonymous) (:0:0)
test (testStepIntoAtReturn.js:116:8)
-- async function --
test (testStepIntoAtReturn.js:114:14)
(anonymous) (:0:0)
test (testStepIntoAtReturn.js:117:6)
-- async function --
test (testStepIntoAtReturn.js:114:14)
(anonymous) (:0:0)
floodWithTimeouts (testStepIntoAtReturn.js:107:15)
-- setTimeout --
floodWithTimeouts (testStepIntoAtReturn.js:108:10)
-- setTimeout --
floodWithTimeouts (testStepIntoAtReturn.js:108:10)
test (testStepIntoAtReturn.js:113:8)
(anonymous) (:0:0)
Running test: testStepOutFromNotAwaitedCall
Checks stepOut from not awaited call
bar (testStepIntoAtReturn.js:160:8)
-- async function --
bar (testStepIntoAtReturn.js:159:22)
foo (testStepIntoAtReturn.js:154:8)
-- async function --
foo (testStepIntoAtReturn.js:153:22)
test (testStepIntoAtReturn.js:146:14)
-- async function --
test (testStepIntoAtReturn.js:145:14)
(anonymous) (:0:0)
test (testStepIntoAtReturn.js:147:8)
-- async function --
test (testStepIntoAtReturn.js:145:14)
(anonymous) (:0:0)
floodWithTimeouts (testStepIntoAtReturn.js:138:15)
-- setTimeout --
floodWithTimeouts (testStepIntoAtReturn.js:139:10)
-- setTimeout --
floodWithTimeouts (testStepIntoAtReturn.js:139:10)
test (testStepIntoAtReturn.js:144:8)
(anonymous) (:0:0)

View File

@ -0,0 +1,187 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-await-optimization
let {session, contextGroup, Protocol} =
InspectorTest.start('stepOut async function');
session.setupScriptMap();
Protocol.Runtime.enable();
InspectorTest.runAsyncTestSuite([
async function testTrivial() {
InspectorTest.log('Check that we have proper async stack at return');
contextGroup.addInlineScript(`
async function test() {
await Promise.resolve();
await foo();
}
async function foo() {
await Promise.resolve();
await bar();
}
async function bar() {
await Promise.resolve();
debugger;
}`, 'testTrivial.js');
await runTestAndStepAction('stepOut');
},
async function testStepOutPrecision() {
InspectorTest.log('Check that stepOut go to resumed outer generator');
contextGroup.addInlineScript(`
function wait() {
return new Promise(resolve => setTimeout(resolve, 0));
}
function floodWithTimeouts(a) {
if (!a.stop)
setTimeout(floodWithTimeouts.bind(this, a), 0);
}
async function test() {
let a = {};
floodWithTimeouts(a)
await wait();
await foo();
await wait();
a.stop = true;
}
async function foo() {
await Promise.resolve();
await bar();
await wait();
}
async function bar() {
await Promise.resolve();
debugger;
await wait();
}`, 'testStepOutPrecision.js');
await runTestAndStepAction('stepOut');
},
async function testStepIntoAtReturn() {
InspectorTest.log('Check that stepInto at return go to resumed outer generator');
contextGroup.addInlineScript(`
function wait() {
return new Promise(resolve => setTimeout(resolve, 0));
}
function floodWithTimeouts(a) {
if (!a.stop)
setTimeout(floodWithTimeouts.bind(this, a), 0);
}
async function test() {
let a = {};
floodWithTimeouts(a)
await wait();
await foo();
a.stop = true;
}
async function foo() {
await Promise.resolve();
await bar();
}
async function bar() {
await Promise.resolve();
debugger;
}`, 'testStepIntoAtReturn.js');
await runTestAndStepAction('stepInto');
},
async function testStepOverAtReturn() {
InspectorTest.log('Check that stepOver at return go to resumed outer generator');
contextGroup.addInlineScript(`
function wait() {
return new Promise(resolve => setTimeout(resolve, 0));
}
function floodWithTimeouts(a) {
if (!a.stop)
setTimeout(floodWithTimeouts.bind(this, a), 0);
}
async function test() {
let a = {};
floodWithTimeouts(a)
await wait();
await foo();
a.stop = true;
}
async function foo() {
await Promise.resolve();
await bar();
}
async function bar() {
await Promise.resolve();
debugger;
}`, 'testStepIntoAtReturn.js');
await runTestAndStepAction('stepOver');
},
async function testStepOutFromNotAwaitedCall() {
InspectorTest.log('Checks stepOut from not awaited call');
contextGroup.addInlineScript(`
function wait() {
return new Promise(resolve => setTimeout(resolve, 0));
}
function floodWithTimeouts(a) {
if (!a.stop)
setTimeout(floodWithTimeouts.bind(this, a), 0);
}
async function test() {
let a = {};
floodWithTimeouts(a)
await wait();
await foo();
a.stop = true;
}
async function foo() {
let a = {};
floodWithTimeouts(a);
await Promise.resolve();
bar();
a.stop = true;
}
async function bar() {
await Promise.resolve();
debugger;
}`, 'testStepIntoAtReturn.js');
await runTestAndStepAction('stepOut');
}
]);
async function runTestAndStepAction(action) {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
let finished =
Protocol.Runtime.evaluate({expression: 'test()', awaitPromise: true})
.then(() => false);
while (true) {
const r = await Promise.race([finished, waitPauseAndDumpStack()]);
if (!r) break;
Protocol.Debugger[action]();
}
await Protocol.Debugger.disable();
}
async function waitPauseAndDumpStack() {
const {params} = await Protocol.Debugger.oncePaused();
session.logCallFrames(params.callFrames);
session.logAsyncStackTrace(params.asyncStackTrace);
InspectorTest.log('');
return true;
}

View File

@ -0,0 +1,200 @@
Tests breakable locations in variable initializations.
Running test: testBreakLocations
Running test: testStepInto
function testFunction() {
var obj1 = |_|{a : 1};
var arr1 = |_|[1];
var promise = |_|Promise.|C|resolve(1).|C|then(x => x |_|* 2|R|).|C|then(x => x |_|/ 2|R|);
|_|Promise.|C|resolve(1).|C|then(x => x |_|* 2|R|).|C|then(x => x |_|/ 2|R|);
|_|promise = Promise.|C|resolve(1).|C|then(x => x |_|* 2|R|).|C|then(x => x |_|/ 2|R|);
var a = |_|1;
const x = |_|(a = 20);
var y = |_|(a = 100);
var z = |_|x + (a = 1) + (a = 2) + (a = 3) + |C|f();
function f() {
for (let { x, y } = |_|{ x: 0, y: 1 }; y |_|> 0; --|_|y) { let z = |_|x + y; }
|R|}
var b = obj1.|_|a;
|_|(async function asyncF() {
let r = |_|await Promise.|C|resolve(42);
|_|return r;
|R|})|C|();
|_|return promise;|R|
}
(anonymous) (expr.js:0:0)
testFunction (test.js:2:13)
(anonymous) (expr.js:0:0)
function testFunction() {
var obj1 = #{a : 1};
var arr1 = [1];
testFunction (test.js:3:13)
(anonymous) (expr.js:0:0)
var obj1 = {a : 1};
var arr1 = #[1];
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
testFunction (test.js:4:16)
(anonymous) (expr.js:0:0)
var arr1 = [1];
var promise = #Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
testFunction (test.js:5:2)
(anonymous) (expr.js:0:0)
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
#Promise.resolve(1).then(x => x * 2).then(x => x / 2);
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
testFunction (test.js:6:2)
(anonymous) (expr.js:0:0)
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
#promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
var a = 1;
testFunction (test.js:7:10)
(anonymous) (expr.js:0:0)
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
var a = #1;
const x = (a = 20);
testFunction (test.js:8:12)
(anonymous) (expr.js:0:0)
var a = 1;
const x = #(a = 20);
var y = (a = 100);
testFunction (test.js:9:10)
(anonymous) (expr.js:0:0)
const x = (a = 20);
var y = #(a = 100);
var z = x + (a = 1) + (a = 2) + (a = 3) + f();
testFunction (test.js:10:10)
(anonymous) (expr.js:0:0)
var y = (a = 100);
var z = #x + (a = 1) + (a = 2) + (a = 3) + f();
function f() {
f (test.js:12:24)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
function f() {
for (let { x, y } = #{ x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
}
f (test.js:12:42)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
function f() {
for (let { x, y } = { x: 0, y: 1 }; y #> 0; --y) { let z = x + y; }
}
f (test.js:12:62)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
function f() {
for (let { x, y } = { x: 0, y: 1 }; y > 0; --y) { let z = #x + y; }
}
f (test.js:12:49)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
function f() {
for (let { x, y } = { x: 0, y: 1 }; y > 0; --#y) { let z = x + y; }
}
f (test.js:12:42)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
function f() {
for (let { x, y } = { x: 0, y: 1 }; y #> 0; --y) { let z = x + y; }
}
f (test.js:13:2)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
for (let { x, y } = { x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
#}
var b = obj1.a;
testFunction (test.js:14:15)
(anonymous) (expr.js:0:0)
}
var b = obj1.#a;
(async function asyncF() {
testFunction (test.js:15:2)
(anonymous) (expr.js:0:0)
var b = obj1.a;
#(async function asyncF() {
let r = await Promise.resolve(42);
asyncF (test.js:16:12)
testFunction (test.js:18:4)
(anonymous) (expr.js:0:0)
(async function asyncF() {
let r = #await Promise.resolve(42);
return r;
asyncF (test.js:17:4)
let r = await Promise.resolve(42);
#return r;
})();
asyncF (test.js:18:2)
return r;
#})();
return promise;
Promise.resolve.then.then.x (test.js:4:64)
var arr1 = [1];
var promise = Promise.resolve(1).then(x => x * 2).then(x => x #/ 2);
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve.then.then.x (test.js:4:67)
var arr1 = [1];
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2#);
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve.then.then.x (test.js:5:50)
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve(1).then(x => x * 2).then(x => x #/ 2);
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve.then.then.x (test.js:5:53)
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve(1).then(x => x * 2).then(x => x / 2#);
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve.then.then.x (test.js:6:60)
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
promise = Promise.resolve(1).then(x => x * 2).then(x => x #/ 2);
var a = 1;
Promise.resolve.then.then.x (test.js:6:63)
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2#);
var a = 1;
Running test: testStepIntoAfterBreakpoint
testFunction (test.js:10:10)
(anonymous) (expr.js:0:0)
var y = (a = 100);
var z = #x + (a = 1) + (a = 2) + (a = 3) + f();
function f() {
f (test.js:12:24)
testFunction (test.js:10:44)
(anonymous) (expr.js:0:0)
function f() {
for (let { x, y } = #{ x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
}

View File

@ -0,0 +1,76 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-await-optimization
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests breakable locations in variable initializations.');
let source = `
function testFunction() {
var obj1 = {a : 1};
var arr1 = [1];
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
var a = 1;
const x = (a = 20);
var y = (a = 100);
var z = x + (a = 1) + (a = 2) + (a = 3) + f();
function f() {
for (let { x, y } = { x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
}
var b = obj1.a;
(async function asyncF() {
let r = await Promise.resolve(42);
return r;
})();
return promise;
}
//# sourceURL=test.js`;
contextGroup.addScript(source);
session.setupScriptMap();
InspectorTest.runAsyncTestSuite([
async function testBreakLocations() {
Protocol.Debugger.enable();
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
let {result:{locations}} = await Protocol.Debugger.getPossibleBreakpoints({
start: {lineNumber: 0, columnNumber : 0, scriptId}});
session.logBreakLocations(locations);
},
async function testStepInto() {
Protocol.Debugger.pause();
let fin = Protocol.Runtime.evaluate({
expression: 'testFunction()//# sourceURL=expr.js', awaitPromise: true}).then(() => false);
let result;
while (result = await Promise.race([fin, Protocol.Debugger.oncePaused()])) {
let {params:{callFrames}} = result;
session.logCallFrames(callFrames);
session.logSourceLocation(callFrames[0].location);
Protocol.Debugger.stepInto();
}
Protocol.Runtime.evaluate({expression: '42'});
await Protocol.Debugger.oncePaused();
await Protocol.Debugger.resume();
},
async function testStepIntoAfterBreakpoint() {
Protocol.Debugger.setBreakpointByUrl({lineNumber: 10, url: 'test.js'});
Protocol.Runtime.evaluate({
expression: 'testFunction()//# sourceURL=expr.js'});
await awaitPausedAndDump();
Protocol.Debugger.stepInto();
await awaitPausedAndDump();
await Protocol.Debugger.resume();
async function awaitPausedAndDump() {
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
session.logSourceLocation(callFrames[0].location);
}
}
]);

View File

@ -8,7 +8,7 @@ var ran = false;
async function test() {
try {
let namespace = await import('modules-skip-5.js');
let namespace = await import('modules-skip-5.js').then(x => x);
assertSame(namespace.static_life, namespace.dynamic_life);
assertSame(namespace.relative_static_life, namespace.dynamic_life);
ran = true;

View File

@ -15,13 +15,17 @@ async function f() {
}
f();
async function g() {
async function importUndefined() {
try {
let namespace = await import({ get toString() { return undefined; }});
await import({ get toString() { return undefined; }})
} catch(e) {
log(2);
}
}
function g() {
let namespace = Promise.resolve().then(importUndefined);
}
g();
%RunMicrotasks();
assertEquals(list, [1,2]);