mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 762363 - ES6 spread-call syntax: f(...args). r=jorendorff.
This commit is contained in:
parent
816764a48e
commit
3cd097471b
@ -416,7 +416,8 @@ js::DirectEval(JSContext *cx, const CallArgs &args)
|
||||
AbstractFramePtr caller = iter.abstractFramePtr();
|
||||
|
||||
JS_ASSERT(IsBuiltinEvalForScope(caller.scopeChain(), args.calleev()));
|
||||
JS_ASSERT(JSOp(*iter.pc()) == JSOP_EVAL);
|
||||
JS_ASSERT(JSOp(*iter.pc()) == JSOP_EVAL ||
|
||||
JSOp(*iter.pc()) == JSOP_SPREADEVAL);
|
||||
JS_ASSERT_IF(caller.isFunctionFrame(),
|
||||
caller.compartment() == caller.callee()->compartment());
|
||||
|
||||
|
@ -5048,6 +5048,9 @@ EmitDelete(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t count);
|
||||
|
||||
static bool
|
||||
EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
@ -5079,18 +5082,20 @@ EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
bool emitArgs = true;
|
||||
ParseNode *pn2 = pn->pn_head;
|
||||
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
|
||||
switch (pn2->getKind()) {
|
||||
case PNK_NAME:
|
||||
if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
|
||||
pn2->name() == cx->names().callFunction)
|
||||
pn2->name() == cx->names().callFunction &&
|
||||
!spread)
|
||||
{
|
||||
/*
|
||||
* Special-casing of callFunction to emit bytecode that directly
|
||||
* invokes the callee with the correct |this| object and arguments.
|
||||
* callFunction(fun, thisArg, ...args) thus becomes:
|
||||
* callFunction(fun, thisArg, arg0, arg1) thus becomes:
|
||||
* - emit lookup for fun
|
||||
* - emit lookup for thisArg
|
||||
* - emit lookups for ...args
|
||||
* - emit lookups for arg0, arg1
|
||||
*
|
||||
* argc is set to the amount of actually emitted args and the
|
||||
* emitting of args below is disabled by setting emitArgs to false.
|
||||
@ -5176,19 +5181,29 @@ EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
*/
|
||||
bool oldEmittingForInit = bce->emittingForInit;
|
||||
bce->emittingForInit = false;
|
||||
for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
|
||||
if (!EmitTree(cx, bce, pn3))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
|
||||
if (!spread) {
|
||||
for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
|
||||
if (!EmitTree(cx, bce, pn3))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!EmitArray(cx, bce, pn2->pn_next, argc))
|
||||
return false;
|
||||
}
|
||||
bce->emittingForInit = oldEmittingForInit;
|
||||
}
|
||||
|
||||
if (Emit3(cx, bce, pn->getOp(), ARGC_HI(argc), ARGC_LO(argc)) < 0)
|
||||
return false;
|
||||
if (!spread) {
|
||||
if (Emit3(cx, bce, pn->getOp(), ARGC_HI(argc), ARGC_LO(argc)) < 0)
|
||||
return false;
|
||||
} else {
|
||||
if (Emit1(cx, bce, pn->getOp()) < 0)
|
||||
return false;
|
||||
}
|
||||
CheckTypeSet(cx, bce, pn->getOp());
|
||||
if (pn->isOp(JSOP_EVAL)) {
|
||||
if (pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL)) {
|
||||
uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
|
||||
EMIT_UINT16_IMM_OP(JSOP_LINENO, lineNum);
|
||||
}
|
||||
@ -5559,7 +5574,29 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
EmitArrayComp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
if (!EmitNewInit(cx, bce, JSProto_Array, pn))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Pass the new array's stack index to the PNK_ARRAYPUSH case via
|
||||
* bce->arrayCompDepth, then simply traverse the PNK_FOR node and
|
||||
* its kids under pn2 to generate this comprehension.
|
||||
*/
|
||||
JS_ASSERT(bce->stackDepth > 0);
|
||||
unsigned saveDepth = bce->arrayCompDepth;
|
||||
bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
|
||||
if (!EmitTree(cx, bce, pn->pn_head))
|
||||
return false;
|
||||
bce->arrayCompDepth = saveDepth;
|
||||
|
||||
/* Emit the usual op needed for decompilation. */
|
||||
return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t count)
|
||||
{
|
||||
/*
|
||||
* Emit code for [a, b, c] that is equivalent to constructing a new
|
||||
@ -5570,31 +5607,8 @@ EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
* JSOP_SETELEM/JSOP_SETPROP would do.
|
||||
*/
|
||||
|
||||
if (pn->isKind(PNK_ARRAYCOMP)) {
|
||||
if (!EmitNewInit(cx, bce, JSProto_Array, pn))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Pass the new array's stack index to the PNK_ARRAYPUSH case via
|
||||
* bce->arrayCompDepth, then simply traverse the PNK_FOR node and
|
||||
* its kids under pn2 to generate this comprehension.
|
||||
*/
|
||||
JS_ASSERT(bce->stackDepth > 0);
|
||||
unsigned saveDepth = bce->arrayCompDepth;
|
||||
bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
|
||||
if (!EmitTree(cx, bce, pn->pn_head))
|
||||
return false;
|
||||
bce->arrayCompDepth = saveDepth;
|
||||
|
||||
/* Emit the usual op needed for decompilation. */
|
||||
return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
|
||||
}
|
||||
|
||||
if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
|
||||
return EmitSingletonInitialiser(cx, bce, pn);
|
||||
|
||||
int32_t nspread = 0;
|
||||
for (ParseNode *elt = pn->pn_head; elt; elt = elt->pn_next) {
|
||||
for (ParseNode *elt = pn; elt; elt = elt->pn_next) {
|
||||
if (elt->isKind(PNK_SPREAD))
|
||||
nspread++;
|
||||
}
|
||||
@ -5607,9 +5621,9 @@ EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
// For arrays with spread, this is a very pessimistic allocation, the
|
||||
// minimum possible final size.
|
||||
SET_UINT24(pc, pn->pn_count - nspread);
|
||||
SET_UINT24(pc, count - nspread);
|
||||
|
||||
ParseNode *pn2 = pn->pn_head;
|
||||
ParseNode *pn2 = pn;
|
||||
jsatomid atomIndex;
|
||||
if (nspread && !EmitNumberOp(cx, 0, bce))
|
||||
return false;
|
||||
@ -5635,7 +5649,7 @@ EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
SET_UINT24(bce->code(off), atomIndex);
|
||||
}
|
||||
}
|
||||
JS_ASSERT(atomIndex == pn->pn_count);
|
||||
JS_ASSERT(atomIndex == count);
|
||||
if (nspread) {
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
@ -6064,8 +6078,14 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
}
|
||||
|
||||
case PNK_ARRAY:
|
||||
case PNK_ARRAYCOMP:
|
||||
ok = EmitArray(cx, bce, pn);
|
||||
if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
|
||||
ok = EmitSingletonInitialiser(cx, bce, pn);
|
||||
else
|
||||
ok = EmitArray(cx, bce, pn->pn_head, pn->pn_count);
|
||||
break;
|
||||
|
||||
case PNK_ARRAYCOMP:
|
||||
ok = EmitArrayComp(cx, bce, pn);
|
||||
break;
|
||||
|
||||
case PNK_OBJECT:
|
||||
|
@ -2998,7 +2998,8 @@ Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_CALL));
|
||||
JS_ASSERT(pn->isArity(PN_LIST));
|
||||
JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_EVAL) ||
|
||||
JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) ||
|
||||
pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL) ||
|
||||
pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
|
||||
|
||||
if (!report(ParseStrictError, pc->sc->strict, pn, msg))
|
||||
@ -5433,8 +5434,8 @@ Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt,
|
||||
!kid->isKind(PNK_DOT) &&
|
||||
!kid->isKind(PNK_ELEM) &&
|
||||
!(kid->isKind(PNK_CALL) &&
|
||||
(kid->isOp(JSOP_CALL) ||
|
||||
kid->isOp(JSOP_EVAL) ||
|
||||
(kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
|
||||
kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_SPREADEVAL) ||
|
||||
kid->isOp(JSOP_FUNCALL) ||
|
||||
kid->isOp(JSOP_FUNAPPLY))))
|
||||
{
|
||||
@ -6194,7 +6195,7 @@ Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::argumentList(Node listNode)
|
||||
Parser<ParseHandler>::argumentList(Node listNode, bool *isSpread)
|
||||
{
|
||||
if (tokenStream.matchToken(TOK_RP, TokenStream::Operand))
|
||||
return true;
|
||||
@ -6203,9 +6204,22 @@ Parser<ParseHandler>::argumentList(Node listNode)
|
||||
bool arg0 = true;
|
||||
|
||||
do {
|
||||
bool spread = false;
|
||||
uint32_t begin = 0;
|
||||
if (tokenStream.matchToken(TOK_TRIPLEDOT, TokenStream::Operand)) {
|
||||
spread = true;
|
||||
begin = pos().begin;
|
||||
*isSpread = true;
|
||||
}
|
||||
|
||||
Node argNode = assignExpr();
|
||||
if (!argNode)
|
||||
return false;
|
||||
if (spread) {
|
||||
argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
|
||||
if (!argNode)
|
||||
return null();
|
||||
}
|
||||
|
||||
if (handler.isOperationWithoutParens(argNode, PNK_YIELD) &&
|
||||
tokenStream.peekToken() == TOK_COMMA) {
|
||||
@ -6213,7 +6227,7 @@ Parser<ParseHandler>::argumentList(Node listNode)
|
||||
return false;
|
||||
}
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (tokenStream.matchToken(TOK_FOR)) {
|
||||
if (!spread && tokenStream.matchToken(TOK_FOR)) {
|
||||
if (pc->lastYieldOffset != startYieldOffset) {
|
||||
reportWithOffset(ParseError, false, pc->lastYieldOffset,
|
||||
JSMSG_BAD_GENEXP_BODY, js_yield_str);
|
||||
@ -6263,8 +6277,13 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
|
||||
handler.addList(lhs, ctorExpr);
|
||||
|
||||
if (tokenStream.matchToken(TOK_LP) && !argumentList(lhs))
|
||||
return null();
|
||||
if (tokenStream.matchToken(TOK_LP)) {
|
||||
bool isSpread = false;
|
||||
if (!argumentList(lhs, &isSpread))
|
||||
return null();
|
||||
if (isSpread)
|
||||
handler.setOp(lhs, JSOP_SPREADNEW);
|
||||
}
|
||||
} else {
|
||||
lhs = primaryExpr(tt);
|
||||
if (!lhs)
|
||||
@ -6297,6 +6316,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
if (!nextMember)
|
||||
return null();
|
||||
} else if (allowCallSyntax && tt == TOK_LP) {
|
||||
JSOp op = JSOP_CALL;
|
||||
nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
|
||||
if (!nextMember)
|
||||
return null();
|
||||
@ -6304,7 +6324,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
if (JSAtom *atom = handler.isName(lhs)) {
|
||||
if (atom == context->names().eval) {
|
||||
/* Select JSOP_EVAL and flag pc as heavyweight. */
|
||||
handler.setOp(nextMember, JSOP_EVAL);
|
||||
op = JSOP_EVAL;
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
|
||||
/*
|
||||
@ -6317,19 +6337,23 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
} else if (JSAtom *atom = handler.isGetProp(lhs)) {
|
||||
/* Select JSOP_FUNAPPLY given foo.apply(...). */
|
||||
if (atom == context->names().apply) {
|
||||
handler.setOp(nextMember, JSOP_FUNAPPLY);
|
||||
op = JSOP_FUNAPPLY;
|
||||
if (pc->sc->isFunctionBox())
|
||||
pc->sc->asFunctionBox()->usesApply = true;
|
||||
} else if (atom == context->names().call) {
|
||||
handler.setOp(nextMember, JSOP_FUNCALL);
|
||||
op = JSOP_FUNCALL;
|
||||
}
|
||||
}
|
||||
|
||||
handler.setBeginPosition(nextMember, lhs);
|
||||
handler.addList(nextMember, lhs);
|
||||
|
||||
if (!argumentList(nextMember))
|
||||
bool isSpread = false;
|
||||
if (!argumentList(nextMember, &isSpread))
|
||||
return null();
|
||||
if (isSpread)
|
||||
op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL);
|
||||
handler.setOp(nextMember, op);
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
return lhs;
|
||||
|
@ -544,7 +544,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
|
||||
bool arrayInitializerComprehensionTail(Node pn);
|
||||
Node generatorExpr(Node kid);
|
||||
bool argumentList(Node listNode);
|
||||
bool argumentList(Node listNode, bool *isSpread);
|
||||
Node letBlock(LetContext letContext);
|
||||
Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
|
||||
|
||||
|
@ -76,6 +76,9 @@ check("o[4 + 'h']", "o['4h']");
|
||||
check("this.x");
|
||||
check("ieval(undef)", "ieval(...)");
|
||||
check("ieval.call()", "ieval.call(...)");
|
||||
check("ieval(...[])", "ieval(...)");
|
||||
check("ieval(...[undef])", "ieval(...)");
|
||||
check("ieval(...[undef, undef])", "ieval(...)");
|
||||
|
||||
for (let tok of ["|", "^", "&", "==", "!==", "===", "!==", "<", "<=", ">", ">=",
|
||||
">>", "<<", ">>>", "+", "-", "*", "/", "%"]) {
|
||||
|
@ -5,9 +5,14 @@ var offenders = [
|
||||
"[1 ... n]",
|
||||
"(...x)",
|
||||
"[...x for (x of y)]",
|
||||
"[...x, x for (x of y)]",
|
||||
"[...]",
|
||||
"(...)",
|
||||
"[...,]"
|
||||
"[...,]",
|
||||
"[... ...[]]",
|
||||
"(... ...[])",
|
||||
"[x, ...]",
|
||||
"(x, ...)"
|
||||
];
|
||||
for (var sample of offenders) {
|
||||
assertThrowsInstanceOf(function () { eval(sample); }, SyntaxError);
|
||||
|
@ -1,3 +1,4 @@
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
assertEqArray([...[1, 2, 3]], [1, 2, 3]);
|
||||
@ -10,8 +11,44 @@ assertEqArray([1,, ...[2],, 3,, 4,], [1,, 2,, 3,, 4,]);
|
||||
assertEqArray([...[1, 2, 3],,,,], [1, 2, 3,,,,]);
|
||||
assertEqArray([,,...[1, 2, 3],,,,], [,,1,2,3,,,,]);
|
||||
|
||||
assertEqArray([...[undefined]], [undefined]);
|
||||
|
||||
// other iterable objects
|
||||
assertEqArray([...Int32Array([1, 2, 3])], [1, 2, 3]);
|
||||
assertEqArray([..."abc"], ["a", "b", "c"]);
|
||||
assertEqArray([...[1, 2, 3].iterator()], [1, 2, 3]);
|
||||
assertEqArray([...Set([1, 2, 3])], [1, 2, 3]);
|
||||
assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
|
||||
let itr = {
|
||||
iterator: function() {
|
||||
return {
|
||||
i: 1,
|
||||
next: function() {
|
||||
if (this.i < 4)
|
||||
return this.i++;
|
||||
else
|
||||
throw StopIteration;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
assertEqArray([...itr], [1, 2, 3]);
|
||||
let gen = {
|
||||
iterator: function() {
|
||||
for (let i = 1; i < 4; i ++)
|
||||
yield i;
|
||||
}
|
||||
};
|
||||
assertEqArray([...gen], [1, 2, 3]);
|
||||
|
||||
let a, b = [1, 2, 3];
|
||||
assertEqArray([...a=b], [1, 2, 3]);
|
||||
|
||||
// According to the draft spec, null and undefined are to be treated as empty
|
||||
// arrays. However, they are not iterable. If the spec is not changed to be in
|
||||
// terms of iterables, these tests should be fixed.
|
||||
//assertEqArray([1, ...null, 2], [1, 2]);
|
||||
//assertEqArray([1, ...undefined, 2], [1, 2]);
|
||||
assertThrowsInstanceOf(() => [...null], TypeError);
|
||||
assertThrowsInstanceOf(() => [...undefined], TypeError);
|
||||
|
||||
|
59
js/src/jit-test/tests/basic/spread-call-eval.js
Normal file
59
js/src/jit-test/tests/basic/spread-call-eval.js
Normal file
@ -0,0 +1,59 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
assertEq(eval(...[]), undefined);
|
||||
assertEq(eval(...["1 + 2"]), 3);
|
||||
|
||||
let a = 10, b = 1;
|
||||
assertEq(eval(...["a + b"]), 11);
|
||||
|
||||
(function() {
|
||||
let a = 20;
|
||||
assertEq(eval(...["a + b"]), 21);
|
||||
})();
|
||||
|
||||
with ({ a: 30 }) {
|
||||
assertEq(eval(...["a + b"]), 31);
|
||||
}
|
||||
|
||||
let line0 = Error().lineNumber;
|
||||
try { // line0 + 1
|
||||
eval(...["("]); // line0 + 2
|
||||
} catch (e) {
|
||||
assertEq(e.lineNumber, line0 + 2);
|
||||
}
|
||||
|
||||
// other iterable objects
|
||||
assertEq(eval(...["a + b"].iterator()), 11);
|
||||
assertEq(eval(...Set(["a + b"])), 11);
|
||||
let itr = {
|
||||
iterator: function() {
|
||||
return {
|
||||
i: 0,
|
||||
next: function() {
|
||||
this.i++;
|
||||
if (this.i == 1)
|
||||
return "a + b";
|
||||
else
|
||||
throw StopIteration;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
assertEq(eval(...itr), 11);
|
||||
let gen = {
|
||||
iterator: function() {
|
||||
yield "a + b";
|
||||
}
|
||||
};
|
||||
assertEq(eval(...gen), 11);
|
||||
|
||||
let c = ["C"], d = "D";
|
||||
assertEq(eval(...c=["c[0] + d"]), "c[0] + dD");
|
||||
|
||||
// According to the draft spec, null and undefined are to be treated as empty
|
||||
// arrays. However, they are not iterable. If the spec is not changed to be in
|
||||
// terms of iterables, these tests should be fixed.
|
||||
//assertEq(eval("a + b", ...null), 11);
|
||||
//assertEq(eval("a + b", ...undefined), 11);
|
||||
assertThrowsInstanceOf(() => eval("a + b", ...null), TypeError);
|
||||
assertThrowsInstanceOf(() => eval("a + b", ...undefined), TypeError);
|
13
js/src/jit-test/tests/basic/spread-call-evaluation-order.js
Normal file
13
js/src/jit-test/tests/basic/spread-call-evaluation-order.js
Normal file
@ -0,0 +1,13 @@
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
var check = [];
|
||||
function t(token) {
|
||||
check.push(token);
|
||||
return token;
|
||||
}
|
||||
let f = (...x) => x;
|
||||
f(3, ...[t(1)], ...[t(2), t(3)], 34, 42, ...[t(4)]);
|
||||
assertEqArray(check, [1, 2, 3, 4]);
|
||||
|
||||
var arr = [1, 2, 3];
|
||||
assertEqArray(f(...arr, arr.pop()), [1, 2, 3, 3]);
|
93
js/src/jit-test/tests/basic/spread-call-funapply.js
Normal file
93
js/src/jit-test/tests/basic/spread-call-funapply.js
Normal file
@ -0,0 +1,93 @@
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
function checkCommon(f) {
|
||||
assertEqArray(f.apply(null, ...[[1, 2, 3]]), [1, 2, 3]);
|
||||
assertEqArray(f.apply(...[null], [1, 2, 3]), [1, 2, 3]);
|
||||
assertEqArray(f.apply(...[null], ...[[1, 2, 3]]), [1, 2, 3]);
|
||||
assertEqArray(f.apply(...[null, [1, 2, 3]]), [1, 2, 3]);
|
||||
|
||||
// other iterable objects
|
||||
assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]);
|
||||
assertEqArray(f.apply(...[null, [1, 2, 3]].iterator()), [1, 2, 3]);
|
||||
let itr = {
|
||||
iterator: function() {
|
||||
return {
|
||||
i: 0,
|
||||
next: function() {
|
||||
this.i++;
|
||||
if (this.i == 1)
|
||||
return null;
|
||||
else if (this.i == 2)
|
||||
return [1, 2, 3];
|
||||
else
|
||||
throw StopIteration;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
assertEqArray(f.apply(...itr), [1, 2, 3]);
|
||||
let gen = {
|
||||
iterator: function() {
|
||||
yield null;
|
||||
yield [1, 2, 3];
|
||||
}
|
||||
};
|
||||
assertEqArray(f.apply(...gen), [1, 2, 3]);
|
||||
|
||||
let a;
|
||||
assertEqArray(f.apply(null, ...a=[[1, 2, 3]]), [1, 2, 3]);
|
||||
|
||||
// According to the draft spec, null and undefined are to be treated as empty
|
||||
// arrays. However, they are not iterable. If the spec is not changed to be in
|
||||
// terms of iterables, these tests should be fixed.
|
||||
//assertEqArray(f.apply(null, ...null, [1, 2, 3]), [1, 2, 3]);
|
||||
//assertEqArray(f.apply(null, ...undefined, [1, 2, 3]), [1, 2, 3]);
|
||||
assertThrowsInstanceOf(() => f.apply(null, ...null, [1, 2, 3]), TypeError);
|
||||
assertThrowsInstanceOf(() => f.apply(null, ...undefined, [1, 2, 3]), TypeError);
|
||||
}
|
||||
|
||||
function checkNormal(f) {
|
||||
checkCommon(f);
|
||||
|
||||
assertEqArray(f.apply(null, ...[[]]), [undefined, undefined, undefined]);
|
||||
assertEqArray(f.apply(null, ...[[1]]), [1, undefined, undefined]);
|
||||
assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2, undefined]);
|
||||
assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3]);
|
||||
|
||||
assertEqArray(f.apply(null, ...[[undefined]]), [undefined, undefined, undefined]);
|
||||
}
|
||||
|
||||
checkNormal(function(a, b, c) [a, b, c]);
|
||||
checkNormal((a, b, c) => [a, b, c]);
|
||||
|
||||
function checkDefault(f) {
|
||||
checkCommon(f);
|
||||
|
||||
assertEqArray(f.apply(null, ...[[]]), [-1, -2, -3]);
|
||||
assertEqArray(f.apply(null, ...[[1]]), [1, -2, -3]);
|
||||
assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2, -3]);
|
||||
assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3]);
|
||||
|
||||
assertEqArray(f.apply(null, ...[[undefined]]), [-1, -2, -3]);
|
||||
}
|
||||
|
||||
checkDefault(function(a = -1, b = -2, c = -3) [a, b, c]);
|
||||
checkDefault((a = -1, b = -2, c = -3) => [a, b, c]);
|
||||
|
||||
function checkRest(f) {
|
||||
checkCommon(f);
|
||||
|
||||
assertEqArray(f.apply(null, ...[[]]), []);
|
||||
assertEqArray(f.apply(null, ...[[1]]), [1]);
|
||||
assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2]);
|
||||
assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3, 4]);
|
||||
|
||||
assertEqArray(f.apply(null, ...[[undefined]]), [undefined]);
|
||||
|
||||
// other iterable objects
|
||||
assertEqArray(f.apply(null, ...Map([[["a", "A"], ["b", "B"]]])).map(([k, v]) => k + v), ["aA", "bB"]);
|
||||
}
|
||||
|
||||
checkRest(function(...x) x);
|
||||
checkRest((...x) => x);
|
11
js/src/jit-test/tests/basic/spread-call-funcall.js
Normal file
11
js/src/jit-test/tests/basic/spread-call-funcall.js
Normal file
@ -0,0 +1,11 @@
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
function check(f) {
|
||||
assertEqArray(f.call(...[null], 1, 2, 3), [1, 2, 3]);
|
||||
assertEqArray(f.call(...[null], 1, ...[2, 3], 4, ...[5, 6]), [1, 2, 3, 4, 5, 6]);
|
||||
assertEqArray(f.call(...[null, 1], ...[2, 3], 4, ...[5, 6]), [1, 2, 3, 4, 5, 6]);
|
||||
assertEqArray(f.call(...[null, 1, ...[2, 3], 4, ...[5, 6]]), [1, 2, 3, 4, 5, 6]);
|
||||
}
|
||||
|
||||
check(function(...x) x);
|
||||
check((...x) => x);
|
16
js/src/jit-test/tests/basic/spread-call-invalid-syntax.js
Normal file
16
js/src/jit-test/tests/basic/spread-call-invalid-syntax.js
Normal file
@ -0,0 +1,16 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var offenders = [
|
||||
"f(1 ... n)",
|
||||
"f(...x for (x in y))",
|
||||
"f(...)",
|
||||
"f(...,)",
|
||||
"f(... ...[])",
|
||||
"f(...x,)",
|
||||
"f(x, ...)",
|
||||
"f(...x, x for (x in y))",
|
||||
"f(x for (x in y), ...x)"
|
||||
];
|
||||
for (var sample of offenders) {
|
||||
assertThrowsInstanceOf(function() { eval(sample); }, SyntaxError);
|
||||
}
|
55
js/src/jit-test/tests/basic/spread-call-length.js
Normal file
55
js/src/jit-test/tests/basic/spread-call-length.js
Normal file
@ -0,0 +1,55 @@
|
||||
let makeCall = farg => Function("f", "arg", "return f(" + farg + ");");
|
||||
let makeFunCall = farg => Function("f", "arg", "return f.call(null, " + farg + ");");
|
||||
let makeNew = farg => Function("f", "arg", "return new f(" + farg + ").length;");
|
||||
|
||||
function checkLength(f, makeFn) {
|
||||
assertEq(makeFn("...[1, 2, 3]")(f), 3);
|
||||
assertEq(makeFn("1, ...[2], 3")(f), 3);
|
||||
assertEq(makeFn("1, ...[2], ...[3]")(f), 3);
|
||||
assertEq(makeFn("1, ...[2, 3]")(f), 3);
|
||||
assertEq(makeFn("1, ...[], 2, 3")(f), 3);
|
||||
|
||||
assertEq(makeFn("...[1]")(f), 1);
|
||||
assertEq(makeFn("...[1, 2]")(f), 2);
|
||||
assertEq(makeFn("...[1, 2, 3, 4]")(f), 4);
|
||||
assertEq(makeFn("1, ...[2, 3, 4], 5")(f), 5);
|
||||
|
||||
assertEq(makeFn("...[undefined]")(f), 1);
|
||||
|
||||
// other iterable objects
|
||||
assertEq(makeFn("...arg")(f, Int32Array([1, 2, 3])), 3);
|
||||
assertEq(makeFn("...arg")(f, "abc"), 3);
|
||||
assertEq(makeFn("...arg")(f, [1, 2, 3].iterator()), 3);
|
||||
assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3);
|
||||
assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
|
||||
let itr = {
|
||||
iterator: function() {
|
||||
return {
|
||||
i: 1,
|
||||
next: function() {
|
||||
if (this.i < 4)
|
||||
return this.i++;
|
||||
else
|
||||
throw StopIteration;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
assertEq(makeFn("...arg")(f, itr), 3);
|
||||
let gen = {
|
||||
iterator: function() {
|
||||
for (let i = 1; i < 4; i ++)
|
||||
yield i;
|
||||
}
|
||||
};
|
||||
assertEq(makeFn("...arg")(f, gen), 3);
|
||||
}
|
||||
|
||||
checkLength(function(x) arguments.length, makeCall);
|
||||
checkLength(function(x) arguments.length, makeFunCall);
|
||||
checkLength((x) => arguments.length, makeCall);
|
||||
checkLength((x) => arguments.length, makeFunCall);
|
||||
function lengthClass(x) {
|
||||
this.length = arguments.length;
|
||||
}
|
||||
checkLength(lengthClass, makeNew);
|
23
js/src/jit-test/tests/basic/spread-call-maxarg.js
Normal file
23
js/src/jit-test/tests/basic/spread-call-maxarg.js
Normal file
@ -0,0 +1,23 @@
|
||||
let a = [];
|
||||
a.length = getMaxArgs() + 1;
|
||||
|
||||
let f = function() {
|
||||
};
|
||||
|
||||
try {
|
||||
f(...a);
|
||||
} catch (e) {
|
||||
assertEq(e.message, "too many function arguments");
|
||||
}
|
||||
|
||||
try {
|
||||
new f(...a);
|
||||
} catch (e) {
|
||||
assertEq(e.message, "too many constructor arguments");
|
||||
}
|
||||
|
||||
try {
|
||||
eval(...a);
|
||||
} catch (e) {
|
||||
assertEq(e.message, "too many function arguments");
|
||||
}
|
9
js/src/jit-test/tests/basic/spread-call-new.js
Normal file
9
js/src/jit-test/tests/basic/spread-call-new.js
Normal file
@ -0,0 +1,9 @@
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
function g(a, b, c) {
|
||||
this.value = [a, b, c];
|
||||
assertEq(Object.getPrototypeOf(this), g.prototype);
|
||||
assertEq(arguments.callee, g);
|
||||
}
|
||||
|
||||
assertEqArray(new g(...[1, 2, 3]).value, [1, 2, 3]);
|
16
js/src/jit-test/tests/basic/spread-call-not-iterable.js
Normal file
16
js/src/jit-test/tests/basic/spread-call-not-iterable.js
Normal file
@ -0,0 +1,16 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
assertThrowsInstanceOf(() => Math.sin(...true), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...false), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...new Date()), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...Function("")), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...function () {}), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...(x => x)), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...1), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...{}), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...{ iterator: 10 }), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...{ iterator: function() undefined }), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...{ iterator: function() this }), TypeError);
|
||||
assertThrowsValue(() => Math.sin(...{ iterator: function() this, next: function() { throw 10; } }), 10);
|
||||
assertThrowsInstanceOf(() => Math.sin(.../a/), TypeError);
|
||||
assertThrowsInstanceOf(() => Math.sin(...new Error()), TypeError);
|
18
js/src/jit-test/tests/basic/spread-call-recursion.js
Normal file
18
js/src/jit-test/tests/basic/spread-call-recursion.js
Normal file
@ -0,0 +1,18 @@
|
||||
let a = [];
|
||||
a.length = 30;
|
||||
|
||||
function check(f) {
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
assertEq(e.message, "too much recursion");
|
||||
}
|
||||
}
|
||||
|
||||
let f = function() f(...a) + 1;
|
||||
let g = () => g(...a) + 1;
|
||||
let h = function() new h(...a) + 1;
|
||||
|
||||
check(f);
|
||||
check(g);
|
||||
check(h);
|
26
js/src/jit-test/tests/basic/spread-call-setcall.js
Normal file
26
js/src/jit-test/tests/basic/spread-call-setcall.js
Normal file
@ -0,0 +1,26 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function g() {
|
||||
}
|
||||
|
||||
let a = {
|
||||
g: function() {
|
||||
}
|
||||
};
|
||||
|
||||
function check(expr) {
|
||||
assertThrowsInstanceOf(Function(expr), ReferenceError);
|
||||
}
|
||||
|
||||
check("g(...[]) = 1");
|
||||
check("a.g(...[]) = 1");
|
||||
check("eval(...['1']) = 1");
|
||||
check("[g(...[])] = 1");
|
||||
check("[a.g(...[])] = 1");
|
||||
check("[eval(...['1'])] = 1");
|
||||
check("({y: g(...[])}) = 1");
|
||||
check("({y: a.g(...[])}) = 1");
|
||||
check("({y: eval(...['1'])}) = 1");
|
||||
check("g(...[]) ++");
|
||||
check("a.g(...[]) ++");
|
||||
check("eval(...['1']) ++");
|
105
js/src/jit-test/tests/basic/spread-call-this-strict.js
Normal file
105
js/src/jit-test/tests/basic/spread-call-this-strict.js
Normal file
@ -0,0 +1,105 @@
|
||||
"use strict";
|
||||
|
||||
let global = this;
|
||||
let p = {};
|
||||
let q = {};
|
||||
|
||||
let g1 = function() {
|
||||
assertEq(this, undefined);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, global);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, p);
|
||||
};
|
||||
g3.apply(p, ...[]);
|
||||
g3.call(p, ...[]);
|
||||
|
||||
g2.apply(p, ...[]);
|
||||
g2.call(p, ...[]);
|
||||
|
||||
let o = {
|
||||
f1: function() {
|
||||
assertEq(this, o);
|
||||
|
||||
let g1 = function() {
|
||||
assertEq(this, undefined);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, o);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, q);
|
||||
};
|
||||
g3.apply(q, ...[]);
|
||||
g3.call(q, ...[]);
|
||||
|
||||
let g4 = x => {
|
||||
assertEq(this, o);
|
||||
};
|
||||
g4.apply(q, ...[]);
|
||||
g4.call(q, ...[]);
|
||||
},
|
||||
f2: x => {
|
||||
assertEq(this, global);
|
||||
let g1 = function() {
|
||||
assertEq(this, undefined);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, global);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, q);
|
||||
};
|
||||
g3.apply(q, ...[]);
|
||||
g3.call(q, ...[]);
|
||||
|
||||
let g4 = x => {
|
||||
assertEq(this, global);
|
||||
};
|
||||
g4.apply(q, ...[]);
|
||||
g4.call(q, ...[]);
|
||||
},
|
||||
f3: function() {
|
||||
assertEq(this, p);
|
||||
|
||||
let g1 = function() {
|
||||
assertEq(this, undefined);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, p);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, q);
|
||||
};
|
||||
g3.apply(q, ...[]);
|
||||
g3.call(q, ...[]);
|
||||
|
||||
let g4 = x => {
|
||||
assertEq(this, p);
|
||||
};
|
||||
g4.apply(q, ...[]);
|
||||
g4.call(q, ...[]);
|
||||
}
|
||||
};
|
||||
o.f1(...[]);
|
||||
o.f2(...[]);
|
||||
o.f3.apply(p, ...[]);
|
||||
o.f2.apply(p, ...[]);
|
123
js/src/jit-test/tests/basic/spread-call-this.js
Normal file
123
js/src/jit-test/tests/basic/spread-call-this.js
Normal file
@ -0,0 +1,123 @@
|
||||
let global = this;
|
||||
let p = {};
|
||||
let q = {};
|
||||
|
||||
let g1 = function() {
|
||||
assertEq(this, global);
|
||||
assertEq(arguments.callee, g1);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, global);
|
||||
// arguments.callee is unbound function object, and following assertion fails.
|
||||
// see Bug 889158
|
||||
//assertEq(arguments.callee, g2);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, p);
|
||||
assertEq(arguments.callee, g3);
|
||||
};
|
||||
g3.apply(p, ...[]);
|
||||
g3.call(p, ...[]);
|
||||
|
||||
g2.apply(p, ...[]);
|
||||
g2.call(p, ...[]);
|
||||
|
||||
let o = {
|
||||
f1: function() {
|
||||
assertEq(this, o);
|
||||
assertEq(arguments.callee, o.f1);
|
||||
|
||||
let g1 = function() {
|
||||
assertEq(this, global);
|
||||
assertEq(arguments.callee, g1);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, o);
|
||||
//assertEq(arguments.callee, g2);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, q);
|
||||
assertEq(arguments.callee, g3);
|
||||
};
|
||||
g3.apply(q, ...[]);
|
||||
g3.call(q, ...[]);
|
||||
|
||||
let g4 = x => {
|
||||
assertEq(this, o);
|
||||
//assertEq(arguments.callee, g4);
|
||||
};
|
||||
g4.apply(q, ...[]);
|
||||
g4.call(q, ...[]);
|
||||
},
|
||||
f2: x => {
|
||||
assertEq(this, global);
|
||||
//assertEq(arguments.callee, o.f2);
|
||||
let g1 = function() {
|
||||
assertEq(this, global);
|
||||
assertEq(arguments.callee, g1);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, global);
|
||||
//assertEq(arguments.callee, g2);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, q);
|
||||
assertEq(arguments.callee, g3);
|
||||
};
|
||||
g3.apply(q, ...[]);
|
||||
g3.call(q, ...[]);
|
||||
|
||||
let g4 = x => {
|
||||
assertEq(this, global);
|
||||
//assertEq(arguments.callee, g4);
|
||||
};
|
||||
g4.apply(q, ...[]);
|
||||
g4.call(q, ...[]);
|
||||
},
|
||||
f3: function() {
|
||||
assertEq(this, p);
|
||||
assertEq(arguments.callee, o.f3);
|
||||
|
||||
let g1 = function() {
|
||||
assertEq(this, global);
|
||||
assertEq(arguments.callee, g1);
|
||||
};
|
||||
g1(...[]);
|
||||
|
||||
let g2 = x => {
|
||||
assertEq(this, p);
|
||||
//assertEq(arguments.callee, g2);
|
||||
};
|
||||
g2(...[]);
|
||||
|
||||
let g3 = function() {
|
||||
assertEq(this, q);
|
||||
assertEq(arguments.callee, g3);
|
||||
};
|
||||
g3.apply(q, ...[]);
|
||||
g3.call(q, ...[]);
|
||||
|
||||
let g4 = x => {
|
||||
assertEq(this, p);
|
||||
//assertEq(arguments.callee, g4);
|
||||
};
|
||||
g4.apply(q, ...[]);
|
||||
g4.call(q, ...[]);
|
||||
}
|
||||
};
|
||||
o.f1(...[]);
|
||||
o.f2(...[]);
|
||||
o.f3.apply(p, ...[]);
|
||||
o.f2.apply(p, ...[]);
|
115
js/src/jit-test/tests/basic/spread-call.js
Normal file
115
js/src/jit-test/tests/basic/spread-call.js
Normal file
@ -0,0 +1,115 @@
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
let makeCall = farg => Function("f", "arg", "return f(" + farg + ");");
|
||||
let makeFunCall = farg => Function("f", "arg", "return f.call(null, " + farg + ");");
|
||||
let makeNew = farg => Function("f", "arg", "return new f(" + farg + ").value;");
|
||||
|
||||
function checkCommon(f, makeFn) {
|
||||
assertEqArray(makeFn("...[1, 2, 3]")(f), [1, 2, 3]);
|
||||
assertEqArray(makeFn("1, ...[2], 3")(f), [1, 2, 3]);
|
||||
assertEqArray(makeFn("1, ...[2], ...[3]")(f), [1, 2, 3]);
|
||||
assertEqArray(makeFn("1, ...[2, 3]")(f), [1, 2, 3]);
|
||||
assertEqArray(makeFn("1, ...[], 2, 3")(f), [1, 2, 3]);
|
||||
|
||||
// other iterable objects
|
||||
assertEqArray(makeFn("...arg")(f, Int32Array([1, 2, 3])), [1, 2, 3]);
|
||||
assertEqArray(makeFn("...arg")(f, "abc"), ["a", "b", "c"]);
|
||||
assertEqArray(makeFn("...arg")(f, [1, 2, 3].iterator()), [1, 2, 3]);
|
||||
assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]);
|
||||
assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
|
||||
let itr = {
|
||||
iterator: function() {
|
||||
return {
|
||||
i: 1,
|
||||
next: function() {
|
||||
if (this.i < 4)
|
||||
return this.i++;
|
||||
else
|
||||
throw StopIteration;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
assertEqArray(makeFn("...arg")(f, itr), [1, 2, 3]);
|
||||
let gen = {
|
||||
iterator: function() {
|
||||
for (let i = 1; i < 4; i ++)
|
||||
yield i;
|
||||
}
|
||||
};
|
||||
assertEqArray(makeFn("...arg")(f, gen), [1, 2, 3]);
|
||||
|
||||
assertEqArray(makeFn("...arg=[1, 2, 3]")(f), [1, 2, 3]);
|
||||
|
||||
// According to the draft spec, null and undefined are to be treated as empty
|
||||
// arrays. However, they are not iterable. If the spec is not changed to be in
|
||||
// terms of iterables, these tests should be fixed.
|
||||
//assertEqArray(makeFn(1, ...null, 2, 3)(f), [1, 2, 3]);
|
||||
//assertEqArray(makeFn(1, ...undefined, 2, 3)(f), [1, 2, 3]);
|
||||
assertThrowsInstanceOf(makeFn("1, ...null, 2, 3"), TypeError);
|
||||
assertThrowsInstanceOf(makeFn("1, ...undefined, 2, 3"), TypeError);
|
||||
}
|
||||
|
||||
function checkNormal(f, makeFn) {
|
||||
checkCommon(f, makeFn);
|
||||
|
||||
assertEqArray(makeFn("...[]")(f), [undefined, undefined, undefined]);
|
||||
assertEqArray(makeFn("...[1]")(f), [1, undefined, undefined]);
|
||||
assertEqArray(makeFn("...[1, 2]")(f), [1, 2, undefined]);
|
||||
assertEqArray(makeFn("...[1, 2, 3, 4]")(f), [1, 2, 3]);
|
||||
|
||||
assertEqArray(makeFn("...[undefined]")(f), [undefined, undefined, undefined]);
|
||||
}
|
||||
|
||||
checkNormal(function(a, b, c) [a, b, c], makeCall);
|
||||
checkNormal(function(a, b, c) [a, b, c], makeFunCall);
|
||||
checkNormal((a, b, c) => [a, b, c], makeCall);
|
||||
checkNormal((a, b, c) => [a, b, c], makeFunCall);
|
||||
function normalClass(a, b, c) {
|
||||
this.value = [a, b, c];
|
||||
assertEq(Object.getPrototypeOf(this), normalClass.prototype);
|
||||
}
|
||||
checkNormal(normalClass, makeNew);
|
||||
|
||||
function checkDefault(f, makeFn) {
|
||||
checkCommon(f, makeFn);
|
||||
|
||||
assertEqArray(makeFn("...[]")(f), [-1, -2, -3]);
|
||||
assertEqArray(makeFn("...[1]")(f), [1, -2, -3]);
|
||||
assertEqArray(makeFn("...[1, 2]")(f), [1, 2, -3]);
|
||||
assertEqArray(makeFn("...[1, 2, 3, 4]")(f), [1, 2, 3]);
|
||||
|
||||
assertEqArray(makeFn("...[undefined]")(f), [-1, -2, -3]);
|
||||
}
|
||||
|
||||
checkDefault(function(a = -1, b = -2, c = -3) [a, b, c], makeCall);
|
||||
checkDefault(function(a = -1, b = -2, c = -3) [a, b, c], makeFunCall);
|
||||
checkDefault((a = -1, b = -2, c = -3) => [a, b, c], makeCall);
|
||||
checkDefault((a = -1, b = -2, c = -3) => [a, b, c], makeFunCall);
|
||||
function defaultClass(a = -1, b = -2, c = -3) {
|
||||
this.value = [a, b, c];
|
||||
assertEq(Object.getPrototypeOf(this), defaultClass.prototype);
|
||||
}
|
||||
checkDefault(defaultClass, makeNew);
|
||||
|
||||
function checkRest(f, makeFn) {
|
||||
checkCommon(f, makeFn);
|
||||
|
||||
assertEqArray(makeFn("...[]")(f), []);
|
||||
assertEqArray(makeFn("1, ...[2, 3, 4], 5")(f), [1, 2, 3, 4, 5]);
|
||||
assertEqArray(makeFn("1, ...[], 2")(f), [1, 2]);
|
||||
assertEqArray(makeFn("1, ...[2, 3], 4, ...[5, 6]")(f), [1, 2, 3, 4, 5, 6]);
|
||||
|
||||
assertEqArray(makeFn("...[undefined]")(f), [undefined]);
|
||||
}
|
||||
|
||||
checkRest(function(...x) x, makeCall);
|
||||
checkRest(function(...x) x, makeFunCall);
|
||||
checkRest((...x) => x, makeCall);
|
||||
checkRest((...x) => x, makeFunCall);
|
||||
function restClass(...x) {
|
||||
this.value = x;
|
||||
assertEq(Object.getPrototypeOf(this), restClass.prototype);
|
||||
}
|
||||
checkRest(restClass, makeNew);
|
@ -412,3 +412,5 @@ MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is
|
||||
MSG_DEF(JSMSG_GENERATOR_FINISHED, 359, 0, JSEXN_TYPEERR, "generator has already finished")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 360, 0, JSEXN_ERR, "Type is too large to allocate")
|
||||
MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPE_OBJECT, 361, 0, JSEXN_ERR, "Expected a type object")
|
||||
MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 362, 0, JSEXN_RANGEERR, "too many constructor arguments")
|
||||
MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 363, 0, JSEXN_RANGEERR, "too many function arguments")
|
||||
|
@ -265,6 +265,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
||||
break;
|
||||
|
||||
case JSOP_EVAL:
|
||||
case JSOP_SPREADEVAL:
|
||||
canTrackVars = false;
|
||||
isIonInlineable = false;
|
||||
break;
|
||||
|
@ -1450,9 +1450,12 @@ types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
* Sub2 lets us continue to distinguish the two subclasses and any extra
|
||||
* properties added to those prototype objects.
|
||||
*/
|
||||
if (JSOp(*pc) != JSOP_NEW)
|
||||
if (JSOp(*pc) == JSOP_NEW)
|
||||
pc += JSOP_NEW_LENGTH;
|
||||
else if (JSOp(*pc) == JSOP_SPREADNEW)
|
||||
pc += JSOP_SPREADNEW_LENGTH;
|
||||
else
|
||||
return false;
|
||||
pc += JSOP_NEW_LENGTH;
|
||||
if (JSOp(*pc) == JSOP_SETPROP) {
|
||||
jsid id = GetAtomId(cx, script, pc, 0);
|
||||
if (id == id_prototype(cx))
|
||||
|
@ -1308,6 +1308,9 @@ ExpressionDecompiler::decompilePC(jsbytecode *pc)
|
||||
case JSOP_FUNCALL:
|
||||
return decompilePC(pcstack[-int32_t(GET_ARGC(pc) + 2)]) &&
|
||||
write("(...)");
|
||||
case JSOP_SPREADCALL:
|
||||
return decompilePC(pcstack[-int32_t(3)]) &&
|
||||
write("(...)");
|
||||
case JSOP_NEWARRAY:
|
||||
return write("[]");
|
||||
case JSOP_REGEXP:
|
||||
@ -2009,11 +2012,13 @@ js::CallResultEscapes(jsbytecode *pc)
|
||||
* - call / not / ifeq
|
||||
*/
|
||||
|
||||
if (*pc != JSOP_CALL)
|
||||
if (*pc == JSOP_CALL)
|
||||
pc += JSOP_CALL_LENGTH;
|
||||
else if (*pc == JSOP_SPREADCALL)
|
||||
pc += JSOP_SPREADCALL_LENGTH;
|
||||
else
|
||||
return true;
|
||||
|
||||
pc += JSOP_CALL_LENGTH;
|
||||
|
||||
if (*pc == JSOP_POP)
|
||||
return false;
|
||||
|
||||
|
@ -90,9 +90,13 @@ OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, JOF_BYTE |JOF_E
|
||||
OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, JOF_BYTE|JOF_DETECTING)
|
||||
OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, JOF_BYTE)
|
||||
|
||||
OPDEF(JSOP_UNUSED41, 41, "unused41", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED42, 42, "unused42", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED43, 43, "unused43", NULL, 1, 0, 0, JOF_BYTE)
|
||||
/* spreadcall variant of JSOP_CALL */
|
||||
OPDEF(JSOP_SPREADCALL,41, "spreadcall", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET)
|
||||
/* spreadcall variant of JSOP_NEW */
|
||||
OPDEF(JSOP_SPREADNEW, 42, "spreadnew", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET)
|
||||
/* spreadcall variant of JSOP_EVAL */
|
||||
OPDEF(JSOP_SPREADEVAL,43, "spreadeval", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET)
|
||||
|
||||
OPDEF(JSOP_UNUSED44, 44, "unused44", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED45, 45, "unused45", NULL, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_UNUSED46, 46, "unused46", NULL, 1, 0, 0, JOF_BYTE)
|
||||
|
@ -2162,10 +2162,12 @@ js::CurrentScriptFileLineOrigin(JSContext *cx, const char **file, unsigned *line
|
||||
JSScript *script = NULL;
|
||||
jsbytecode *pc = NULL;
|
||||
types::TypeScript::GetPcScript(cx, &script, &pc);
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_EVAL);
|
||||
JS_ASSERT(*(pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_SPREADEVAL);
|
||||
JS_ASSERT(*(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
|
||||
: JSOP_SPREADEVAL_LENGTH)) == JSOP_LINENO);
|
||||
*file = script->filename();
|
||||
*linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
|
||||
*linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
|
||||
: JSOP_SPREADEVAL_LENGTH));
|
||||
*origin = script->originPrincipals();
|
||||
return;
|
||||
}
|
||||
|
@ -1471,8 +1471,8 @@ PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecod
|
||||
* executing on cx. If there is no current script executing on cx (e.g., a
|
||||
* native called directly through JSAPI (e.g., by setTimeout)), NULL and 0 are
|
||||
* returned as the file and line. Additionally, this function avoids the full
|
||||
* linear scan to compute line number when the caller guarnatees that the
|
||||
* script compilation occurs at a JSOP_EVAL.
|
||||
* linear scan to compute line number when the caller guarantees that the
|
||||
* script compilation occurs at a JSOP_EVAL/JSOP_SPREADEVAL.
|
||||
*/
|
||||
|
||||
enum LineOption {
|
||||
|
@ -2405,6 +2405,61 @@ BEGIN_CASE(JSOP_EVAL)
|
||||
}
|
||||
END_CASE(JSOP_EVAL)
|
||||
|
||||
BEGIN_CASE(JSOP_SPREADNEW)
|
||||
BEGIN_CASE(JSOP_SPREADCALL)
|
||||
if (regs.fp()->hasPushedSPSFrame())
|
||||
cx->runtime()->spsProfiler.updatePC(script, regs.pc);
|
||||
/* FALL THROUGH */
|
||||
|
||||
BEGIN_CASE(JSOP_SPREADEVAL)
|
||||
{
|
||||
JS_ASSERT(regs.stackDepth() >= 3);
|
||||
RootedObject &aobj = rootObject0;
|
||||
aobj = ®s.sp[-1].toObject();
|
||||
|
||||
uint32_t length = aobj->as<ArrayObject>().length();
|
||||
|
||||
if (length > ARGS_LENGTH_MAX) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
op == JSOP_SPREADNEW ? JSMSG_TOO_MANY_CON_SPREADARGS
|
||||
: JSMSG_TOO_MANY_FUN_SPREADARGS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
InvokeArgs args(cx);
|
||||
|
||||
if (!args.init(length))
|
||||
return false;
|
||||
|
||||
args.setCallee(regs.sp[-3]);
|
||||
args.setThis(regs.sp[-2]);
|
||||
|
||||
if (!GetElements(cx, aobj, length, args.array()))
|
||||
goto error;
|
||||
|
||||
if (op == JSOP_SPREADNEW) {
|
||||
if (!InvokeConstructor(cx, args))
|
||||
goto error;
|
||||
} else if (op == JSOP_SPREADCALL) {
|
||||
if (!Invoke(cx, args))
|
||||
goto error;
|
||||
} else {
|
||||
JS_ASSERT(op == JSOP_SPREADEVAL);
|
||||
if (IsBuiltinEvalForScope(regs.fp()->scopeChain(), args.calleev())) {
|
||||
if (!DirectEval(cx, args))
|
||||
goto error;
|
||||
} else {
|
||||
if (!Invoke(cx, args))
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
regs.sp -= 2;
|
||||
regs.sp[-1] = args.rval();
|
||||
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
|
||||
}
|
||||
END_CASE(JSOP_SPREADCALL)
|
||||
|
||||
BEGIN_CASE(JSOP_FUNAPPLY)
|
||||
{
|
||||
CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
|
||||
|
@ -22,7 +22,7 @@ namespace js {
|
||||
* and saved versions. If deserialization fails, the data should be
|
||||
* invalidated if possible.
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 151);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 152);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user