 Chromium Code Reviews
 Chromium Code Reviews Issue 2643023002:
  [async-await] Move remaining async-await code to TF  (Closed)
    
  
    Issue 2643023002:
  [async-await] Move remaining async-await code to TF  (Closed) 
  | Index: src/builtins/builtins-async-function.cc | 
| diff --git a/src/builtins/builtins-async-function.cc b/src/builtins/builtins-async-function.cc | 
| index a03c976ef0e5fc1fa40460ba85123672af7151f7..3339a25046e521c990739c7b61b81b64fa42d6f1 100644 | 
| --- a/src/builtins/builtins-async-function.cc | 
| +++ b/src/builtins/builtins-async-function.cc | 
| @@ -2,7 +2,7 @@ | 
| // Use of this source code is governed by a BSD-style license that can be | 
| // found in the LICENSE file. | 
| -#include "src/builtins/builtins-promise.h" | 
| +#include "src/builtins/builtins-async.h" | 
| #include "src/builtins/builtins-utils.h" | 
| #include "src/builtins/builtins.h" | 
| #include "src/code-stub-assembler.h" | 
| @@ -14,12 +14,151 @@ typedef compiler::Node Node; | 
| typedef CodeStubAssembler::ParameterMode ParameterMode; | 
| typedef compiler::CodeAssemblerState CodeAssemblerState; | 
| -class AsyncFunctionBuiltinsAssembler : public PromiseBuiltinsAssembler { | 
| +class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler { | 
| public: | 
| explicit AsyncFunctionBuiltinsAssembler(CodeAssemblerState* state) | 
| - : PromiseBuiltinsAssembler(state) {} | 
| + : AsyncBuiltinsAssembler(state) {} | 
| + | 
| + protected: | 
| + void AsyncFunctionAwait(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, | 
| + JSGeneratorObject::ResumeMode resume_mode); | 
| +}; | 
| + | 
| +namespace { | 
| + | 
| +// Describe fields of Context associated with AsyncFunctionAwait resume | 
| +// closures. | 
| +// TODO(jgruber): Refactor to reuse code for upcoming async-generators. | 
| +class AwaitContext { | 
| + public: | 
| + enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength }; | 
| }; | 
| +} // anonymous namespace | 
| + | 
| +void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure( | 
| + Node* context, Node* sent_value, | 
| + JSGeneratorObject::ResumeMode resume_mode) { | 
| + DCHECK(resume_mode == JSGeneratorObject::kNext || | 
| + resume_mode == JSGeneratorObject::kThrow); | 
| + | 
| + Node* const generator = | 
| + LoadContextElement(context, AwaitContext::kGeneratorSlot); | 
| + CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE)); | 
| + | 
| + // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with | 
| + // unnecessary runtime checks removed. | 
| + // TODO(jgruber): Refactor to reuse code from builtins-generator.cc. | 
| + | 
| + // Ensure that the generator is neither closed nor running. | 
| + CSA_SLOW_ASSERT( | 
| + this, | 
| + SmiGreaterThan( | 
| + LoadObjectField(generator, JSGeneratorObject::kContinuationOffset), | 
| + SmiConstant(JSGeneratorObject::kGeneratorClosed))); | 
| + | 
| + // Resume the {receiver} using our trampoline. | 
| + Callable callable = CodeFactory::ResumeGenerator(isolate()); | 
| + CallStub(callable, context, sent_value, generator, SmiConstant(resume_mode)); | 
| 
jgruber
2017/01/19 16:12:00
GeneratorPrototype{Next,Return} basically boils do
 | 
| + | 
| + // The resulting Promise is a throwaway, so it doesn't matter what it | 
| + // resolves to. What is important is that we don't end up keeping the | 
| + // whole chain of intermediate Promises alive by returning the return value | 
| + // of ResumeGenerator, as that would create a memory leak. | 
| +} | 
| + | 
| +TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) { | 
| + CSA_ASSERT_JS_ARGC_EQ(this, 1); | 
| + Node* const sentError = Parameter(1); | 
| + Node* const context = Parameter(4); | 
| + | 
| + AsyncFunctionAwaitResumeClosure(context, sentError, | 
| + JSGeneratorObject::kThrow); | 
| + Return(UndefinedConstant()); | 
| +} | 
| + | 
| +TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) { | 
| + CSA_ASSERT_JS_ARGC_EQ(this, 1); | 
| + Node* const sentValue = Parameter(1); | 
| + Node* const context = Parameter(4); | 
| + | 
| + AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext); | 
| + Return(UndefinedConstant()); | 
| +} | 
| + | 
| +// ES#abstract-ops-async-function-await | 
| +// AsyncFunctionAwait ( value ) | 
| +// Shared logic for the core of await. The parser desugars | 
| +// await awaited | 
| +// into | 
| +// yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise) | 
| +// The 'awaited' parameter is the value; the generator stands in | 
| +// for the asyncContext, and .promise is the larger promise under | 
| +// construction by the enclosing async function. | 
| +void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( | 
| + 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)); | 
| + | 
| + NodeGenerator1 create_closure_context = [&](Node* native_context) -> Node* { | 
| + Node* const context = | 
| + CreatePromiseContext(native_context, AwaitContext::kLength); | 
| + StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot, | 
| + generator); | 
| + return context; | 
| + }; | 
| + | 
| + // 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 | 
| 
jgruber
2017/01/19 16:12:00
I think caitp has already done some work on this.
 | 
| + // InternalPerformPromiseThen. | 
| + | 
| + Node* const result = Await( | 
| + context, generator, awaited, outer_promise, create_closure_context, | 
| + Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN, | 
| + Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught); | 
| + | 
| + Return(result); | 
| +} | 
| + | 
| +// 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) { | 
| + CSA_ASSERT_JS_ARGC_EQ(this, 3); | 
| + Node* const generator = Parameter(1); | 
| + Node* const awaited = Parameter(2); | 
| + Node* const outer_promise = Parameter(3); | 
| + Node* const context = Parameter(6); | 
| + | 
| + static const bool kIsPredictedAsCaught = true; | 
| + | 
| + AsyncFunctionAwait(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) { | 
| + CSA_ASSERT_JS_ARGC_EQ(this, 3); | 
| + Node* const generator = Parameter(1); | 
| + Node* const awaited = Parameter(2); | 
| + Node* const outer_promise = Parameter(3); | 
| + Node* const context = Parameter(6); | 
| + | 
| + static const bool kIsPredictedAsCaught = false; | 
| + | 
| + AsyncFunctionAwait(context, generator, awaited, outer_promise, | 
| + kIsPredictedAsCaught); | 
| +} | 
| + | 
| TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) { | 
| CSA_ASSERT_JS_ARGC_EQ(this, 0); | 
| Node* const context = Parameter(3); |