diff --git a/lib/IRGen/ESTreeIRGen-func.cpp b/lib/IRGen/ESTreeIRGen-func.cpp index e19d530e06c..92326884aac 100644 --- a/lib/IRGen/ESTreeIRGen-func.cpp +++ b/lib/IRGen/ESTreeIRGen-func.cpp @@ -251,13 +251,15 @@ NormalFunction *ESTreeIRGen::genCapturingFunction( capturedState); return newFunc; } - + const bool isGeneratorInnerFunction = + functionKind == Function::DefinitionKind::GeneratorInnerArrow; auto compileFunc = [this, newFunc, functionNode, legacyClsCtx = curFunction()->legacyClassContext, capturedState, - parentScope] { + parentScope, + isGeneratorInnerFunction] { FunctionContext newFunctionContext{ this, newFunc, functionNode->getSemInfo()}; newFunctionContext.legacyClassContext = legacyClsCtx; @@ -268,12 +270,44 @@ NormalFunction *ESTreeIRGen::genCapturingFunction( // Propagate captured "this", "new.target" and "arguments" from parents. curFunction()->capturedState = capturedState; - emitFunctionPrologue( - functionNode, - Builder.createBasicBlock(newFunc), - InitES5CaptureState::No, - DoEmitDeclarations::Yes, - parentScope); + if (isGeneratorInnerFunction && !hasSimpleParams(functionNode)) { + // This inner generator with non simple params will be stepped once by the + // outer generator to initialize params, before being then available to + // user code. Note that since we are in a more locked down use case (an + // async arrow function, compared to a regular generator function), we + // know that the generator can only be invoked with .next(), not + // .return(). Generator arrow functions are not in the language. + auto *prologueBB = Builder.createBasicBlock(newFunc); + auto *entryPointBB = Builder.createBasicBlock(newFunc); + // Initialize parameters. + Builder.setInsertionBlock(prologueBB); + emitFunctionPrologue( + functionNode, + prologueBB, + InitES5CaptureState::No, + DoEmitDeclarations::Yes, + parentScope); + // Then on the next stepping of the generator, branch to the actual + // function body code. + Builder.createSaveAndYieldInst( + Builder.getLiteralUndefined(), + Builder.getLiteralBool(false), + entryPointBB); + // Actual entry point of function from the caller's perspective. + Builder.setInsertionBlock(entryPointBB); + genResumeGenerator( + GenFinally::No, + Builder.createAllocStackInst( + genAnonymousLabelName("isReturn_entry"), Type::createBoolean()), + Builder.createBasicBlock(newFunc)); + } else { + emitFunctionPrologue( + functionNode, + Builder.createBasicBlock(newFunc), + InitES5CaptureState::No, + DoEmitDeclarations::Yes, + parentScope); + } auto *body = ESTree::getBlockStatement(functionNode); assert(body && "empty function body"); diff --git a/test/hermes/regress-async-arrow.js b/test/hermes/regress-async-arrow.js new file mode 100644 index 00000000000..5ce1493944d --- /dev/null +++ b/test/hermes/regress-async-arrow.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: %hermes -O %s | %FileCheck --match-full-lines %s +// RUN: %hermes -O -emit-binary -out %t.hbc %s && %hermes %t.hbc | %FileCheck --match-full-lines %s +// RUN: %shermes -exec %s | %FileCheck --match-full-lines %s + +print("async arrow"); +// CHECK: async arrow + +function foo() { + return 10; +} + +(async (x = foo()) => { + print (await Promise.resolve("hello")); +})() +// CHECK-NEXT: hello