mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 757676 - Implement JS default parameters. r=jorendorff
This commit is contained in:
parent
a37933ddeb
commit
ccc0336692
@ -5102,6 +5102,11 @@ EmitStatementList(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t
|
||||
PushStatement(bce->sc, &stmtInfo, STMT_BLOCK, top);
|
||||
|
||||
ParseNode *pnchild = pn->pn_head;
|
||||
|
||||
// Destructuring is handled in args body for functions with default
|
||||
// arguments.
|
||||
if (pn->pn_xflags & PNX_DESTRUCT && bce->sc->fun()->hasDefaults())
|
||||
pnchild = pnchild->pn_next;
|
||||
if (pn->pn_xflags & PNX_FUNCDEFS) {
|
||||
/*
|
||||
* This block contains top-level function definitions. To ensure
|
||||
@ -5115,7 +5120,7 @@ EmitStatementList(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t
|
||||
* mode for scripts does not allow separate emitter passes.
|
||||
*/
|
||||
JS_ASSERT(bce->sc->inFunction);
|
||||
if (pn->pn_xflags & PNX_DESTRUCT) {
|
||||
if (pn->pn_xflags & PNX_DESTRUCT && !bce->sc->fun()->hasDefaults()) {
|
||||
/*
|
||||
* Assign the destructuring arguments before defining any
|
||||
* functions, see bug 419662.
|
||||
@ -5860,6 +5865,50 @@ EmitUnary(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return Emit1(cx, bce, op) >= 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitDefaults(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_ARGSBODY));
|
||||
ParseNode *pnlast = pn->last();
|
||||
unsigned ndefaults = 0;
|
||||
for (ParseNode *arg = pn->pn_head; arg != pnlast; arg = arg->pn_next) {
|
||||
if (arg->pn_expr)
|
||||
ndefaults++;
|
||||
}
|
||||
JSFunction *fun = bce->sc->fun();
|
||||
unsigned nformal = fun->nargs - fun->hasRest();
|
||||
EMIT_UINT16_IMM_OP(JSOP_ACTUALSFILLED, nformal - ndefaults);
|
||||
ptrdiff_t top = bce->offset();
|
||||
size_t tableSize = (size_t)(JUMP_OFFSET_LEN * (3 + ndefaults));
|
||||
if (EmitN(cx, bce, JSOP_TABLESWITCH, tableSize) < 0)
|
||||
return false;
|
||||
jsbytecode *pc = bce->code(top + JUMP_OFFSET_LEN);
|
||||
JS_ASSERT(nformal >= ndefaults);
|
||||
SET_JUMP_OFFSET(pc, nformal - ndefaults);
|
||||
pc += JUMP_OFFSET_LEN;
|
||||
SET_JUMP_OFFSET(pc, nformal - 1);
|
||||
pc += JUMP_OFFSET_LEN;
|
||||
|
||||
// Fill body of switch, which sets defaults where needed.
|
||||
for (ParseNode *arg = pn->pn_head; arg != pnlast; arg = arg->pn_next) {
|
||||
if (!arg->pn_expr)
|
||||
continue;
|
||||
SET_JUMP_OFFSET(pc, bce->offset() - top);
|
||||
pc += JUMP_OFFSET_LEN;
|
||||
if (!EmitTree(cx, bce, arg->pn_expr))
|
||||
return false;
|
||||
if (!BindNameToSlot(cx, bce, arg))
|
||||
return false;
|
||||
if (!EmitVarOp(cx, arg, JSOP_SETARG, bce))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(pc == bce->code(top + tableSize));
|
||||
SET_JUMP_OFFSET(bce->code(top), bce->offset() - top);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
@ -5881,7 +5930,50 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
case PNK_ARGSBODY:
|
||||
{
|
||||
JSFunction *fun = bce->sc->fun();
|
||||
ParseNode *pnlast = pn->last();
|
||||
if (fun->hasDefaults()) {
|
||||
if (pnlast->pn_xflags & PNX_DESTRUCT) {
|
||||
JS_ASSERT(pnlast->pn_head->isKind(PNK_SEMI));
|
||||
|
||||
// Defaults must be able to access destructured arguments, so do
|
||||
// that now.
|
||||
if (!EmitTree(cx, bce, pnlast->pn_head))
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseNode *rest = NULL;
|
||||
if (fun->hasRest()) {
|
||||
JS_ASSERT(!bce->sc->funArgumentsHasLocalBinding());
|
||||
|
||||
// Defaults and rest also need special handling. The rest
|
||||
// parameter needs to be undefined while defaults are being
|
||||
// processed. To do this, we create the rest argument and let it
|
||||
// sit on the stack while processing defaults. The rest
|
||||
// parameter's slot is set to undefined for the course of
|
||||
// default processing.
|
||||
rest = pn->pn_head;
|
||||
while (rest->pn_next != pnlast)
|
||||
rest = rest->pn_next;
|
||||
if (Emit1(cx, bce, JSOP_REST) < 0)
|
||||
return false;
|
||||
CheckTypeSet(cx, bce, JSOP_REST);
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
return false;
|
||||
if (!EmitVarOp(cx, rest, JSOP_SETARG, bce))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
}
|
||||
if (!EmitDefaults(cx, bce, pn))
|
||||
return false;
|
||||
if (fun->hasRest()) {
|
||||
if (!EmitVarOp(cx, rest, JSOP_SETARG, bce))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (ParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
|
||||
if (!pn2->isDefn())
|
||||
continue;
|
||||
@ -5891,8 +5983,9 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
if (!bce->noteClosedArg(pn2))
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (pn2->pn_next == pnlast && bce->sc->fun()->hasRest()) {
|
||||
/* Fill rest parameter. */
|
||||
if (pn2->pn_next == pnlast && fun->hasRest() && !fun->hasDefaults()) {
|
||||
|
||||
// Fill rest parameter. We handled the case with defaults above.
|
||||
JS_ASSERT(!bce->sc->funArgumentsHasLocalBinding());
|
||||
bce->switchToProlog();
|
||||
if (Emit1(cx, bce, JSOP_REST) < 0)
|
||||
|
@ -614,8 +614,8 @@ struct ParseNode {
|
||||
ObjectBox *objbox; /* block or regexp object */
|
||||
};
|
||||
union {
|
||||
ParseNode *expr; /* function body, var initializer, or
|
||||
base object of PNK_DOT */
|
||||
ParseNode *expr; /* function body, var initializer, argument default,
|
||||
or base object of PNK_DOT */
|
||||
Definition *lexdef; /* lexical definition for this use */
|
||||
};
|
||||
UpvarCookie cookie; /* upvar cookie with absolute frame
|
||||
|
@ -1279,13 +1279,14 @@ LeaveFunction(ParseNode *fn, Parser *parser, PropertyName *funName = NULL,
|
||||
}
|
||||
|
||||
bool
|
||||
Parser::functionArguments(ParseNode **listp, bool &hasRest)
|
||||
Parser::functionArguments(ParseNode **listp, bool &hasDefaults, bool &hasRest)
|
||||
{
|
||||
if (tokenStream.getToken() != TOK_LP) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
hasDefaults = false;
|
||||
hasRest = false;
|
||||
|
||||
if (!tokenStream.matchToken(TOK_RP)) {
|
||||
@ -1308,6 +1309,11 @@ Parser::functionArguments(ParseNode **listp, bool &hasRest)
|
||||
/* See comment below in the TOK_NAME case. */
|
||||
if (duplicatedArg)
|
||||
goto report_dup_and_destructuring;
|
||||
if (hasDefaults) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
|
||||
return false;
|
||||
}
|
||||
|
||||
destructuringArg = true;
|
||||
|
||||
/*
|
||||
@ -1407,6 +1413,22 @@ Parser::functionArguments(ParseNode **listp, bool &hasRest)
|
||||
return false;
|
||||
if (!DefineArg(tc->sc->funbox->node, name, slot, this))
|
||||
return false;
|
||||
|
||||
if (tokenStream.matchToken(TOK_ASSIGN)) {
|
||||
if (hasRest) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_REST_WITH_DEFAULT);
|
||||
return false;
|
||||
}
|
||||
hasDefaults = true;
|
||||
ParseNode *def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT);
|
||||
if (!def_expr)
|
||||
return false;
|
||||
tc->sc->funbox->node->pn_body->last()->pn_expr = def_expr;
|
||||
} else if (!hasRest && hasDefaults) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1566,11 +1588,13 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
||||
|
||||
/* Now parse formal argument list and compute fun->nargs. */
|
||||
ParseNode *prelude = NULL;
|
||||
bool hasRest;
|
||||
if (!functionArguments(&prelude, hasRest))
|
||||
bool hasRest, hasDefaults;
|
||||
if (!functionArguments(&prelude, hasDefaults, hasRest))
|
||||
return NULL;
|
||||
|
||||
fun->setArgCount(funsc.bindings.numArgs());
|
||||
if (hasDefaults)
|
||||
fun->setHasDefaults();
|
||||
if (hasRest)
|
||||
fun->setHasRest();
|
||||
|
||||
@ -4939,7 +4963,7 @@ class GenexpGuard {
|
||||
}
|
||||
|
||||
void endBody();
|
||||
bool checkValidBody(ParseNode *pn);
|
||||
bool checkValidBody(ParseNode *pn, unsigned err);
|
||||
bool maybeNoteGenerator(ParseNode *pn);
|
||||
};
|
||||
|
||||
@ -4957,14 +4981,14 @@ GenexpGuard::endBody()
|
||||
* generator expression.
|
||||
*/
|
||||
bool
|
||||
GenexpGuard::checkValidBody(ParseNode *pn)
|
||||
GenexpGuard::checkValidBody(ParseNode *pn, unsigned err = JSMSG_BAD_GENEXP_BODY)
|
||||
{
|
||||
TreeContext *tc = parser->tc;
|
||||
if (tc->yieldCount > startYieldCount) {
|
||||
ParseNode *errorNode = tc->yieldNode;
|
||||
if (!errorNode)
|
||||
errorNode = pn;
|
||||
parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_yield_str);
|
||||
parser->reportErrorNumber(errorNode, JSREPORT_ERROR, err, js_yield_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5536,6 +5560,24 @@ static const char js_generator_str[] = "generator";
|
||||
#endif /* JS_HAS_GENERATOR_EXPRS */
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
ParseNode *
|
||||
Parser::assignExprWithoutYield(unsigned msg)
|
||||
{
|
||||
#ifdef JS_HAS_GENERATORS
|
||||
GenexpGuard yieldGuard(this);
|
||||
#endif
|
||||
ParseNode *res = assignExpr();
|
||||
if (res) {
|
||||
#ifdef JS_HAS_GENERATORS
|
||||
if (!yieldGuard.checkValidBody(res, msg)) {
|
||||
freeTree(res);
|
||||
res = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
JSBool
|
||||
Parser::argumentList(ParseNode *listNode)
|
||||
{
|
||||
|
@ -180,6 +180,7 @@ struct Parser : private AutoGCRooter
|
||||
VarContext varContext = HoistVars);
|
||||
ParseNode *expr();
|
||||
ParseNode *assignExpr();
|
||||
ParseNode *assignExprWithoutYield(unsigned err);
|
||||
ParseNode *condExpr1();
|
||||
ParseNode *orExpr1();
|
||||
ParseNode *andExpr1i();
|
||||
@ -209,7 +210,7 @@ struct Parser : private AutoGCRooter
|
||||
* Additional JS parsers.
|
||||
*/
|
||||
enum FunctionType { Getter, Setter, Normal };
|
||||
bool functionArguments(ParseNode **list, bool &hasRest);
|
||||
bool functionArguments(ParseNode **list, bool &hasDefaults, bool &hasRest);
|
||||
|
||||
ParseNode *functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind);
|
||||
|
||||
|
22
js/src/jit-test/tests/arguments/defaults-basic.js
Normal file
22
js/src/jit-test/tests/arguments/defaults-basic.js
Normal file
@ -0,0 +1,22 @@
|
||||
function f1(a, bIs, b=3) {
|
||||
assertEq(a, 1);
|
||||
assertEq(b, bIs);
|
||||
}
|
||||
assertEq(f1.length, 3);
|
||||
f1(1, 3);
|
||||
f1(1, 42, 42);
|
||||
function f2(a, bIs, cIs, b=3, c=4) {
|
||||
assertEq(a, 1);
|
||||
assertEq(b, bIs);
|
||||
assertEq(c, cIs);
|
||||
}
|
||||
assertEq(f2.length, 5);
|
||||
f2(1, 3, 4);
|
||||
f2(1, 42, 4, 42);
|
||||
f2(1, 42, 43, 42, 43);
|
||||
function f3(a, b, c=4) {
|
||||
assertEq(a, 1);
|
||||
assertEq(b, undefined);
|
||||
assertEq(c, 4);
|
||||
}
|
||||
f3(1);
|
34
js/src/jit-test/tests/arguments/defaults-decompile.js
Normal file
34
js/src/jit-test/tests/arguments/defaults-decompile.js
Normal file
@ -0,0 +1,34 @@
|
||||
function f1(a=1) {}
|
||||
assertEq(f1.toString(), "function f1(a = 1) {\n}");
|
||||
function f2(a=1, b=2, c=3) {}
|
||||
assertEq(f2.toString(), "function f2(a = 1, b = 2, c = 3) {\n}");
|
||||
function f3(a, b, c=1, d=2) {}
|
||||
assertEq(f3.toString(), "function f3(a, b, c = 1, d = 2) {\n}");
|
||||
function f4(a, [b], c=1) {}
|
||||
assertEq(f4.toString(), "function f4(a, [b], c = 1) {\n}");
|
||||
function f5(a, b, c=1, ...rest) {}
|
||||
assertEq(f5.toString(), "function f5(a, b, c = 1, ...rest) {\n}");
|
||||
function f6(a, [b], c=1, ...rest) {}
|
||||
assertEq(f6.toString(), "function f6(a, [b], c = 1, ...rest) {\n}");
|
||||
function f7(a, c=d = 190) {}
|
||||
assertEq(f7.toString(), "function f7(a, c = d = 190) {\n}");
|
||||
function f8(a=(b = 8)) {
|
||||
function nested() {
|
||||
return a + b;
|
||||
}
|
||||
return nested;
|
||||
}
|
||||
assertEq(f8.toString(), "function f8(a = b = 8) {\n\n\
|
||||
function nested() {\n\
|
||||
return a + b;\n\
|
||||
}\n\n\
|
||||
return nested;\n\
|
||||
}");
|
||||
function f9(a, b, c={complexity : .5, is : 40 + great.prop}, d=[42], ...rest) {}
|
||||
assertEq(f9.toString(), "function f9(a, b, c = {complexity: 0.5, is: 40 + great.prop}, d = [42], ...rest) {\n}");
|
||||
function f10(a=12) { return arguments; }
|
||||
assertEq(f10.toString(), "function f10(a = 12) {\n return arguments;\n}");
|
||||
function f11(a=(0, c=1)) {}
|
||||
assertEq(f11.length, 1);
|
||||
var g = eval("(" + f11 + ")");
|
||||
assertEq(g.length, 1);
|
27
js/src/jit-test/tests/arguments/defaults-evaluation-order.js
Normal file
27
js/src/jit-test/tests/arguments/defaults-evaluation-order.js
Normal file
@ -0,0 +1,27 @@
|
||||
function f1(a, bIs, cIs, dIs, b=a, c=d, d=5) {
|
||||
assertEq(a, 1);
|
||||
assertEq(b, bIs);
|
||||
assertEq(c, cIs);
|
||||
assertEq(d, dIs);
|
||||
}
|
||||
f1(1, 1, undefined, 5);
|
||||
f1(1, 42, undefined, 5, 42);
|
||||
f1(1, 42, 43, 5, 42, 43);
|
||||
f1(1, 42, 43, 44, 42, 43, 44);
|
||||
function f2(a=[]) { return a; }
|
||||
assertEq(f2() !== f2(), true);
|
||||
function f3(a=function () {}) { return a; }
|
||||
assertEq(f3() !== f3(), true);
|
||||
function f4(a=Date) { return a; }
|
||||
assertEq(f4(), Date);
|
||||
Date = 0;
|
||||
assertEq(f4(), 0);
|
||||
function f5(x=FAIL()) {}; // don't throw
|
||||
var n = 0;
|
||||
function f6(a=n++) {}
|
||||
assertEq(n, 0);
|
||||
function f7([a, b], A=a, B=b) {
|
||||
assertEq(A, a);
|
||||
assertEq(B, b);
|
||||
}
|
||||
f7([0, 1]);
|
6
js/src/jit-test/tests/arguments/defaults-exceptions.js
Normal file
6
js/src/jit-test/tests/arguments/defaults-exceptions.js
Normal file
@ -0,0 +1,6 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
function die() { throw "x"; }
|
||||
var ok = true;
|
||||
function f(a = die()) { ok = false; }
|
||||
assertThrowsValue(f, "x");
|
21
js/src/jit-test/tests/arguments/defaults-invalid-syntax.js
Normal file
21
js/src/jit-test/tests/arguments/defaults-invalid-syntax.js
Normal file
@ -0,0 +1,21 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("function f(...rest=23) {}");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("function f(a=16, b) {}");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("function f([a]=4) {}");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("function f(a=4, [b]) {}");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("function f(a=yield 24) {}");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("function f(a={a : 19 + (yield 24).prop}) {}");
|
||||
}, SyntaxError);
|
||||
function silly_but_okay(a=(function () { yield 97; })) {}
|
33
js/src/jit-test/tests/arguments/defaults-scoping.js
Normal file
33
js/src/jit-test/tests/arguments/defaults-scoping.js
Normal file
@ -0,0 +1,33 @@
|
||||
var x = 'global';
|
||||
function f(a=x) { // local variable x
|
||||
var x = 'local';
|
||||
return a;
|
||||
}
|
||||
assertEq(f(), undefined);
|
||||
|
||||
function g(f=function () { return ++x; }) { // closes on local variable x
|
||||
var x = 0;
|
||||
return f;
|
||||
}
|
||||
var gf = g();
|
||||
assertEq(gf(), 1);
|
||||
assertEq(gf(), 2);
|
||||
gf = g();
|
||||
assertEq(gf(), 1);
|
||||
|
||||
function h(f=function (s) { return eval(s); }) { // closes on local scope
|
||||
var x = 'hlocal';
|
||||
return f;
|
||||
}
|
||||
var hf = h();
|
||||
assertEq(hf('x'), 'hlocal');
|
||||
assertEq(hf('f'), hf);
|
||||
assertEq(hf('var x = 3; x'), 3);
|
||||
|
||||
function j(expr, v=eval(expr)) {
|
||||
return v;
|
||||
}
|
||||
assertEq(j("expr"), "expr");
|
||||
assertEq(j("v"), undefined);
|
||||
assertEq(j("Array"), Array);
|
||||
assertEq(j("arguments").length, 1);
|
10
js/src/jit-test/tests/arguments/defaults-with-arguments.js
Normal file
10
js/src/jit-test/tests/arguments/defaults-with-arguments.js
Normal file
@ -0,0 +1,10 @@
|
||||
function f(a=1, b=2, c=3) { return arguments; }
|
||||
var args = f();
|
||||
assertEq(args.length, 0);
|
||||
assertEq("0" in args, false);
|
||||
args = f(5, 6);
|
||||
assertEq(args.length, 2);
|
||||
assertEq(args[1], 6);
|
||||
args = f(9, 8, 7, 6, 5);
|
||||
assertEq(args.length, 5);
|
||||
assertEq(args[4], 5);
|
19
js/src/jit-test/tests/arguments/defaults-with-rest.js
Normal file
19
js/src/jit-test/tests/arguments/defaults-with-rest.js
Normal file
@ -0,0 +1,19 @@
|
||||
load(libdir + "eqArrayHelper.js");
|
||||
|
||||
function f1(a, bIs, b=3, ...rest) {
|
||||
assertEq(a, 1);
|
||||
assertEq(bIs, b);
|
||||
assertEqArray(rest, []);
|
||||
}
|
||||
assertEq(f1.length, 3);
|
||||
f1(1, 3);
|
||||
f1(1, 42, 42);
|
||||
function f2(a=rest, ...rest) {
|
||||
assertEq(a, undefined);
|
||||
}
|
||||
f2();
|
||||
function f3(a=rest, ...rest) {
|
||||
assertEq(a, 1);
|
||||
assertEqArray(rest, [2, 3, 4]);
|
||||
}
|
||||
f3(1, 2, 3, 4);
|
@ -349,3 +349,6 @@ MSG_DEF(JSMSG_PARAMETER_AFTER_REST, 295, 0, JSEXN_SYNTAXERR, "parameter after
|
||||
MSG_DEF(JSMSG_NO_REST_NAME, 296, 0, JSEXN_SYNTAXERR, "no parameter name after ...")
|
||||
MSG_DEF(JSMSG_ARGUMENTS_AND_REST, 297, 0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter")
|
||||
MSG_DEF(JSMSG_FUNCTION_ARGUMENTS_AND_REST, 298, 0, JSEXN_ERR, "the 'arguments' property of a function with a rest parameter may not be used")
|
||||
MSG_DEF(JSMSG_REST_WITH_DEFAULT, 299, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
|
||||
MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
|
||||
MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression")
|
||||
|
@ -2202,6 +2202,8 @@ class AutoIdRooter : private AutoGCRooter
|
||||
#define JSFUN_HAS_REST 0x0100 /* function has a rest (...) parameter */
|
||||
#define JSFUN_CONSTRUCTOR 0x0200 /* native that can be called as a ctor
|
||||
without creating a this object */
|
||||
#define JSFUN_HAS_DEFAULTS 0x0400 /* function has at least one default
|
||||
parameter */
|
||||
|
||||
#define JSFUN_FLAGS_MASK 0x07f8 /* overlay JSFUN_* attributes --
|
||||
bits 12-15 are used internally to
|
||||
|
@ -63,6 +63,7 @@ struct JSFunction : public JSObject
|
||||
} u;
|
||||
js::HeapPtrAtom atom; /* name for diagnostics and decompiling */
|
||||
|
||||
bool hasDefaults() const { return flags & JSFUN_HAS_DEFAULTS; }
|
||||
bool hasRest() const { return flags & JSFUN_HAS_REST; }
|
||||
bool isInterpreted() const { return kind() >= JSFUN_INTERPRETED; }
|
||||
bool isNative() const { return !isInterpreted(); }
|
||||
@ -91,6 +92,11 @@ struct JSFunction : public JSObject
|
||||
this->flags |= JSFUN_HAS_REST;
|
||||
}
|
||||
|
||||
void setHasDefaults() {
|
||||
JS_ASSERT(!hasDefaults());
|
||||
this->flags |= JSFUN_HAS_DEFAULTS;
|
||||
}
|
||||
|
||||
/* uint16_t representation bounds number of call object dynamic slots. */
|
||||
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
|
||||
|
||||
|
@ -3388,6 +3388,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||
case JSOP_RSH:
|
||||
case JSOP_LSH:
|
||||
case JSOP_URSH:
|
||||
case JSOP_ACTUALSFILLED:
|
||||
pushed[0].addType(cx, Type::Int32Type());
|
||||
break;
|
||||
case JSOP_FALSE:
|
||||
|
@ -2812,6 +2812,12 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH)
|
||||
END_VARLEN_CASE
|
||||
}
|
||||
|
||||
BEGIN_CASE(JSOP_ACTUALSFILLED)
|
||||
{
|
||||
PUSH_INT32(JS_MAX(regs.fp()->numActualArgs(), GET_UINT16(regs.pc)));
|
||||
}
|
||||
END_CASE(JSOP_ACTUALSFILLED)
|
||||
|
||||
BEGIN_CASE(JSOP_ARGUMENTS)
|
||||
JS_ASSERT(!regs.fp()->fun()->hasRest());
|
||||
if (script->needsArgsObj()) {
|
||||
|
@ -5533,11 +5533,18 @@ js_DecompileFunction(JSPrinter *jp)
|
||||
jp->script = script;
|
||||
#endif
|
||||
|
||||
jsbytecode *deftable = NULL;
|
||||
jsbytecode *defbegin = NULL;
|
||||
int32_t deflen = 0;
|
||||
uint16_t defstart = 0;
|
||||
unsigned nformal = fun->nargs - fun->hasRest();
|
||||
|
||||
for (unsigned i = 0; i < fun->nargs; i++) {
|
||||
if (i > 0)
|
||||
js_puts(jp, ", ");
|
||||
|
||||
if (i == unsigned(fun->nargs) - 1 && fun->hasRest())
|
||||
bool isRest = fun->hasRest() && i == unsigned(fun->nargs) - 1;
|
||||
if (isRest)
|
||||
js_puts(jp, "...");
|
||||
JSAtom *param = GetArgOrVarAtom(jp, i);
|
||||
|
||||
@ -5548,6 +5555,7 @@ js_DecompileFunction(JSPrinter *jp)
|
||||
ptrdiff_t todo;
|
||||
const char *lval;
|
||||
|
||||
JS_ASSERT(deflen == 0);
|
||||
LOCAL_ASSERT(*pc == JSOP_GETARG || *pc == JSOP_GETALIASEDVAR);
|
||||
pc += js_CodeSpec[*pc].length;
|
||||
LOCAL_ASSERT(*pc == JSOP_DUP);
|
||||
@ -5572,10 +5580,49 @@ js_DecompileFunction(JSPrinter *jp)
|
||||
continue;
|
||||
}
|
||||
|
||||
#undef LOCAL_ASSERT
|
||||
#endif
|
||||
|
||||
if (!QuoteString(&jp->sprinter, param, 0)) {
|
||||
// Compute default parameters.
|
||||
if ((*pc == JSOP_REST && pc[1] == JSOP_UNDEFINED) ||
|
||||
*pc == JSOP_ACTUALSFILLED) {
|
||||
#define SKIP(pc, op) LOCAL_ASSERT(*pc == op); pc += js_CodeSpec[op].length;
|
||||
JS_ASSERT(fun->hasDefaults());
|
||||
JS_ASSERT(deflen == 0);
|
||||
if (fun->hasRest()) {
|
||||
SKIP(pc, JSOP_REST);
|
||||
SKIP(pc, JSOP_UNDEFINED);
|
||||
JS_ASSERT(*pc == JSOP_SETARG || *pc == JSOP_SETALIASEDVAR);
|
||||
pc += js_CodeSpec[*pc].length;
|
||||
SKIP(pc, JSOP_POP);
|
||||
}
|
||||
SKIP(pc, JSOP_ACTUALSFILLED);
|
||||
JS_ASSERT(*pc == JSOP_TABLESWITCH);
|
||||
defbegin = pc;
|
||||
deflen = GET_JUMP_OFFSET(pc);
|
||||
pc += JUMP_OFFSET_LEN;
|
||||
defstart = GET_JUMP_OFFSET(pc);
|
||||
pc += JUMP_OFFSET_LEN;
|
||||
pc += JUMP_OFFSET_LEN; // Skip high
|
||||
deftable = pc;
|
||||
pc = defbegin + deflen;
|
||||
if (fun->hasRest()) {
|
||||
SKIP(pc, JSOP_SETARG);
|
||||
SKIP(pc, JSOP_POP);
|
||||
}
|
||||
#undef SKIP
|
||||
}
|
||||
|
||||
#undef LOCAL_ASSERT
|
||||
|
||||
if (fun->hasDefaults() && deflen && i >= defstart && !isRest) {
|
||||
#define TABLE_OFF(off) GET_JUMP_OFFSET(&deftable[(off)*JUMP_OFFSET_LEN])
|
||||
jsbytecode *casestart = defbegin + TABLE_OFF(i - defstart);
|
||||
jsbytecode *caseend = defbegin + ((i < nformal - 1) ? TABLE_OFF(i - defstart + 1) : deflen);
|
||||
#undef TABLE_OFF
|
||||
unsigned exprlength = caseend - casestart - js_CodeSpec[JSOP_POP].length;
|
||||
if (!DecompileCode(jp, script, casestart, exprlength, 0))
|
||||
return JS_FALSE;
|
||||
} else if (!QuoteString(&jp->sprinter, param, 0)) {
|
||||
ok = JS_FALSE;
|
||||
break;
|
||||
}
|
||||
|
@ -517,3 +517,5 @@ OPDEF(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 5, 0, 1, 0, JOF_ATOM)
|
||||
|
||||
/* This opcode is the target of the entry jump for some loop. */
|
||||
OPDEF(JSOP_LOOPENTRY, 227, "loopentry", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
OPDEF(JSOP_ACTUALSFILLED, 228, "actualsfilled", NULL, 3, 0, 1, 0, JOF_UINT16)
|
||||
|
@ -25,7 +25,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 - 115);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 116);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user