diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 7c5156ffbed..8d45c370dc2 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -284,6 +284,7 @@ selfhosting:: selfhosted.out.h selfhosting_srcs := \ $(srcdir)/builtin/Utilities.js \ $(srcdir)/builtin/Array.js \ + $(srcdir)/builtin/AsyncFunctions.js \ $(srcdir)/builtin/Date.js \ $(srcdir)/builtin/Error.js \ $(srcdir)/builtin/Generator.js \ diff --git a/js/src/builtin/AsyncFunctions.js b/js/src/builtin/AsyncFunctions.js new file mode 100644 index 00000000000..0f918d3a9f9 --- /dev/null +++ b/js/src/builtin/AsyncFunctions.js @@ -0,0 +1,46 @@ +/* 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/. */ + +function AsyncFunction_wrap(genFunction) { + var wrapper = function() { + // The try block is required to handle throws in default arguments properly. + try { + return AsyncFunction_start(callFunction(std_Function_apply, genFunction, this, arguments)); + } catch (e) { + return Promise_reject(e); + } + }; + SetFunctionExtendedSlot(genFunction, 1, wrapper); + var name = GetFunName(genFunction); + if (typeof name !== 'undefined') + SetFunName(wrapper, name); + return wrapper; +} + +function AsyncFunction_start(generator) { + return AsyncFunction_resume(generator, undefined, generator.next); +} + +function AsyncFunction_resume(gen, v, method) { + let result; + try { + // get back into async function, run to next await point + result = callFunction(method, gen, v); + } catch (exc) { + // The async function itself failed. + return Promise_reject(exc); + } + + if (result.done) + return Promise_resolve(result.value); + + // If we get here, `await` occurred. `gen` is paused at a yield point. + return callFunction(Promise_then, + Promise_resolve(result.value), + function(val) { + return AsyncFunction_resume(gen, val, gen.next); + }, function (err) { + return AsyncFunction_resume(gen, err, gen.throw); + }); +} diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 5dad148c7cc..e53ea56fb1c 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -240,7 +240,6 @@ bool BytecodeEmitter::emit1(JSOp op) { MOZ_ASSERT(checkStrictOrSloppy(op)); - ptrdiff_t offset; if (!emitCheck(1, &offset)) return false; @@ -1991,7 +1990,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_PREDECREMENT: case PNK_POSTDECREMENT: case PNK_THROW: - case PNK_AWAIT: MOZ_ASSERT(pn->isArity(PN_UNARY)); *answer = true; return true; @@ -2013,6 +2011,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_YIELD_STAR: case PNK_YIELD: + case PNK_AWAIT: MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; @@ -5858,6 +5857,16 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA); pn->setOp(JSOP_FUNWITHPROTO); } + + if (funbox->isAsync()) + return emitAsyncWrapper(index, funbox->needsHomeObject()); + + if (pn->getOp() == JSOP_DEFFUN) { + if (!emitIndex32(JSOP_LAMBDA, index)) + return false; + return emit1(JSOP_DEFFUN); + } + return emitIndex32(pn->getOp(), index); } @@ -5877,7 +5886,14 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) MOZ_ASSERT(pn->getOp() == JSOP_NOP); MOZ_ASSERT(atBodyLevel()); switchToPrologue(); - if (!emitIndex32(JSOP_DEFFUN, index)) + if (funbox->isAsync()) { + if (!emitAsyncWrapper(index, fun->isMethod())) + return false; + } else { + if (!emitIndex32(JSOP_LAMBDA, index)) + return false; + } + if (!emit1(JSOP_DEFFUN)) return false; if (!updateSourceCoordNotes(pn->pn_pos.begin)) return false; @@ -5891,7 +5907,11 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) bi->kind() == Binding::ARGUMENT); MOZ_ASSERT(bi.argOrLocalIndex() < JS_BIT(20)); #endif - if (!emitIndexOp(JSOP_LAMBDA, index)) + if (funbox->isAsync()) { + if (!emitAsyncWrapper(index, false)) + return false; + } + else if (!emitIndexOp(JSOP_LAMBDA, index)) return false; MOZ_ASSERT(pn->getOp() == JSOP_GETLOCAL || pn->getOp() == JSOP_GETARG); JSOp setOp = pn->getOp() == JSOP_GETLOCAL ? JSOP_SETLOCAL : JSOP_SETARG; @@ -5904,6 +5924,30 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) return true; } +bool +BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject) { + JSAtom* atom = Atomize(cx, "AsyncFunction_wrap", 18); + if (!atom) + return false; + /* TODO Comment */ + if (needsHomeObject && !emitIndex32(JSOP_LAMBDA, index)) + return false; + if (!emitAtomOp(atom, JSOP_GETINTRINSIC)) + return false; + if (!emit1(JSOP_UNDEFINED)) + return false; + if (needsHomeObject) { + if (!emitDupAt(2)) + return false; + } else { + if (!emitIndex32(JSOP_LAMBDA, index)) + return false; + } + if (!emitCall(JSOP_CALL, 1)) + return false; + return true; +} + bool BytecodeEmitter::emitDo(ParseNode* pn) { @@ -7070,7 +7114,12 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, propdef->pn_right->pn_funbox->needsHomeObject()) { MOZ_ASSERT(propdef->pn_right->pn_funbox->function()->allowSuperProperty()); - if (!emit2(JSOP_INITHOMEOBJECT, isIndex)) + bool isAsync = propdef->pn_right->pn_funbox->isAsync(); + if (isAsync && !emit1(JSOP_SWAP)) + return false; + if (!emit2(JSOP_INITHOMEOBJECT, isIndex + isAsync)) + return false; + if (isAsync && !emit1(JSOP_POP)) return false; } @@ -7689,6 +7738,7 @@ BytecodeEmitter::emitTree(ParseNode* pn) break; case PNK_YIELD: + case PNK_AWAIT: ok = emitYield(pn); break; @@ -8022,13 +8072,6 @@ BytecodeEmitter::emitTree(ParseNode* pn) return false; break; - // PNK_AWAIT handling is not yet implemented (in this part), - // so currently we just return "true" as a placeholder. - case PNK_AWAIT: - if (!emit1(JSOP_TRUE)) - return false; - break; - case PNK_POSHOLDER: MOZ_ASSERT_UNREACHABLE("Should never try to emit PNK_POSHOLDER"); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 44fe9cb0646..25afd64df7a 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -482,6 +482,8 @@ struct BytecodeEmitter bool emitPropOp(ParseNode* pn, JSOp op); bool emitPropIncDec(ParseNode* pn); + bool emitAsyncWrapper(unsigned index, bool isMethod); + bool emitComputedPropertyName(ParseNode* computedPropName); // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 8bd153bf2b1..1fb654143a5 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2496,15 +2496,15 @@ static const VMFunction DefFunOperationInfo = FunctionInfo(De bool BaselineCompiler::emit_JSOP_DEFFUN() { - RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); - frame.syncStack(0); - masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); + frame.popRegsAndSync(1); + masm.unboxObject(R0, R0.scratchReg()); + masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg()); prepareVMCall(); - pushArg(ImmGCPtr(fun)); pushArg(R0.scratchReg()); + pushArg(R1.scratchReg()); pushArg(ImmGCPtr(script)); return callVM(DefFunOperationInfo); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 2e167d0aa3d..5b694866962 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3737,8 +3737,9 @@ void CodeGenerator::visitDefFun(LDefFun* lir) { Register scopeChain = ToRegister(lir->scopeChain()); + Register fun = ToRegister(lir->fun()); - pushArg(ImmGCPtr(lir->mir()->fun())); + pushArg(fun); pushArg(scopeChain); pushArg(ImmGCPtr(current->mir()->info().script())); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 9dd58efe9e1..05220414063 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -12585,13 +12585,11 @@ IonBuilder::jsop_defvar(uint32_t index) bool IonBuilder::jsop_deffun(uint32_t index) { - JSFunction* fun = script()->getFunction(index); - if (fun->isNative() && IsAsmJSModuleNative(fun->native())) - return abort("asm.js module function"); - MOZ_ASSERT(analysis().usesScopeChain()); - MDefFun* deffun = MDefFun::New(alloc(), fun, current->scopeChain()); + MDefinition *funDef = current->pop(); + + MDefFun* deffun = MDefFun::New(alloc(), funDef, current->scopeChain()); current->add(deffun); return resumeAfter(deffun); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index bae8688a443..95ffc97efc0 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -156,7 +156,11 @@ LIRGenerator::visitDefVar(MDefVar* ins) void LIRGenerator::visitDefFun(MDefFun* ins) { - LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(ins->scopeChain())); + MDefinition* fun = ins->fun(); + MOZ_ASSERT(fun->type() == MIRType_Object); + + LDefFun* lir = new(alloc()) LDefFun(useRegisterAtStart(fun), + useRegisterAtStart(ins->scopeChain())); add(lir, ins); assignSafepoint(lir, ins); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 40779372ce9..2f7779ff14a 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -7319,29 +7319,26 @@ class MDefVar }; class MDefFun - : public MUnaryInstruction, - public NoTypePolicy::Data + : public MBinaryInstruction, + public ObjectPolicy<0>::Data { - CompilerFunction fun_; - private: - MDefFun(JSFunction* fun, MDefinition* scopeChain) - : MUnaryInstruction(scopeChain), - fun_(fun) + MDefFun(MDefinition* fun, MDefinition* scopeChain) + : MBinaryInstruction(fun, scopeChain) {} public: INSTRUCTION_HEADER(DefFun) - static MDefFun* New(TempAllocator& alloc, JSFunction* fun, MDefinition* scopeChain) { + static MDefFun* New(TempAllocator& alloc, MDefinition* fun, MDefinition* scopeChain) { return new(alloc) MDefFun(fun, scopeChain); } - JSFunction* fun() const { - return fun_; + MDefinition* fun() const { + return getOperand(0); } MDefinition* scopeChain() const { - return getOperand(0); + return getOperand(1); } bool possiblyCalls() const override { return true; diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 56f5416d97e..f4a77ef3034 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1228,19 +1228,23 @@ class LDefVar : public LCallInstructionHelper<0, 1, 0> } }; -class LDefFun : public LCallInstructionHelper<0, 1, 0> +class LDefFun : public LCallInstructionHelper<0, 2, 0> { public: LIR_HEADER(DefFun) - explicit LDefFun(const LAllocation& scopeChain) + LDefFun(const LAllocation& fun, const LAllocation& scopeChain) { - setOperand(0, scopeChain); + setOperand(0, fun); + setOperand(1, scopeChain); } - const LAllocation* scopeChain() { + const LAllocation* fun() { return getOperand(0); } + const LAllocation* scopeChain() { + return getOperand(1); + } MDefFun* mir() const { return mir_->toDefFun(); } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index e3bbc0d795b..5ff6ed85922 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -467,7 +467,7 @@ class JSFunction : public js::NativeObject } js::FunctionAsyncKind asyncKind() const { - return isInterpretedLazy() ? lazyScript()->asyncKind() : nonLazyScript()->asyncKind(); + return isInterpretedLazy() ? lazyScript()->asyncKind() : nonLazyScript()->asyncKind(); } bool isGenerator() const { return generatorKind() != js::NotGenerator; } diff --git a/js/src/tests/ecma_7/AsyncFunctions/1.1.1.js b/js/src/tests/ecma_7/AsyncFunctions/1.1.1.js new file mode 100644 index 00000000000..549646eee22 --- /dev/null +++ b/js/src/tests/ecma_7/AsyncFunctions/1.1.1.js @@ -0,0 +1,18 @@ +/* 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/. */ + +function assertThrowsSE(code) { + assertThrows(() => Reflect.parse(code), SyntaxError); +} + +assertThrowsSE("'use strict'; async function eval() {}"); +assertThrowsSE("'use strict'; async function arguments() {}"); +assertThrowsSE("async function a(k = super.prop) { }"); +assertThrowsSE("async function a() { super.prop(); }"); +assertThrowsSE("async function a() { super(); }"); + +assertThrowsSE("async function a(k = await 3) {}"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_7/AsyncFunctions/1.1.2.js b/js/src/tests/ecma_7/AsyncFunctions/1.1.2.js new file mode 100644 index 00000000000..c73e14df294 --- /dev/null +++ b/js/src/tests/ecma_7/AsyncFunctions/1.1.2.js @@ -0,0 +1,13 @@ +/* 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/. */ + +async function test() { } + +var anon = async function() { } + +assertEq(test.name, "test"); +assertEq(anon.name, ""); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_7/AsyncFunctions/methods.js b/js/src/tests/ecma_7/AsyncFunctions/methods.js new file mode 100644 index 00000000000..68adc4e9d35 --- /dev/null +++ b/js/src/tests/ecma_7/AsyncFunctions/methods.js @@ -0,0 +1,56 @@ +/* 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/. */ + +var Promise = ShellPromise; + +class X { + constructor() { + this.value = 42; + } + async getValue() { + return this.value; + } + setValue(value) { + this.value = value; + } + async increment() { + var value = await this.getValue(); + this.setValue(value + 1); + return this.getValue(); + } + async getBaseClassName() { + return 'X'; + } + static async getStaticValue() { + return 44; + } +} + +class Y extends X { + constructor() { } + async getBaseClassName() { + return super.getBaseClassName(); + } +} + +var objLiteral = { + async get() { + return 45; + }, + someStuff: 5 +}; + +var x = new X(); +var y = new Y(); + +Promise.all([ + assertEventuallyEq(x.getValue(), 42), + assertEventuallyEq(x.increment(), 43), + assertEventuallyEq(X.getStaticValue(), 44), + assertEventuallyEq(objLiteral.get(), 45), + assertEventuallyEq(y.getBaseClassName(), 'X'), +]).then(() => { + if (typeof reportCompare === "function") + reportCompare(true, true); +}); diff --git a/js/src/tests/ecma_7/AsyncFunctions/semantics.js b/js/src/tests/ecma_7/AsyncFunctions/semantics.js new file mode 100644 index 00000000000..311d71cf12a --- /dev/null +++ b/js/src/tests/ecma_7/AsyncFunctions/semantics.js @@ -0,0 +1,168 @@ +/* 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/. */ + +var Promise = ShellPromise; + +async function empty() { + +} + +async function simpleReturn() { + return 1; +} + +async function simpleAwait() { + var result = await 2; + return result; +} + +async function simpleAwaitAsync() { + var result = await simpleReturn(); + return 2 + result; +} + +async function returnOtherAsync() { + return 1 + await simpleAwaitAsync(); +} + +async function simpleThrower() { + throw new Error(); +} + +async function delegatedThrower() { + var val = await simpleThrower(); + return val; +} + +async function tryCatch() { + try { + await delegatedThrower(); + return 'FAILED'; + } catch (_) { + return 5; + } +} + +async function tryCatchThrow() { + try { + await delegatedThrower(); + return 'FAILED'; + } catch (_) { + return delegatedThrower(); + } +} + +async function wellFinally() { + try { + await delegatedThrower(); + } catch (_) { + return 'FAILED'; + } finally { + return 6; + } +} + +async function finallyMayFail() { + try { + await delegatedThrower(); + } catch (_) { + return 5; + } finally { + return delegatedThrower(); + } +} + +async function embedded() { + async function inner() { + return 7; + } + return await inner(); +} + +// recursion, it works! +async function fib(n) { + return (n == 0 || n == 1) ? n : await fib(n - 1) + await fib(n - 2); +} + +// mutual recursion +async function isOdd(n) { + async function isEven(n) { + return n === 0 || await isOdd(n - 1); + } + return n !== 0 && await isEven(n - 1); +} + +// recursion, take three! +var hardcoreFib = async function fib2(n) { + return (n == 0 || n == 1) ? n : await fib2(n - 1) + await fib2(n - 2); +} + +var asyncExpr = async function() { + return 10; +} + +var namedAsyncExpr = async function simple() { + return 11; +} + +async function executionOrder() { + var value = 0; + async function first() { + return (value = value === 0 ? 1 : value); + } + async function second() { + return (value = value === 0 ? 2 : value); + } + async function third() { + return (value = value === 0 ? 3 : value); + } + return await first() + await second() + await third() + 6; +} + +async function miscellaneous() { + if (arguments.length === 3 && + arguments.callee.name === "miscellaneous") + return 14; +} + +function thrower() { + throw 15; +} + +async function defaultArgs(arg = thrower()) { + +} + +// Async functions are not constructible +assertThrows(() => { + async function Person() { + + } + new Person(); +}, TypeError); + +Promise.all([ + assertEventuallyEq(empty(), undefined), + assertEventuallyEq(simpleReturn(), 1), + assertEventuallyEq(simpleAwait(), 2), + assertEventuallyEq(simpleAwaitAsync(), 3), + assertEventuallyEq(returnOtherAsync(), 4), + assertEventuallyThrows(simpleThrower(), Error), + assertEventuallyEq(tryCatch(), 5), + assertEventuallyThrows(tryCatchThrow(), Error), + assertEventuallyEq(wellFinally(), 6), + assertEventuallyThrows(finallyMayFail(), Error), + assertEventuallyEq(embedded(), 7), + assertEventuallyEq(fib(6), 8), + assertEventuallyEq(executionOrder(), 9), + assertEventuallyEq(asyncExpr(), 10), + assertEventuallyEq(namedAsyncExpr(), 11), + assertEventuallyEq(isOdd(12).then(v => v ? "oops" : 12), 12), + assertEventuallyEq(hardcoreFib(7), 13), + assertEventuallyEq(miscellaneous(1, 2, 3), 14), + assertEventuallyEq(defaultArgs().catch(e => e), 15) +]).then(() => { + if (typeof reportCompare === "function") + reportCompare(true, true); +}); diff --git a/js/src/tests/ecma_7/AsyncFunctions/shell.js b/js/src/tests/ecma_7/AsyncFunctions/shell.js index e69de29bb2d..3d9550cd9a7 100644 --- a/js/src/tests/ecma_7/AsyncFunctions/shell.js +++ b/js/src/tests/ecma_7/AsyncFunctions/shell.js @@ -0,0 +1,27 @@ +/* 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/. */ + +/** + * These methods are inspired by chai-as-promised library for promise testing + * in JS applications. They check if promises eventually resolve to a given value + * or are rejected with a specified error type. + */ + +if (typeof assertEventuallyEq === 'undefined') { + assertEventuallyEq = function(promise, expected) { + return promise.then(actual => assertEq(actual, expected)); + }; +} + +if (typeof assertEventuallyThrows === 'undefined') { + assertEventuallyThrows = function(promise, expectedErrorType) { + return promise.catch(actualE => assertEq(actualE instanceof expectedErrorType, true)); + }; +} + +if (typeof assertEventuallyDeepEq === 'undefined') { + assertEventuallyDeepEq = function(promise, expected) { + return promise.then(actual => assertDeepEq(actual, expected)); + }; +} diff --git a/js/src/tests/ecma_7/AsyncFunctions/syntax-modules.js b/js/src/tests/ecma_7/AsyncFunctions/syntax-modules.js new file mode 100644 index 00000000000..cce6d15c382 --- /dev/null +++ b/js/src/tests/ecma_7/AsyncFunctions/syntax-modules.js @@ -0,0 +1,8 @@ +parseModule("async function f() { await 3; }"); +parseModule("async function f() { await 3; }"); +assertThrows(() => parseModule("var await = 5;"), SyntaxError); +assertThrows(() => parseModule("export var await;"), SyntaxError); +assertThrows(() => parseModule("async function f() { function g() { await 3; } }"), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 5a32a10e138..061a4850fb7 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3507,17 +3507,18 @@ CASE(JSOP_DEFVAR) } END_CASE(JSOP_DEFVAR) -CASE(JSOP_DEFFUN) -{ +CASE(JSOP_DEFFUN) { /* * A top-level function defined in Global or Eval code (see ECMA-262 * Ed. 3), or else a SpiderMonkey extension: a named function statement in * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - ReservedRooted fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); + MOZ_ASSERT(REGS.sp[-1].isObject()); + ReservedRooted fun(&rootFunction0, ®S.sp[-1].toObject().as()); if (!DefFunOperation(cx, script, REGS.fp()->scopeChain(), fun)) goto error; + REGS.sp--; } END_CASE(JSOP_DEFFUN) @@ -4321,14 +4322,6 @@ js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject scopeChain, * requests in server-side JS. */ RootedFunction fun(cx, funArg); - if (fun->isNative() || fun->environment() != scopeChain) { - fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, nullptr, TenuredObject); - if (!fun) - return false; - } else { - MOZ_ASSERT(script->treatAsRunOnce()); - MOZ_ASSERT(!script->functionNonDelazifying()); - } /* * We define the function as a property of the variable object and not the diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index fc5d0e0a791..766278e2d46 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1294,7 +1294,7 @@ * Operands: uint32_t funcIndex * Stack: => */ \ - macro(JSOP_DEFFUN, 127,"deffun", NULL, 5, 0, 0, JOF_OBJECT) \ + macro(JSOP_DEFFUN, 127,"deffun", NULL, 1, 1, 0, JOF_BYTE) \ /* * Defines the new binding on the frame's current variables-object (the * scope object on the scope chain designated to receive new variables) with @@ -1983,7 +1983,6 @@ * Stack: val => ToString(val) */ \ macro(JSOP_TOSTRING, 228, "tostring", NULL, 1, 1, 1, JOF_BYTE) - /* * In certain circumstances it may be useful to "pad out" the opcode space to * a power of two. Use this macro to do so. diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 9af4614a485..8ef58d00cac 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -237,7 +237,12 @@ CallObject::createForFunction(JSContext* cx, HandleObject enclosing, HandleFunct * object holding function's name. */ if (callee->isNamedLambda()) { - scopeChain = DeclEnvObject::create(cx, scopeChain, callee); + if (callee->isAsync()) { + RootedFunction fun(cx, &callee->getExtendedSlot(1).toObject().as()); + scopeChain = DeclEnvObject::create(cx, scopeChain, fun); + } else { + scopeChain = DeclEnvObject::create(cx, scopeChain, callee); + } if (!scopeChain) return nullptr; }