зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1185106 - Part 4: Implement async functions. (r=efaust, r=jandem)
This commit is contained in:
Родитель
06f8c0d213
Коммит
48ac7512bc
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2496,15 +2496,15 @@ static const VMFunction DefFunOperationInfo = FunctionInfo<DefFunOperationFn>(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);
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
});
|
|
@ -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);
|
||||
});
|
|
@ -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));
|
||||
};
|
||||
}
|
|
@ -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);
|
|
@ -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<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
|
||||
MOZ_ASSERT(REGS.sp[-1].isObject());
|
||||
ReservedRooted<JSFunction*> fun(&rootFunction0, ®S.sp[-1].toObject().as<JSFunction>());
|
||||
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<JSFunction>());
|
||||
scopeChain = DeclEnvObject::create(cx, scopeChain, fun);
|
||||
} else {
|
||||
scopeChain = DeclEnvObject::create(cx, scopeChain, callee);
|
||||
}
|
||||
if (!scopeChain)
|
||||
return nullptr;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче