author | André Bargull <andre.bargull@gmail.com> |
Mon, 25 Feb 2019 05:09:51 -0800 | |
changeset 461674 | 135c13d4ceba69ab9b70c4803198b107c8f0cc06 |
parent 461673 | e125746cec9579271e48e5570ab9fec66222633d |
child 461675 | 48fb1e2b6e97f1f089c259d4f3209ee26a274e74 |
push id | 35626 |
push user | csabou@mozilla.com |
push date | Thu, 28 Feb 2019 11:31:08 +0000 |
treeherder | mozilla-central@2ea0c1db7e60 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | arai |
bugs | 1530324 |
milestone | 67.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/AsyncIteration.js +++ b/js/src/builtin/AsyncIteration.js @@ -2,25 +2,25 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ function AsyncIteratorIdentity() { return this; } function AsyncGeneratorNext(val) { - assert(IsAsyncGeneratorGeneratorObject(this), + assert(IsAsyncGeneratorObject(this), "ThisArgument must be a generator object for async generators"); return resumeGenerator(this, val, "next"); } function AsyncGeneratorThrow(val) { - assert(IsAsyncGeneratorGeneratorObject(this), + assert(IsAsyncGeneratorObject(this), "ThisArgument must be a generator object for async generators"); return resumeGenerator(this, val, "throw"); } function AsyncGeneratorReturn(val) { - assert(IsAsyncGeneratorGeneratorObject(this), + assert(IsAsyncGeneratorObject(this), "ThisArgument must be a generator object for async generators"); var rval = { value: val, done: true }; return resumeGenerator(this, rval, "return"); }
--- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -995,22 +995,18 @@ bool ModuleObject::noteFunctionDeclarati for (const auto& funDecl : *funDecls) { fun = funDecl.fun; obj = Lambda(cx, fun, env); if (!obj) { return false; } - if (fun->isAsync()) { - if (fun->isGenerator()) { - obj = WrapAsyncGenerator(cx, obj.as<JSFunction>()); - } else { - obj = WrapAsyncFunction(cx, obj.as<JSFunction>()); - } + if (fun->isAsync() && !fun->isGenerator()) { + obj = WrapAsyncFunction(cx, obj.as<JSFunction>()); } if (!obj) { return false; } value = ObjectValue(*obj); if (!SetProperty(cx, env, funDecl.name->asPropertyName(), value)) {
--- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -363,17 +363,17 @@ JSString* js::ObjectToSource(JSContext* if (!buf.append("get ")) { return false; } } else if (kind == PropertyKind::Setter) { if (!buf.append("set ")) { return false; } } else if (kind == PropertyKind::Method && fun) { - if (IsWrappedAsyncFunction(fun)) { + if (IsWrappedAsyncFunction(fun) || fun->isAsync()) { if (!buf.append("async ")) { return false; } } if (fun->isGenerator()) { if (!buf.append('*')) { return false;
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -5438,19 +5438,16 @@ JSScript* js::TestingFunctionArgumentToS break; } } // Get unwrapped async function. if (IsWrappedAsyncFunction(fun)) { fun = GetUnwrappedAsyncFunction(fun); } - if (IsWrappedAsyncGenerator(fun)) { - fun = GetUnwrappedAsyncGenerator(fun); - } if (!fun->isInterpreted()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TESTING_SCRIPTS_ONLY); return nullptr; } JSScript* script = JSFunction::getOrCreateScript(cx, fun);
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3193,26 +3193,27 @@ bool BytecodeEmitter::emitAnonymousFunct } bool BytecodeEmitter::emitAnonymousFunctionWithComputedName( ParseNode* node, FunctionPrefixKind prefixKind) { MOZ_ASSERT(node->isDirectRHSAnonFunction()); if (node->is<FunctionNode>()) { if (!emitTree(node)) { - // [stack] # !isAsync || !needsHomeObject + // [stack] # !isAsync || isGenerator || !needsHomeObject // [stack] NAME FUN - // [stack] # isAsync && needsHomeObject + // [stack] # isAsync && !isGenerator && needsHomeObject // [stack] NAME UNWRAPPED WRAPPED return false; } unsigned depth = 1; FunctionNode* funNode = &node->as<FunctionNode>(); FunctionBox* funbox = funNode->funbox(); - if (funbox->isAsync() && funbox->needsHomeObject()) { + if (funbox->isAsync() && !funbox->isGenerator() && + funbox->needsHomeObject()) { depth = 2; } if (!emitDupAt(depth)) { // [stack] # !isAsync || !needsHomeObject // [stack] NAME FUN NAME // [stack] # isAsync && needsHomeObject // [stack] NAME UNWRAPPED WRAPPED NAME return false; @@ -5802,20 +5803,19 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e // Make the function object a literal in the outer script's pool. unsigned index = objectList.add(funNode->funbox()); // Non-hoisted functions simply emit their respective op. if (!funNode->functionIsHoisted()) { // JSOP_LAMBDA_ARROW is always preceded by a new.target MOZ_ASSERT(fun->isArrow() == (funNode->syntaxKind() == FunctionSyntaxKind::Arrow)); - if (funbox->isAsync()) { + if (funbox->isAsync() && !funbox->isGenerator()) { MOZ_ASSERT(!needsProto); - return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(), - fun->isGenerator()); + return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow()); } if (fun->isArrow()) { if (sc->allowNewTarget()) { if (!emit1(JSOP_NEWTARGET)) { return false; } } else { @@ -5862,19 +5862,18 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e RootedModuleObject module(cx, sc->asModuleContext()->module()); if (!module->noteFunctionDeclaration(cx, name, fun)) { return false; } } else { MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext()); MOZ_ASSERT(funNode->syntaxKind() == FunctionSyntaxKind::Statement); MOZ_ASSERT(inPrologue()); - if (funbox->isAsync()) { - if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(), - fun->isGenerator())) { + if (funbox->isAsync() && !funbox->isGenerator()) { + if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow())) { return false; } } else { if (!emitIndex32(JSOP_LAMBDA, index)) { return false; } } if (!emit1(JSOP_DEFFUN)) { @@ -5884,19 +5883,19 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e } else { // For functions nested within functions and blocks, make a lambda and // initialize the binding name of the function in the current scope. NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize); if (!noe.prepareForRhs()) { return false; } - if (funbox->isAsync()) { + if (funbox->isAsync() && !funbox->isGenerator()) { if (!emitAsyncWrapper(index, /* needsHomeObject = */ false, - /* isArrow = */ false, funbox->isGenerator())) { + /* isArrow = */ false)) { return false; } } else { if (!emitIndexOp(JSOP_LAMBDA, index)) { return false; } } if (!noe.emitAssignment()) { @@ -5929,17 +5928,17 @@ bool BytecodeEmitter::emitAsyncWrapperLa return false; } } return true; } bool BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, - bool isArrow, bool isGenerator) { + bool isArrow) { // needsHomeObject can be true for propertyList for extended class. // In that case push both unwrapped and wrapped function, in order to // initialize home object of unwrapped function, and set wrapped function // as a property. // // lambda // unwrapped // dup // unwrapped unwrapped // toasync // unwrapped wrapped @@ -5964,24 +5963,18 @@ bool BytecodeEmitter::emitAsyncWrapper(u if (!emitAsyncWrapperLambda(index, isArrow)) { return false; } if (needsHomeObject) { if (!emit1(JSOP_DUP)) { return false; } } - if (isGenerator) { - if (!emit1(JSOP_TOASYNCGEN)) { - return false; - } - } else { - if (!emit1(JSOP_TOASYNC)) { - return false; - } + if (!emit1(JSOP_TOASYNC)) { + return false; } return true; } bool BytecodeEmitter::emitDo(BinaryNode* doNode) { ParseNode* bodyNode = doNode->left(); DoWhileEmitter doWhile(this); @@ -7897,17 +7890,18 @@ bool BytecodeEmitter::emitPropertyList(L } } if (propVal->is<FunctionNode>() && propVal->as<FunctionNode>().funbox()->needsHomeObject()) { FunctionBox* funbox = propVal->as<FunctionNode>().funbox(); MOZ_ASSERT(funbox->function()->allowSuperProperty()); - if (!pe.emitInitHomeObject(funbox->asyncKind())) { + bool isAsyncNonGenerator = funbox->isAsync() && !funbox->isGenerator(); + if (!pe.emitInitHomeObject(isAsyncNonGenerator)) { // [stack] CTOR? OBJ CTOR? KEY? FUN return false; } } return true; }; PropertyEmitter::Kind kind =
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -651,17 +651,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter { MOZ_MUST_USE bool emitAwaitInInnermostScope(UnaryNode* awaitNode); MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope); MOZ_MUST_USE bool emitPropLHS(PropertyAccess* prop); MOZ_MUST_USE bool emitPropIncDec(UnaryNode* incDec); MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow); MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, - bool isArrow, bool isGenerator); + bool isArrow); MOZ_MUST_USE bool emitComputedPropertyName(UnaryNode* computedPropName); // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM // opcode onto the stack in the right order. In the case of SETELEM, the // value to be assigned must already be pushed. enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref }; MOZ_MUST_USE bool emitElemOperands(PropertyByValue* elem,
--- a/js/src/frontend/ObjectEmitter.cpp +++ b/js/src/frontend/ObjectEmitter.cpp @@ -223,25 +223,24 @@ bool PropertyEmitter::prepareForComputed #ifdef DEBUG propertyState_ = PropertyState::ComputedValue; #endif return true; } bool PropertyEmitter::emitInitHomeObject( - FunctionAsyncKind kind /* = FunctionAsyncKind::SyncFunction */) { + bool isAsyncNonGenerator /* = false */) { MOZ_ASSERT(propertyState_ == PropertyState::PropValue || propertyState_ == PropertyState::IndexValue || propertyState_ == PropertyState::ComputedValue); // [stack] CTOR? HOMEOBJ CTOR? KEY? FUN - bool isAsync = kind == FunctionAsyncKind::AsyncFunction; - if (isAsync) { + if (isAsyncNonGenerator) { // [stack] CTOR? HOMEOBJ CTOR? KEY? UNWRAPPED WRAPPED if (!bce_->emit1(JSOP_SWAP)) { // [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED UNWRAPPED return false; } } // There are the following values on the stack conditionally, between @@ -250,28 +249,28 @@ bool PropertyEmitter::emitInitHomeObject // * KEY if isIndexOrComputed_ // * WRAPPED if isAsync // // JSOP_INITHOMEOBJECT uses one of the following: // * HOMEOBJ if !isStatic_ // (`super.foo` points the super prototype property) // * the 2nd CTOR if isStatic_ // (`super.foo` points the super constructor property) - if (!bce_->emitDupAt(1 + isIndexOrComputed_ + isAsync)) { + if (!bce_->emitDupAt(1 + isIndexOrComputed_ + isAsyncNonGenerator)) { // [stack] # non-static method // [stack] CTOR? HOMEOBJ CTOR KEY? WRAPPED? FUN CTOR // [stack] # static method // [stack] CTOR? HOMEOBJ KEY? WRAPPED? FUN HOMEOBJ return false; } if (!bce_->emit1(JSOP_INITHOMEOBJECT)) { // [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED? FUN return false; } - if (isAsync) { + if (isAsyncNonGenerator) { if (!bce_->emit1(JSOP_POP)) { // [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED return false; } } #ifdef DEBUG if (propertyState_ == PropertyState::PropValue) {
--- a/js/src/frontend/ObjectEmitter.h +++ b/js/src/frontend/ObjectEmitter.h @@ -13,18 +13,16 @@ #include <stddef.h> // size_t, ptrdiff_t #include <stdint.h> // uint32_t #include "frontend/EmitterScope.h" // EmitterScope #include "frontend/TDZCheckCache.h" // TDZCheckCache #include "js/RootingAPI.h" // JS::Handle, JS::Rooted #include "vm/BytecodeUtil.h" // JSOp #include "vm/JSAtom.h" // JSAtom -#include "vm/JSFunction.h" // JSFunction, FunctionPrefixKind -#include "vm/JSScript.h" // FunctionAsyncKind #include "vm/NativeObject.h" // PlainObject #include "vm/Scope.h" // LexicalScope namespace js { namespace frontend { struct BytecodeEmitter; @@ -239,18 +237,17 @@ class MOZ_STACK_CLASS PropertyEmitter { // { [ key ]: value } // ^ // | // keyPos MOZ_MUST_USE bool prepareForComputedPropKey( const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype); MOZ_MUST_USE bool prepareForComputedPropValue(); - MOZ_MUST_USE bool emitInitHomeObject( - FunctionAsyncKind kind = FunctionAsyncKind::SyncFunction); + MOZ_MUST_USE bool emitInitHomeObject(bool isAsyncNonGenerator = false); // @param key // Property key MOZ_MUST_USE bool emitInitProp(JS::Handle<JSAtom*> key); MOZ_MUST_USE bool emitInitGetter(JS::Handle<JSAtom*> key); MOZ_MUST_USE bool emitInitSetter(JS::Handle<JSAtom*> key); MOZ_MUST_USE bool emitInitIndexProp(); @@ -513,17 +510,17 @@ class MOZ_RAII AutoSaveLocalStrictMode { // emit(function_for_m); // ce.emitInitHomeObject(); // ce.emitInitProp(atom_of_m); // // `async m() { super.f(); }` in class // // after emitInitConstructor/emitInitDefaultConstructor // ce.prepareForPropValue(Some(offset_of_m)); // emit(function_for_m); -// ce.emitInitHomeObject(FunctionAsyncKind::Async); +// ce.emitInitHomeObject(true); // ce.emitInitProp(atom_of_m); // // `get p() { super.f(); }` in class // // after emitInitConstructor/emitInitDefaultConstructor // ce.prepareForPropValue(Some(offset_of_p)); // emit(function_for_p); // ce.emitInitHomeObject(); // ce.emitInitGetter(atom_of_m);
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1980,17 +1980,18 @@ JSFunction* AllocNewFunction(JSContext* #endif flags = (generatorKind == GeneratorKind::NotGenerator && asyncKind == FunctionAsyncKind::SyncFunction ? JSFunction::INTERPRETED_NORMAL : JSFunction::INTERPRETED_GENERATOR_OR_ASYNC); } // We store the async wrapper in a slot for later access. - if (asyncKind == FunctionAsyncKind::AsyncFunction) { + if (asyncKind == FunctionAsyncKind::AsyncFunction && + generatorKind == GeneratorKind::NotGenerator) { allocKind = gc::AllocKind::FUNCTION_EXTENDED; } fun = NewFunctionWithProto(cx, nullptr, 0, flags, nullptr, atom, proto, allocKind, TenuredObject); if (!fun) { return nullptr; } @@ -2561,18 +2562,23 @@ GeneralParser<ParseHandler, Unit>::funct } return funNode; } RootedObject proto(cx_); if (generatorKind == GeneratorKind::Generator || asyncKind == FunctionAsyncKind::AsyncFunction) { - proto = - GlobalObject::getOrCreateGeneratorFunctionPrototype(cx_, cx_->global()); + if (generatorKind == GeneratorKind::Generator && + asyncKind == FunctionAsyncKind::AsyncFunction) { + proto = GlobalObject::getOrCreateAsyncGenerator(cx_, cx_->global()); + } else { + proto = GlobalObject::getOrCreateGeneratorFunctionPrototype( + cx_, cx_->global()); + } if (!proto) { return null(); } } RootedFunction fun( cx_, newFunction(funName, kind, generatorKind, asyncKind, proto)); if (!fun) { return null();
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/offThreadCompileScript-03.js @@ -0,0 +1,50 @@ +// |jit-test| skip-if: helperThreadCount() === 0 + +// Test offThreadCompileScript for different function types. + +load(libdir + 'asserts.js'); + +var id; + +id = offThreadCompileScript(` + function f() { return "pass"; } + f(); +`); +assertEq(runOffThreadScript(id), "pass"); + +id = offThreadCompileScript(` + function* f() { return "pass"; } + f().next(); +`); +assertDeepEq(runOffThreadScript(id), {value: "pass", done: true}); + +id = offThreadCompileScript(` + async function f() { return "pass"; } + f(); +`); +assertEventuallyEq(runOffThreadScript(id), "pass"); + +id = offThreadCompileScript(` + async function* f() { return "pass"; } + f().next(); +`); +assertEventuallyDeepEq(runOffThreadScript(id), {value: "pass", done: true}); + +// Copied from js/src/tests/shell.js +function getPromiseResult(promise) { + var result, error, caught = false; + promise.then(r => { result = r; }, + e => { caught = true; error = e; }); + drainJobQueue(); + if (caught) + throw error; + return result; +} + +function assertEventuallyEq(promise, expected) { + assertEq(getPromiseResult(promise), expected); +} + +function assertEventuallyDeepEq(promise, expected) { + assertDeepEq(getPromiseResult(promise), expected); +}
--- a/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js +++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js @@ -13,26 +13,20 @@ let hits = 0; let resumption = undefined; dbg.onEnterFrame = frame => { if (frame.type == "call" && frame.callee.name === "f") { frame.onPop = completion => { assertEq(completion.return, resumption.return); hits++; }; - // Don't tell anyone, but if we force-return a generator object here, - // the robots will still detect it and throw an error. No protection - // against Skynet, for us poor humans! + // If we force-return a generator object here, the caller will never + // receive an async generator object. resumption = frame.eval(`(function* f2() { hit2 = true; })()`); assertEq(resumption.return.class, "Generator"); return resumption; } }; -let error; -try { - g.f(0); -} catch (e) { - error = e; -} +let it = g.f(0); assertEq(hits, 1); -assertEq(error instanceof g.Error, true); +assertEq(gw.makeDebuggeeValue(it), resumption.return); assertEq(g.hit2, false);
--- a/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js +++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js @@ -39,16 +39,16 @@ assertEq(hits, 1); drainJobQueue(); assertEq(hits, 1); let pw2 = gw.makeDebuggeeValue(p2); assertEq(pw2.isPromise, true); assertEq(pw2.promiseState, "fulfilled"); -assertEq(pw2.promiseValue.getProperty("value").return, 123); +assertEq(pw2.promiseValue.getProperty("value").return, "moar ponies"); assertEq(pw2.promiseValue.getProperty("done").return, true); let pw = gw.makeDebuggeeValue(p); assertEq(pw.isPromise, true); assertEq(pw.promiseState, "fulfilled"); assertEq(pw.promiseValue.getProperty("value").return, undefined); assertEq(pw.promiseValue.getProperty("done").return, true);
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -4634,38 +4634,16 @@ bool BaselineCodeGen<Handler>::emit_JSOP } masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); frame.pop(); frame.push(R0); return true; } -typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction); -static const VMFunction ToAsyncGenInfo = - FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen"); - -template <typename Handler> -bool BaselineCodeGen<Handler>::emit_JSOP_TOASYNCGEN() { - frame.syncStack(0); - masm.unboxObject(frame.addressOfStackValue(-1), R0.scratchReg()); - - prepareVMCall(); - pushArg(R0.scratchReg()); - - if (!callVM(ToAsyncGenInfo)) { - return false; - } - - masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); - frame.pop(); - frame.push(R0); - return true; -} - typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject, HandleValue); static const VMFunction ToAsyncIterInfo = FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter"); template <typename Handler> bool BaselineCodeGen<Handler>::emit_JSOP_TOASYNCITER() { frame.syncStack(0); masm.unboxObject(frame.addressOfStackValue(-2), R0.scratchReg()); @@ -6070,16 +6048,17 @@ MethodStatus BaselineCompiler::emitBody( } switch (op) { // ===== NOT Yet Implemented ===== case JSOP_FORCEINTERPRETER: // Intentionally not implemented. case JSOP_UNUSED71: case JSOP_UNUSED151: + case JSOP_UNUSED192: case JSOP_LIMIT: // === !! WARNING WARNING WARNING !! === // DO NOT add new ops to this list! All bytecode ops MUST have Baseline // support. Follow-up bugs are not acceptable. JitSpew(JitSpew_BaselineAbort, "Unhandled op: %s", CodeName[op]); return Method_CantCompile; #define EMIT_OP(OP) \
--- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -187,17 +187,16 @@ namespace jit { _(JSOP_DEBUGLEAVELEXICALENV) \ _(JSOP_PUSHVARENV) \ _(JSOP_POPVARENV) \ _(JSOP_EXCEPTION) \ _(JSOP_DEBUGGER) \ _(JSOP_ARGUMENTS) \ _(JSOP_REST) \ _(JSOP_TOASYNC) \ - _(JSOP_TOASYNCGEN) \ _(JSOP_TOASYNCITER) \ _(JSOP_TOID) \ _(JSOP_TOSTRING) \ _(JSOP_TABLESWITCH) \ _(JSOP_ITER) \ _(JSOP_MOREITER) \ _(JSOP_ISNOITER) \ _(JSOP_ENDITER) \
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -11373,25 +11373,16 @@ typedef JSObject* (*ToAsyncFn)(JSContext static const VMFunction ToAsyncInfo = FunctionInfo<ToAsyncFn>(js::WrapAsyncFunction, "ToAsync"); void CodeGenerator::visitToAsync(LToAsync* lir) { pushArg(ToRegister(lir->unwrapped())); callVM(ToAsyncInfo, lir); } -typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction); -static const VMFunction ToAsyncGenInfo = - FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen"); - -void CodeGenerator::visitToAsyncGen(LToAsyncGen* lir) { - pushArg(ToRegister(lir->unwrapped())); - callVM(ToAsyncGenInfo, lir); -} - typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject, HandleValue); static const VMFunction ToAsyncIterInfo = FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter"); void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) { pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex)); pushArg(ToRegister(lir->iterator())); callVM(ToAsyncIterInfo, lir);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2330,19 +2330,16 @@ AbortReasonOr<Ok> IonBuilder::inspectOpc case JSOP_TYPEOF: case JSOP_TYPEOFEXPR: return jsop_typeof(); case JSOP_TOASYNC: return jsop_toasync(); - case JSOP_TOASYNCGEN: - return jsop_toasyncgen(); - case JSOP_TOASYNCITER: return jsop_toasynciter(); case JSOP_TOID: return jsop_toid(); case JSOP_ITERNEXT: return jsop_iternext(); @@ -2528,16 +2525,17 @@ AbortReasonOr<Ok> IonBuilder::inspectOpc break; case JSOP_FORCEINTERPRETER: // Intentionally not implemented. break; case JSOP_UNUSED71: case JSOP_UNUSED151: + case JSOP_UNUSED192: case JSOP_LIMIT: break; } // Track a simpler message, since the actionable abort message is a // static string, and the internal opcode name isn't an actionable // thing anyways. trackActionableAbort("Unsupported bytecode"); @@ -13132,28 +13130,16 @@ AbortReasonOr<Ok> IonBuilder::jsop_toasy MToAsync* ins = MToAsync::New(alloc(), unwrapped); current->add(ins); current->push(ins); return resumeAfter(ins); } -AbortReasonOr<Ok> IonBuilder::jsop_toasyncgen() { - MDefinition* unwrapped = current->pop(); - MOZ_ASSERT(unwrapped->type() == MIRType::Object); - - MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped); - - current->add(ins); - current->push(ins); - - return resumeAfter(ins); -} - AbortReasonOr<Ok> IonBuilder::jsop_toasynciter() { MDefinition* nextMethod = current->pop(); MDefinition* iterator = current->pop(); MOZ_ASSERT(iterator->type() == MIRType::Object); MToAsyncIter* ins = MToAsyncIter::New(alloc(), iterator, nextMethod); current->add(ins);
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -636,17 +636,16 @@ class IonBuilder : public MIRGenerator, AbortReasonOr<Ok> jsop_lambda_arrow(JSFunction* fun); AbortReasonOr<Ok> jsop_setfunname(uint8_t prefixKind); AbortReasonOr<Ok> jsop_pushlexicalenv(uint32_t index); AbortReasonOr<Ok> jsop_copylexicalenv(bool copySlots); AbortReasonOr<Ok> jsop_functionthis(); AbortReasonOr<Ok> jsop_globalthis(); AbortReasonOr<Ok> jsop_typeof(); AbortReasonOr<Ok> jsop_toasync(); - AbortReasonOr<Ok> jsop_toasyncgen(); AbortReasonOr<Ok> jsop_toasynciter(); AbortReasonOr<Ok> jsop_toid(); AbortReasonOr<Ok> jsop_iter(); AbortReasonOr<Ok> jsop_itermore(); AbortReasonOr<Ok> jsop_isnoiter(); AbortReasonOr<Ok> jsop_iterend(); AbortReasonOr<Ok> jsop_iternext(); AbortReasonOr<Ok> jsop_in();
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1126,23 +1126,16 @@ void LIRGenerator::visitTypeOf(MTypeOf* } void LIRGenerator::visitToAsync(MToAsync* ins) { LToAsync* lir = new (alloc()) LToAsync(useRegisterAtStart(ins->input())); defineReturn(lir, ins); assignSafepoint(lir, ins); } -void LIRGenerator::visitToAsyncGen(MToAsyncGen* ins) { - LToAsyncGen* lir = - new (alloc()) LToAsyncGen(useRegisterAtStart(ins->input())); - defineReturn(lir, ins); - assignSafepoint(lir, ins); -} - void LIRGenerator::visitToAsyncIter(MToAsyncIter* ins) { LToAsyncIter* lir = new (alloc()) LToAsyncIter(useRegisterAtStart(ins->getIterator()), useBoxAtStart(ins->getNextMethod())); defineReturn(lir, ins); assignSafepoint(lir, ins); }
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4377,27 +4377,16 @@ class MToAsync : public MUnaryInstructio setResultType(MIRType::Object); } public: INSTRUCTION_HEADER(ToAsync) TRIVIAL_NEW_WRAPPERS }; -class MToAsyncGen : public MUnaryInstruction, public SingleObjectPolicy::Data { - explicit MToAsyncGen(MDefinition* unwrapped) - : MUnaryInstruction(classOpcode, unwrapped) { - setResultType(MIRType::Object); - } - - public: - INSTRUCTION_HEADER(ToAsyncGen) - TRIVIAL_NEW_WRAPPERS -}; - class MToAsyncIter : public MBinaryInstruction, public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data { explicit MToAsyncIter(MDefinition* iterator, MDefinition* nextMethod) : MBinaryInstruction(classOpcode, iterator, nextMethod) { setResultType(MIRType::Object); } public: @@ -6799,17 +6788,18 @@ struct LambdaFunctionInfo { singletonType(fun->isSingleton()), useSingletonForClone(ObjectGroup::useSingletonForClone(fun)) { // If this assert fails, make sure CodeGenerator::visitLambda does the // right thing. We can't assert this off-thread in CodeGenerator, // because fun->isAsync() accesses the script/lazyScript and can race // with delazification on the main thread. MOZ_ASSERT_IF(flags & JSFunction::EXTENDED, fun->isArrow() || fun->allowSuperProperty() || - fun->isSelfHostedBuiltin() || fun->isAsync()); + fun->isSelfHostedBuiltin() || + (fun->isAsync() && !fun->isGenerator())); } // Be careful when calling this off-thread. Don't call any JSFunction* // methods that depend on script/lazyScript - this can race with // delazification on the main thread. JSFunction* funUnsafe() const { return fun_; } bool appendRoots(MRootList& roots) const {
--- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -787,28 +787,16 @@ class LToAsync : public LCallInstruction explicit LToAsync(const LAllocation& input) : LCallInstructionHelper(classOpcode) { setOperand(0, input); } const LAllocation* unwrapped() { return getOperand(0); } }; -class LToAsyncGen : public LCallInstructionHelper<1, 1, 0> { - public: - LIR_HEADER(ToAsyncGen) - - explicit LToAsyncGen(const LAllocation& input) - : LCallInstructionHelper(classOpcode) { - setOperand(0, input); - } - - const LAllocation* unwrapped() { return getOperand(0); } -}; - class LToAsyncIter : public LCallInstructionHelper<1, 1 + BOX_PIECES, 0> { public: LIR_HEADER(ToAsyncIter) explicit LToAsyncIter(const LAllocation& iterator, const LBoxAllocation& nextMethod) : LCallInstructionHelper(classOpcode) { setOperand(0, iterator);
--- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -470,17 +470,17 @@ static bool MappedArgGetter(JSContext* c } else if (JSID_IS_ATOM(id, cx->names().length)) { if (!argsobj.hasOverriddenLength()) { vp.setInt32(argsobj.initialLength()); } } else { MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().callee)); if (!argsobj.hasOverriddenCallee()) { RootedFunction callee(cx, &argsobj.callee()); - if (callee->isAsync()) { + if (callee->isAsync() && !callee->isGenerator()) { vp.setObject(*GetWrappedAsyncFunction(callee)); } else { vp.setObject(*callee); } } } return true; }
--- a/js/src/vm/AsyncIteration.cpp +++ b/js/src/vm/AsyncIteration.cpp @@ -17,131 +17,16 @@ #include "vm/SelfHosting.h" #include "vm/JSContext-inl.h" #include "vm/JSObject-inl.h" #include "vm/List-inl.h" using namespace js; -#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1 -#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0 - -// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody. -static bool WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - RootedFunction wrapped(cx, &args.callee().as<JSFunction>()); - RootedValue unwrappedVal( - cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)); - - // Step 1. - InvokeArgs args2(cx); - if (!FillArgumentsFromArraylike(cx, args2, args)) { - return false; - } - - RootedValue generatorVal(cx); - if (!Call(cx, unwrappedVal, args.thisv(), args2, &generatorVal)) { - return false; - } - - // Handle the case when the debugger force-returned an unexpected value. - if (!generatorVal.isObject() || - !generatorVal.toObject().is<AsyncGeneratorGeneratorObject>()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_UNEXPECTED_TYPE, "return value", - JS::InformalValueTypeName(generatorVal)); - return false; - } - - // Step 2. - AsyncGeneratorObject* asyncGenObj = - AsyncGeneratorObject::create(cx, wrapped, generatorVal); - if (!asyncGenObj) { - return false; - } - - // Step 3 (skipped). - // Done in AsyncGeneratorObject::create and generator. - - // Step 4. - args.rval().setObject(*asyncGenObj); - return true; -} - -JSObject* js::WrapAsyncGeneratorWithProto(JSContext* cx, - HandleFunction unwrapped, - HandleObject proto) { - MOZ_ASSERT(unwrapped->isAsync()); - MOZ_ASSERT(proto, - "We need an explicit prototype to avoid the default" - "%FunctionPrototype% fallback in NewFunctionWithProto()."); - - // Create a new function with AsyncGeneratorPrototype, reusing the name and - // the length of `unwrapped`. - - RootedAtom funName(cx, unwrapped->explicitName()); - uint16_t length; - if (!JSFunction::getLength(cx, unwrapped, &length)) { - return nullptr; - } - - JSFunction* wrapped = NewFunctionWithProto( - cx, WrappedAsyncGenerator, length, JSFunction::NATIVE_FUN, nullptr, - funName, proto, gc::AllocKind::FUNCTION_EXTENDED); - if (!wrapped) { - return nullptr; - } - - if (unwrapped->hasInferredName()) { - wrapped->setInferredName(unwrapped->inferredName()); - } - - // Link them to each other to make GetWrappedAsyncGenerator and - // GetUnwrappedAsyncGenerator work. - unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, - ObjectValue(*wrapped)); - wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, - ObjectValue(*unwrapped)); - - return wrapped; -} - -JSObject* js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped) { - RootedObject proto(cx, - GlobalObject::getOrCreateAsyncGenerator(cx, cx->global())); - if (!proto) { - return nullptr; - } - - return WrapAsyncGeneratorWithProto(cx, unwrapped, proto); -} - -bool js::IsWrappedAsyncGenerator(JSFunction* fun) { - return fun->maybeNative() == WrappedAsyncGenerator; -} - -JSFunction* js::GetWrappedAsyncGenerator(JSFunction* unwrapped) { - MOZ_ASSERT(unwrapped->isAsync()); - return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT) - .toObject() - .as<JSFunction>(); -} - -JSFunction* js::GetUnwrappedAsyncGenerator(JSFunction* wrapped) { - MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped)); - JSFunction* unwrapped = - &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT) - .toObject() - .as<JSFunction>(); - MOZ_ASSERT(unwrapped->isAsync()); - return unwrapped; -} - // Async Iteration proposal 4.1.1 Await Fulfilled Functions. MOZ_MUST_USE bool js::AsyncGeneratorAwaitedFulfilled( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value) { return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); } // Async Iteration proposal 4.1.2 Await Rejected Functions. @@ -273,29 +158,27 @@ static AsyncGeneratorObject* OrdinaryCre } } // Step 3. return NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto); } /* static */ AsyncGeneratorObject* AsyncGeneratorObject::create( - JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal) { - MOZ_ASSERT(generatorVal.isObject()); - MOZ_ASSERT(generatorVal.toObject().is<AsyncGeneratorGeneratorObject>()); + JSContext* cx, HandleFunction asyncGen) { + MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator()); AsyncGeneratorObject* asyncGenObj = OrdinaryCreateFromConstructorAsynGen(cx, asyncGen); if (!asyncGenObj) { return nullptr; } - // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart. - // Step 6. - asyncGenObj->setGenerator(generatorVal); + // ES2019 draft rev c2aad21fee7f5ddc89fdf7d3d305618ca3a13242 + // 25.5.3.2 AsyncGeneratorStart. // Step 7. asyncGenObj->setSuspendedStart(); // Step 8. asyncGenObj->clearSingleQueueRequest(); asyncGenObj->clearCachedRequest(); @@ -441,67 +324,62 @@ static MOZ_MUST_USE bool AsyncGeneratorY // Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. // Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g. // Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext // steps 12-14, 16-20. // Execution context switching is handled in generator. MOZ_MUST_USE bool js::AsyncGeneratorResume( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, CompletionKind completionKind, HandleValue argument) { - RootedValue generatorVal(cx, asyncGenObj->generatorVal()); - - // This case can only happen when the debugger force-returned the same - // generator object for two async generator calls. It doesn't matter how we - // handle it, as long as we don't crash. - if (generatorVal.toObject().as<AbstractGeneratorObject>().isClosed() || - !generatorVal.toObject().as<AbstractGeneratorObject>().isSuspended()) { - return AsyncGeneratorReturned(cx, asyncGenObj, UndefinedHandleValue); - } + MOZ_ASSERT(!asyncGenObj->isClosed(), + "closed generator when resuming async generator"); + MOZ_ASSERT(asyncGenObj->isSuspended(), + "non-suspended generator when resuming async generator"); // 11.4.3.5 steps 12-14, 16-20. HandlePropertyName funName = completionKind == CompletionKind::Normal ? cx->names().AsyncGeneratorNext : completionKind == CompletionKind::Throw ? cx->names().AsyncGeneratorThrow : cx->names().AsyncGeneratorReturn; FixedInvokeArgs<1> args(cx); args[0].set(argument); - RootedValue result(cx); - if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) { + RootedValue thisOrRval(cx, ObjectValue(*asyncGenObj)); + if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) { // 11.4.3.2 step 5.d, f. - if (!generatorVal.toObject().as<AbstractGeneratorObject>().isClosed()) { - generatorVal.toObject().as<AbstractGeneratorObject>().setClosed(); + if (!asyncGenObj->isClosed()) { + asyncGenObj->setClosed(); } return AsyncGeneratorThrown(cx, asyncGenObj); } // 4.1 steps 2-9. - if (asyncGenObj->generatorObj()->isAfterAwait()) { - return AsyncGeneratorAwait(cx, asyncGenObj, result); + if (asyncGenObj->isAfterAwait()) { + return AsyncGeneratorAwait(cx, asyncGenObj, thisOrRval); } // The following code corresponds to the following 3 cases: // * yield // * yield* // * return // For yield and return, property access is done on an internal result // object and it's not observable. // For yield*, it's done on a possibly user-provided result object, and // it's observable. // // Note that IteratorComplete steps in 8.2.1 are done in bytecode. // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. - RootedObject resultObj(cx, &result.toObject()); + RootedObject resultObj(cx, &thisOrRval.toObject()); RootedValue value(cx); if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) { return false; } - if (asyncGenObj->generatorObj()->isAfterYield()) { + if (asyncGenObj->isAfterYield()) { return AsyncGeneratorYield(cx, asyncGenObj, value); } // 11.4.3.2 step 5.d-g. return AsyncGeneratorReturned(cx, asyncGenObj, value); } static const JSFunctionSpec async_iterator_proto_methods[] = {
--- a/js/src/vm/AsyncIteration.h +++ b/js/src/vm/AsyncIteration.h @@ -3,53 +3,25 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef vm_AsyncIteration_h #define vm_AsyncIteration_h #include "builtin/Promise.h" +#include "js/Class.h" #include "vm/GeneratorObject.h" #include "vm/JSContext.h" #include "vm/JSObject.h" #include "vm/List.h" namespace js { -// An async generator is implemented using two function objects, which are -// referred to as the "unwrapped" and the "wrapped" async generator object. -// The unwrapped function is a generator function compiled from the async -// generator's script. |await| expressions within the async generator are -// compiled like |yield| expression for the generator function with dedicated -// opcode. The unwrapped function is never exposed to user script. -// The wrapped function is a native function which wraps the generator function, -// hence its name, and is the publicly exposed object of the async generator. -// -// The unwrapped async generator is created while compiling the async generator, -// and the wrapped async generator is created while executing the async -// generator declaration or expression. - -// Create a wrapped async generator from an unwrapped async generator with given -// prototype object. -JSObject* WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, - HandleObject proto); - -// Create a wrapped async generator from an unwrapped async generator -// with default prototype object. -JSObject* WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped); - -// Returns true if the given function is a wrapped async generator. -bool IsWrappedAsyncGenerator(JSFunction* fun); - -// Returns a wrapped async generator from an unwrapped async generator. -JSFunction* GetWrappedAsyncGenerator(JSFunction* unwrapped); - -// Return an unwrapped async generator from a wrapped async generator. -JSFunction* GetUnwrappedAsyncGenerator(JSFunction* wrapped); +class AsyncGeneratorObject; // Resume the async generator when the `await` operand fulfills to `value`. MOZ_MUST_USE bool AsyncGeneratorAwaitedFulfilled( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value); // Resume the async generator when the `await` operand rejects with `reason`. MOZ_MUST_USE bool AsyncGeneratorAwaitedRejected( @@ -62,18 +34,16 @@ MOZ_MUST_USE bool AsyncGeneratorAwaitedR // fulfilled or rejected. MOZ_MUST_USE bool AsyncGeneratorYieldReturnAwaitedFulfilled( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value); MOZ_MUST_USE bool AsyncGeneratorYieldReturnAwaitedRejected( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue reason); -class AsyncGeneratorObject; - // AsyncGeneratorRequest record in the spec. // Stores the info from AsyncGenerator#{next,return,throw}. // // This object is reused across multiple requests as an optimization, and // stored in the Slot_CachedRequest slot. class AsyncGeneratorRequest : public NativeObject { private: enum AsyncGeneratorRequestSlots { @@ -123,24 +93,21 @@ class AsyncGeneratorRequest : public Nat JS::Value completionValue() const { return getFixedSlot(Slot_CompletionValue); } PromiseObject* promise() const { return &getFixedSlot(Slot_Promise).toObject().as<PromiseObject>(); } }; -class AsyncGeneratorObject : public NativeObject { +class AsyncGeneratorObject : public AbstractGeneratorObject { private: enum AsyncGeneratorObjectSlots { // Int32 value containing one of the |State| fields from below. - Slot_State = 0, - - // Generator object for the unwrapped async generator. - Slot_Generator, + Slot_State = AbstractGeneratorObject::RESERVED_SLOTS, // * null value if this async generator has no requests // * AsyncGeneratorRequest if this async generator has only one request // * list object if this async generator has 2 or more requests Slot_QueueOrRequest, // Cached AsyncGeneratorRequest for later use. // undefined if there's no cache. @@ -178,18 +145,16 @@ class AsyncGeneratorObject : public Nati State_Completed }; State state() const { return static_cast<State>(getFixedSlot(Slot_State).toInt32()); } void setState(State state_) { setFixedSlot(Slot_State, Int32Value(state_)); } - void setGenerator(const Value& value) { setFixedSlot(Slot_Generator, value); } - // Queue is implemented in 2 ways. If only one request is queued ever, // request is stored directly to the slot. Once 2 requests are queued, a // list is created and requests are appended into it, and the list is // stored to the slot. bool isSingleQueue() const { return getFixedSlot(Slot_QueueOrRequest).isNull() || getFixedSlot(Slot_QueueOrRequest) @@ -216,18 +181,17 @@ class AsyncGeneratorObject : public Nati } void setQueue(ListObject* queue_) { setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_)); } public: static const Class class_; - static AsyncGeneratorObject* create(JSContext* cx, HandleFunction asyncGen, - HandleValue generatorVal); + static AsyncGeneratorObject* create(JSContext* cx, HandleFunction asyncGen); bool isSuspendedStart() const { return state() == State_SuspendedStart; } bool isSuspendedYield() const { return state() == State_SuspendedYield; } bool isExecuting() const { return state() == State_Executing; } bool isAwaitingYieldReturn() const { return state() == State_AwaitingYieldReturn; } bool isAwaitingReturn() const { return state() == State_AwaitingReturn; } @@ -235,23 +199,16 @@ class AsyncGeneratorObject : public Nati void setSuspendedStart() { setState(State_SuspendedStart); } void setSuspendedYield() { setState(State_SuspendedYield); } void setExecuting() { setState(State_Executing); } void setAwaitingYieldReturn() { setState(State_AwaitingYieldReturn); } void setAwaitingReturn() { setState(State_AwaitingReturn); } void setCompleted() { setState(State_Completed); } - JS::Value generatorVal() const { return getFixedSlot(Slot_Generator); } - AsyncGeneratorGeneratorObject* generatorObj() const { - return &getFixedSlot(Slot_Generator) - .toObject() - .as<AsyncGeneratorGeneratorObject>(); - } - static MOZ_MUST_USE bool enqueueRequest( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, Handle<AsyncGeneratorRequest*> request); static AsyncGeneratorRequest* dequeueRequest( JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); static AsyncGeneratorRequest* peekRequest(
--- a/js/src/vm/BytecodeUtil.cpp +++ b/js/src/vm/BytecodeUtil.cpp @@ -2059,17 +2059,16 @@ bool ExpressionDecompiler::decompilePC(j return write("JS_IS_CONSTRUCTING"); case JSOP_ITER: return write("ITER"); case JSOP_LAMBDA: case JSOP_LAMBDA_ARROW: case JSOP_TOASYNC: - case JSOP_TOASYNCGEN: return write("FUN"); case JSOP_TOASYNCITER: return write("ASYNCITER"); case JSOP_MOREITER: // For stack dump, defIndex == 0 is not used. MOZ_ASSERT(defIndex == 1);
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -217,18 +217,16 @@ static const Class DebuggerSource_class * even though their actual code is indeed JavaScript. Debugger should treat * async functions and generators like any other scripted function, so we must * carefully check for them whenever we want inspect a function. */ static JSFunction* RemoveAsyncWrapper(JSFunction* fun) { if (js::IsWrappedAsyncFunction(fun)) { fun = js::GetUnwrappedAsyncFunction(fun); - } else if (js::IsWrappedAsyncGenerator(fun)) { - fun = js::GetUnwrappedAsyncGenerator(fun); } return fun; } static inline bool EnsureFunctionHasScript(JSContext* cx, HandleFunction fun) { if (fun->isInterpretedLazy()) { AutoRealm ar(cx, fun); @@ -11197,18 +11195,17 @@ bool DebuggerObject::isAsyncFunction() c MOZ_ASSERT(isDebuggeeFunction()); return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isAsync(); } bool DebuggerObject::isGeneratorFunction() const { MOZ_ASSERT(isDebuggeeFunction()); - JSFunction* fun = RemoveAsyncWrapper(&referent()->as<JSFunction>()); - return fun->isGenerator(); + return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isGenerator(); } bool DebuggerObject::isGlobal() const { return referent()->is<GlobalObject>(); } bool DebuggerObject::isScriptedProxy() const { return js::IsScriptedProxy(referent()); }
--- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -3638,17 +3638,17 @@ bool js::InitFunctionEnvironmentObjects( MOZ_ASSERT(frame.isFunctionFrame()); MOZ_ASSERT(frame.callee()->needsSomeEnvironmentObject()); RootedFunction callee(cx, frame.callee()); // Named lambdas may have an environment that holds itself for recursion. if (callee->needsNamedLambdaEnvironment()) { NamedLambdaObject* declEnv; - if (callee->isAsync()) { + if (callee->isAsync() && !callee->isGenerator()) { // Named async function needs special environment to return // wrapped function for the binding. RootedFunction fun(cx, GetWrappedAsyncFunction(callee)); declEnv = NamedLambdaObject::create(cx, frame, fun); } else { declEnv = NamedLambdaObject::create(cx, frame); } if (!declEnv) {
--- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -2,16 +2,17 @@ * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "vm/GeneratorObject.h" #include "js/PropertySpec.h" +#include "vm/AsyncIteration.h" #include "vm/JSObject.h" #include "vm/ArrayObject-inl.h" #include "vm/Debugger-inl.h" #include "vm/JSAtom-inl.h" #include "vm/JSScript-inl.h" #include "vm/NativeObject-inl.h" #include "vm/Stack-inl.h" @@ -19,54 +20,29 @@ using namespace js; JSObject* AbstractGeneratorObject::create(JSContext* cx, AbstractFramePtr frame) { MOZ_ASSERT(frame.isGeneratorFrame()); MOZ_ASSERT(frame.script()->nfixed() == 0); MOZ_ASSERT(!frame.isConstructing()); - Rooted<GlobalObject*> global(cx, cx->global()); - - RootedValue pval(cx); RootedFunction fun(cx, frame.callee()); Rooted<AbstractGeneratorObject*> genObj(cx); if (!fun->isAsync()) { - MOZ_ASSERT(fun->isGenerator()); - - // FIXME: This would be faster if we could avoid doing a lookup to get - // the prototype for the instance. Bug 906600. - if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) { - return nullptr; - } - RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr); - if (!proto) { - proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global); - if (!proto) { - return nullptr; - } - } - - genObj = NewObjectWithGivenProto<GeneratorObject>(cx, proto); - if (!genObj) { - return nullptr; - } + genObj = GeneratorObject::create(cx, fun); } else if (fun->isGenerator()) { - RootedObject proto(cx, nullptr); - genObj = NewObjectWithGivenProto<AsyncGeneratorGeneratorObject>(cx, proto); - if (!genObj) { - return nullptr; - } + genObj = AsyncGeneratorObject::create(cx, fun); } else { RootedObject proto(cx, nullptr); genObj = NewObjectWithGivenProto<AsyncFunctionGeneratorObject>(cx, proto); - if (!genObj) { - return nullptr; - } + } + if (!genObj) { + return nullptr; } genObj->setCallee(*frame.callee()); genObj->setEnvironmentChain(*frame.environmentChain()); if (frame.script()->needsArgsObj()) { genObj->setArgsObj(frame.argsObj()); } genObj->clearExpressionStack(); @@ -213,32 +189,47 @@ bool AbstractGeneratorObject::resume(JSC activation.regs().sp++; MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth())); activation.regs().sp[-1] = arg; genObj->setRunning(); return true; } +GeneratorObject* GeneratorObject::create(JSContext* cx, HandleFunction fun) { + MOZ_ASSERT(fun->isGenerator() && !fun->isAsync()); + + // FIXME: This would be faster if we could avoid doing a lookup to get + // the prototype for the instance. Bug 906600. + RootedValue pval(cx); + if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) { + return nullptr; + } + RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr); + if (!proto) { + proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, cx->global()); + if (!proto) { + return nullptr; + } + } + return NewObjectWithGivenProto<GeneratorObject>(cx, proto); +} + const Class GeneratorObject::class_ = { "Generator", JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS)}; static const JSFunctionSpec generator_methods[] = { JS_SELF_HOSTED_FN("next", "GeneratorNext", 1, 0), JS_SELF_HOSTED_FN("throw", "GeneratorThrow", 1, 0), JS_SELF_HOSTED_FN("return", "GeneratorReturn", 1, 0), JS_FS_END}; const Class AsyncFunctionGeneratorObject::class_ = { "AsyncFunctionGenerator", JSCLASS_HAS_RESERVED_SLOTS(AsyncFunctionGeneratorObject::RESERVED_SLOTS)}; -const Class AsyncGeneratorGeneratorObject::class_ = { - "AsyncGeneratorGenerator", - JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorGeneratorObject::RESERVED_SLOTS)}; - JSObject* js::NewSingletonObjectWithFunctionPrototype( JSContext* cx, Handle<GlobalObject*> global) { RootedObject proto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global)); if (!proto) { return nullptr; } return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject); @@ -329,10 +320,10 @@ bool AbstractGeneratorObject::isAfterYie code[offset] == JSOP_AWAIT); return code[offset] == op; } template <> bool JSObject::is<js::AbstractGeneratorObject>() const { return is<GeneratorObject>() || is<AsyncFunctionGeneratorObject>() || - is<AsyncGeneratorGeneratorObject>(); + is<AsyncGeneratorObject>(); }
--- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -201,32 +201,27 @@ class AbstractGeneratorObject : public N } }; class GeneratorObject : public AbstractGeneratorObject { public: enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS }; static const Class class_; + + static GeneratorObject* create(JSContext* cx, HandleFunction fun); }; class AsyncFunctionGeneratorObject : public AbstractGeneratorObject { public: enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS }; static const Class class_; }; -class AsyncGeneratorGeneratorObject : public AbstractGeneratorObject { - public: - enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS }; - - static const Class class_; -}; - bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame, Handle<AbstractGeneratorObject*> obj, HandleValue val, uint32_t resumeKind); /** * Return the generator object associated with the given frame. The frame must * be a call frame for a generator. If the generator object hasn't been created * yet, or hasn't been stored in the stack slot yet, this returns null.
--- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -407,19 +407,19 @@ inline int32_t GlobalObject::OffThreadPl /* static */ JSObject* GlobalObject::createOffThreadObject( JSContext* cx, Handle<GlobalObject*> global, unsigned slot) { // Don't create prototype objects for off-thread parse globals. Instead // create a placeholder object which we can use to find the real prototype // when the off-thread compartment is merged back into the target // compartment. MOZ_ASSERT(global->zone()->createdForHelperThread()); - MOZ_ASSERT(slot == GENERATOR_FUNCTION_PROTO || slot == MODULE_PROTO || - slot == IMPORT_ENTRY_PROTO || slot == EXPORT_ENTRY_PROTO || - slot == REQUESTED_MODULE_PROTO); + MOZ_ASSERT(slot == GENERATOR_FUNCTION_PROTO || slot == ASYNC_GENERATOR || + slot == MODULE_PROTO || slot == IMPORT_ENTRY_PROTO || + slot == EXPORT_ENTRY_PROTO || slot == REQUESTED_MODULE_PROTO); auto placeholder = OffThreadPlaceholderObject::New(cx, slot); if (!placeholder) { return nullptr; } global->setSlot(slot, ObjectValue(*placeholder)); return placeholder;
--- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -741,16 +741,20 @@ static bool EnsureParserCreatedClasses(J if (!EnsureConstructor(cx, global, JSProto_RegExp)) { return false; // needed by regular expression literals } if (!GlobalObject::initGenerators(cx, global)) { return false; // needed by function*() {} } + if (!GlobalObject::initAsyncGenerators(cx, global)) { + return false; // needed by async function*() {} + } + if (kind == ParseTaskKind::Module && !GlobalObject::ensureModulePrototypesCreated(cx, global)) { return false; } return true; }
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1942,16 +1942,17 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_ } /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) CASE(JSOP_TRY_DESTRUCTURING) CASE(JSOP_UNUSED71) CASE(JSOP_UNUSED151) + CASE(JSOP_UNUSED192) CASE(JSOP_TRY) CASE(JSOP_CONDSWITCH) { MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1); ADVANCE_AND_DISPATCH(1); } CASE(JSOP_JUMPTARGET) CASE(JSOP_LOOPHEAD) { @@ -3588,28 +3589,16 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_ if (!wrapped) { goto error; } REGS.sp[-1].setObject(*wrapped); } END_CASE(JSOP_TOASYNC) - CASE(JSOP_TOASYNCGEN) { - ReservedRooted<JSFunction*> unwrapped( - &rootFunction0, ®S.sp[-1].toObject().as<JSFunction>()); - JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped); - if (!wrapped) { - goto error; - } - - REGS.sp[-1].setObject(*wrapped); - } - END_CASE(JSOP_TOASYNCGEN) - CASE(JSOP_TOASYNCITER) { ReservedRooted<Value> nextMethod(&rootValue0, REGS.sp[-1]); ReservedRooted<JSObject*> iter(&rootObject1, ®S.sp[-2].toObject()); JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter, nextMethod); if (!asyncIter) { goto error; }
--- a/js/src/vm/JSFunction.cpp +++ b/js/src/vm/JSFunction.cpp @@ -285,20 +285,21 @@ bool CallerGetterImpl(JSContext* cx, con ++iter; } if (iter.done() || !iter.isFunctionFrame()) { args.rval().setNull(); return true; } - RootedObject caller(cx, iter.callee(cx)); - if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync()) { - caller = GetWrappedAsyncFunction(&caller->as<JSFunction>()); + JSFunction* callerFun = iter.callee(cx); + if (callerFun->isAsync() && !callerFun->isGenerator()) { + callerFun = GetWrappedAsyncFunction(callerFun); } + RootedObject caller(cx, callerFun); if (!cx->compartment()->wrap(cx, &caller)) { return false; } // Censor the caller if we don't have full access to it. If we do, but the // caller is a function with strict mode code, throw a TypeError per ES5. // If we pass these checks, we can return the computed caller. { @@ -312,18 +313,16 @@ bool CallerGetterImpl(JSContext* cx, con JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); return false; } JSFunction* callerFun = &callerObj->as<JSFunction>(); if (IsWrappedAsyncFunction(callerFun)) { callerFun = GetUnwrappedAsyncFunction(callerFun); - } else if (IsWrappedAsyncGenerator(callerFun)) { - callerFun = GetUnwrappedAsyncGenerator(callerFun); } MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); if (callerFun->strict()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CALLER_IS_STRICT); return false; @@ -360,36 +359,33 @@ static bool CallerSetter(JSContext* cx, static const JSPropertySpec function_properties[] = { JS_PSGS("arguments", ArgumentsGetter, ArgumentsSetter, 0), JS_PSGS("caller", CallerGetter, CallerSetter, 0), JS_PS_END}; static bool ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id) { - bool isAsyncGenerator = IsWrappedAsyncGenerator(fun); - - MOZ_ASSERT_IF(!isAsyncGenerator, - fun->isInterpreted() || fun->isAsmJSNative()); + MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative()); MOZ_ASSERT(id == NameToId(cx->names().prototype)); // Assert that fun is not a compiler-created function object, which // must never leak to script or embedding code and then be mutated. // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above. - MOZ_ASSERT_IF(!isAsyncGenerator, !IsInternalFunctionObject(*fun)); + MOZ_ASSERT(!IsInternalFunctionObject(*fun)); MOZ_ASSERT(!fun->isBoundFunction()); // Make the prototype object an instance of Object with the same parent as // the function object itself, unless the function is an ES6 generator. In // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is // the GeneratorObjectPrototype singleton. bool isGenerator = fun->isGenerator(); Rooted<GlobalObject*> global(cx, &fun->global()); RootedObject objProto(cx); - if (isAsyncGenerator) { + if (isGenerator && fun->isAsync()) { objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global); } else if (isGenerator) { objProto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global); } else { objProto = GlobalObject::getOrCreateObjectPrototype(cx, global); } if (!objProto) { return false; @@ -400,17 +396,17 @@ static bool ResolveInterpretedFunctionPr if (!proto) { return false; } // Per ES5 13.2 the prototype's .constructor property is configurable, // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft, // section 15.19.3, the .prototype of a generator function does not link // back with a .constructor. - if (!isGenerator && !isAsyncGenerator) { + if (!isGenerator) { RootedValue objVal(cx, ObjectValue(*fun)); if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0)) { return false; } } // Per ES5 15.3.5.2 a user-defined function's .prototype property is // initially non-configurable, non-enumerable, and writable. @@ -432,22 +428,19 @@ bool JSFunction::needsPrototypeProperty( * ES6 9.2.8 MakeConstructor defines the .prototype property on constructors. * Generators are not constructors, but they have a .prototype property * anyway, according to errata to ES6. See bug 1191486. * * Thus all of the following don't get a .prototype property: * - Methods (that are not class-constructors or generators) * - Arrow functions * - Function.prototype + * - Async functions */ - if (isBuiltin()) { - return IsWrappedAsyncGenerator(this); - } - - return isConstructor() || isGenerator() || isAsync(); + return !isBuiltin() && (isConstructor() || isGenerator()); } static bool fun_mayResolve(const JSAtomState& names, jsid id, JSObject*) { if (!JSID_IS_ATOM(id)) { return false; } JSAtom* atom = JSID_TO_ATOM(id); @@ -541,19 +534,20 @@ static bool fun_resolve(JSContext* cx, H template <XDRMode mode> XDRResult js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSourceObject sourceObject, MutableHandleFunction objp) { enum FirstWordFlag { HasAtom = 0x1, - HasGeneratorProto = 0x2, - IsLazy = 0x4, - HasSingletonType = 0x8 + IsGenerator = 0x2, + IsAsync = 0x4, + IsLazy = 0x8, + HasSingletonType = 0x10 }; /* NB: Keep this in sync with CloneInnerInterpretedFunction. */ RootedAtom atom(xdr->cx()); uint32_t firstword = 0; /* bitmask of FirstWordFlag */ uint32_t flagsword = 0; /* word for argument count and fun->flags */ JSContext* cx = xdr->cx(); @@ -567,18 +561,22 @@ XDRResult js::XDRInterpretedFunction(XDR return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun); } if (fun->explicitName() || fun->hasInferredName() || fun->hasGuessedAtom()) { firstword |= HasAtom; } - if (fun->isGenerator() || fun->isAsync()) { - firstword |= HasGeneratorProto; + if (fun->isGenerator()) { + firstword |= IsGenerator; + } + + if (fun->isAsync()) { + firstword |= IsAsync; } if (fun->isInterpretedLazy()) { // Encode a lazy script. firstword |= IsLazy; lazy = fun->lazyScript(); } else { // Encode the script. @@ -610,19 +608,23 @@ XDRResult js::XDRInterpretedFunction(XDR if (firstword & HasAtom) { MOZ_TRY(XDRAtom(xdr, &atom)); } MOZ_TRY(xdr->codeUint32(&flagsword)); if (mode == XDR_DECODE) { RootedObject proto(cx); - if (firstword & HasGeneratorProto) { - proto = - GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global()); + if ((firstword & IsGenerator) || (firstword & IsAsync)) { + if ((firstword & IsGenerator) && (firstword & IsAsync)) { + proto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()); + } else { + proto = GlobalObject::getOrCreateGeneratorFunctionPrototype( + cx, cx->global()); + } if (!proto) { return xdr->fail(JS::TranscodeResult_Throw); } } gc::AllocKind allocKind = gc::AllocKind::FUNCTION; if (uint16_t(flagsword) & JSFunction::EXTENDED) { allocKind = gc::AllocKind::FUNCTION_EXTENDED; @@ -878,20 +880,16 @@ JSString* js::FunctionToString(JSContext if (IsAsmJSFunction(fun)) { return AsmJSFunctionToString(cx, fun); } if (IsWrappedAsyncFunction(fun)) { RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun)); return FunctionToString(cx, unwrapped, isToSource); } - if (IsWrappedAsyncGenerator(fun)) { - RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun)); - return FunctionToString(cx, unwrapped, isToSource); - } RootedScript script(cx); if (fun->hasScript()) { script = fun->nonLazyScript(); } // Default class constructors are self-hosted, but have their source // objects overridden to refer to the span of the class statement or @@ -1910,34 +1908,39 @@ static bool CreateDynamicFunction(JSCont * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, * and so would a call to f from another top-level's script or function. */ RootedAtom anonymousAtom(cx, cx->names().anonymous); // Initialize the function with the default prototype: // Leave as nullptr to get the default from clasp for normal functions. // Use %Generator% for generators and the unwrapped function of async - // functions and async generators. + // functions. Use %AsyncGenerator% for async generator functions. RootedObject defaultProto(cx); if (isGenerator || isAsync) { - defaultProto = - GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, global); + if (isGenerator && isAsync) { + defaultProto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()); + } else { + defaultProto = + GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, global); + } if (!defaultProto) { return false; } } // Step 30-37 (reordered). RootedObject globalLexical(cx, &global->lexicalEnvironment()); JSFunction::Flags flags = (isGenerator || isAsync) ? JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC : JSFunction::INTERPRETED_LAMBDA; - gc::AllocKind allocKind = - isAsync ? gc::AllocKind::FUNCTION_EXTENDED : gc::AllocKind::FUNCTION; + gc::AllocKind allocKind = (isAsync && !isGenerator) + ? gc::AllocKind::FUNCTION_EXTENDED + : gc::AllocKind::FUNCTION; RootedFunction fun( cx, NewFunctionWithProto(cx, nullptr, 0, flags, globalLexical, anonymousAtom, defaultProto, allocKind, TenuredObject)); if (!fun) { return false; } @@ -1989,27 +1992,22 @@ static bool CreateDynamicFunction(JSCont } // Steps 6, 29. RootedObject proto(cx); if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) { return false; } - if (isAsync) { + if (isAsync && !isGenerator) { // Create the async function wrapper. - JSObject* wrapped; - if (isGenerator) { - wrapped = proto ? WrapAsyncGeneratorWithProto(cx, fun, proto) - : WrapAsyncGenerator(cx, fun); - } else { - // Step 9.d, use %AsyncFunctionPrototype% as the fallback prototype. - wrapped = proto ? WrapAsyncFunctionWithProto(cx, fun, proto) - : WrapAsyncFunction(cx, fun); - } + + // Step 9.d, use %AsyncFunctionPrototype% as the fallback prototype. + JSObject* wrapped = proto ? WrapAsyncFunctionWithProto(cx, fun, proto) + : WrapAsyncFunction(cx, fun); if (!wrapped) { return false; } fun = &wrapped->as<JSFunction>(); } else { // Steps 7.d, 8.d (implicit). // Call SetPrototype if an explicit prototype was given. @@ -2194,18 +2192,22 @@ bool js::CanReuseScriptForClone(JS::Real } static inline JSFunction* NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind, gc::AllocKind allocKind, HandleObject proto) { RootedObject cloneProto(cx, proto); if (!proto && (fun->isGenerator() || fun->isAsync())) { - cloneProto = - GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global()); + if (fun->isGenerator() && fun->isAsync()) { + cloneProto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()); + } else { + cloneProto = + GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global()); + } if (!cloneProto) { return nullptr; } } RootedFunction clone(cx); clone = NewObjectWithClassProto<JSFunction>(cx, cloneProto, allocKind, newKind);
--- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -3951,18 +3951,22 @@ void js::DescribeScriptedCallerForCompil } static JSObject* CloneInnerInterpretedFunction( JSContext* cx, HandleScope enclosingScope, HandleFunction srcFun, Handle<ScriptSourceObject*> sourceObject) { /* NB: Keep this in sync with XDRInterpretedFunction. */ RootedObject cloneProto(cx); if (srcFun->isGenerator() || srcFun->isAsync()) { - cloneProto = - GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global()); + if (srcFun->isGenerator() && srcFun->isAsync()) { + cloneProto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()); + } else { + cloneProto = + GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global()); + } if (!cloneProto) { return nullptr; } } gc::AllocKind allocKind = srcFun->getAllocKind(); uint16_t flags = srcFun->flags(); if (srcFun->isSelfHostedBuiltin()) {
--- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2062,25 +2062,18 @@ * * Category: Variables and Scopes * Type: This * Operands: * Stack: this => this */ \ MACRO(JSOP_CHECKTHISREINIT, 191, "checkthisreinit", NULL, 1, 1, 1, JOF_BYTE) \ /* - * Pops the top of stack value as 'unwrapped', converts it to async - * generator 'wrapped', and pushes 'wrapped' back on the stack. - * - * Category: Statements - * Type: Generator - * Operands: - * Stack: unwrapped => wrapped */ \ - MACRO(JSOP_TOASYNCGEN, 192, "toasyncgen", NULL, 1, 1, 1, JOF_BYTE) \ + MACRO(JSOP_UNUSED192, 192, "unused192", NULL, 1, 0, 0, JOF_BYTE) \ /* * Pops the top two values on the stack as 'propval' and 'obj', pushes * 'propval' property of 'obj' onto the stack. * * Like JSOP_GETELEM but for call context. * * Category: Literals * Type: Object
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -38,16 +38,17 @@ #include "js/CharacterEncoding.h" #include "js/CompilationAndEvaluation.h" #include "js/Date.h" #include "js/PropertySpec.h" #include "js/StableStringChars.h" #include "js/Wrapper.h" #include "util/StringBuffer.h" #include "vm/ArgumentsObject.h" +#include "vm/AsyncIteration.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" #include "vm/Interpreter.h" #include "vm/Iteration.h" #include "vm/JSContext.h" #include "vm/JSFunction.h" #include "vm/JSObject.h" #include "vm/PIC.h" @@ -2555,18 +2556,18 @@ static const JSFunctionSpec intrinsic_fu JS_FN("GeneratorObjectIsClosed", intrinsic_GeneratorObjectIsClosed, 1, 0), JS_FN("IsSuspendedGenerator", intrinsic_IsSuspendedGenerator, 1, 0), JS_FN("GeneratorIsRunning", intrinsic_GeneratorIsRunning, 1, 0), JS_FN("GeneratorSetClosed", intrinsic_GeneratorSetClosed, 1, 0), JS_FN("IsAsyncFunctionGeneratorObject", intrinsic_IsInstanceOfBuiltin<AsyncFunctionGeneratorObject>, 1, 0), - JS_FN("IsAsyncGeneratorGeneratorObject", - intrinsic_IsInstanceOfBuiltin<AsyncGeneratorGeneratorObject>, 1, 0), + JS_FN("IsAsyncGeneratorObject", + intrinsic_IsInstanceOfBuiltin<AsyncGeneratorObject>, 1, 0), JS_INLINABLE_FN("GuardToArrayBuffer", intrinsic_GuardToBuiltin<ArrayBufferObject>, 1, 0, IntrinsicGuardToArrayBuffer), JS_INLINABLE_FN("GuardToSharedArrayBuffer", intrinsic_GuardToBuiltin<SharedArrayBufferObject>, 1, 0, IntrinsicGuardToSharedArrayBuffer), JS_FN("IsWrappedArrayBuffer",