mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 574132 - Implement rest parameters for JavaScript. r=jorendorff.
This commit is contained in:
parent
ced2b3f44a
commit
39bc920a58
@ -84,7 +84,7 @@ frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
|
|||||||
Probes::compileScriptBegin(filename, lineno);
|
Probes::compileScriptBegin(filename, lineno);
|
||||||
}
|
}
|
||||||
~ProbesManager() { Probes::compileScriptEnd(filename, lineno); }
|
~ProbesManager() { Probes::compileScriptEnd(filename, lineno); }
|
||||||
};
|
};
|
||||||
ProbesManager probesManager(filename, lineno);
|
ProbesManager probesManager(filename, lineno);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -231,6 +231,9 @@ frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!parser.checkForArgumentsAndRest())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Nowadays the threaded interpreter needs a stop instruction, so we
|
* Nowadays the threaded interpreter needs a stop instruction, so we
|
||||||
* do have to emit that here.
|
* do have to emit that here.
|
||||||
|
@ -5890,6 +5890,19 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||||||
if (!bce->noteClosedArg(pn2))
|
if (!bce->noteClosedArg(pn2))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
if (pn2->pn_next == pnlast && bce->sc->fun()->hasRest()) {
|
||||||
|
/* Fill rest parameter. */
|
||||||
|
JS_ASSERT(!bce->sc->funArgumentsHasLocalBinding());
|
||||||
|
bce->switchToProlog();
|
||||||
|
if (Emit1(cx, bce, JSOP_REST) < 0)
|
||||||
|
return false;
|
||||||
|
CheckTypeSet(cx, bce, JSOP_REST);
|
||||||
|
if (!EmitVarOp(cx, pn2, JSOP_SETARG, bce))
|
||||||
|
return false;
|
||||||
|
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||||
|
return false;
|
||||||
|
bce->switchToMain();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ok = EmitTree(cx, bce, pnlast);
|
ok = EmitTree(cx, bce, pnlast);
|
||||||
break;
|
break;
|
||||||
|
@ -669,22 +669,36 @@ Parser::functionBody(FunctionBodyType type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
bool hasRest = tc->sc->fun()->hasRest();
|
||||||
* Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
|
BindingKind bindKind = tc->sc->bindings.lookup(context, arguments, NULL);
|
||||||
* forces an 'arguments' binding.
|
switch (bindKind) {
|
||||||
*/
|
case NONE:
|
||||||
if (tc->sc->bindingsAccessedDynamically() && !tc->sc->bindings.hasBinding(context, arguments)) {
|
/* Functions with rest parameters are free from arguments. */
|
||||||
|
if (hasRest)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
|
||||||
|
* forces an 'arguments' binding.
|
||||||
|
*/
|
||||||
|
if (!tc->sc->bindingsAccessedDynamically())
|
||||||
|
break;
|
||||||
if (!tc->sc->bindings.addVariable(context, arguments))
|
if (!tc->sc->bindings.addVariable(context, arguments))
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/* 'arguments' is now bound, so fall through. */
|
||||||
* Now that all possible 'arguments' bindings have been added, note whether
|
case VARIABLE:
|
||||||
* 'arguments' has a local binding and whether it unconditionally needs an
|
case CONSTANT:
|
||||||
* arguments object.
|
if (hasRest) {
|
||||||
*/
|
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARGUMENTS_AND_REST);
|
||||||
BindingKind bindKind = tc->sc->bindings.lookup(context, arguments, NULL);
|
return NULL;
|
||||||
if (bindKind == VARIABLE || bindKind == CONSTANT) {
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that all possible 'arguments' bindings have been added, note whether
|
||||||
|
* 'arguments' has a local binding and whether it unconditionally needs an
|
||||||
|
* arguments object.
|
||||||
|
*/
|
||||||
tc->sc->setFunArgumentsHasLocalBinding();
|
tc->sc->setFunArgumentsHasLocalBinding();
|
||||||
|
|
||||||
/* Dynamic scope access destroys all hope of optimization. */
|
/* Dynamic scope access destroys all hope of optimization. */
|
||||||
@ -705,13 +719,34 @@ Parser::functionBody(FunctionBodyType type)
|
|||||||
tc->sc->setFunDefinitelyNeedsArgsObj();
|
tc->sc->setFunDefinitelyNeedsArgsObj();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ARGUMENT:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Parser::checkForArgumentsAndRest()
|
||||||
|
{
|
||||||
|
JS_ASSERT(!tc->sc->inFunction);
|
||||||
|
if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) {
|
||||||
|
PropertyName *arguments = context->runtime->atomState.argumentsAtom;
|
||||||
|
for (AtomDefnRange r = tc->lexdeps->all(); !r.empty(); r.popFront()) {
|
||||||
|
if (r.front().key() == arguments) {
|
||||||
|
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARGUMENTS_AND_REST);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* We're not in a function context, so we don't expect any bindings. */
|
||||||
|
JS_ASSERT(tc->sc->bindings.lookup(context, arguments, NULL) == NONE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a placeholder Definition node for |atom|.
|
// Create a placeholder Definition node for |atom|.
|
||||||
// Nb: unlike most functions that are passed a Parser, this one gets a
|
// Nb: unlike most functions that are passed a Parser, this one gets a
|
||||||
// SharedContext passed in separately, because in this case |sc| may not equal
|
// SharedContext passed in separately, because in this case |sc| may not equal
|
||||||
@ -1250,13 +1285,15 @@ LeaveFunction(ParseNode *fn, Parser *parser, PropertyName *funName = NULL,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Parser::functionArguments(ParseNode **listp)
|
Parser::functionArguments(ParseNode **listp, bool &hasRest)
|
||||||
{
|
{
|
||||||
if (tokenStream.getToken() != TOK_LP) {
|
if (tokenStream.getToken() != TOK_LP) {
|
||||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
|
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasRest = false;
|
||||||
|
|
||||||
if (!tokenStream.matchToken(TOK_RP)) {
|
if (!tokenStream.matchToken(TOK_RP)) {
|
||||||
#if JS_HAS_DESTRUCTURING
|
#if JS_HAS_DESTRUCTURING
|
||||||
JSAtom *duplicatedArg = NULL;
|
JSAtom *duplicatedArg = NULL;
|
||||||
@ -1265,6 +1302,10 @@ Parser::functionArguments(ParseNode **listp)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
if (hasRest) {
|
||||||
|
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PARAMETER_AFTER_REST);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
switch (TokenKind tt = tokenStream.getToken()) {
|
switch (TokenKind tt = tokenStream.getToken()) {
|
||||||
#if JS_HAS_DESTRUCTURING
|
#if JS_HAS_DESTRUCTURING
|
||||||
case TOK_LB:
|
case TOK_LB:
|
||||||
@ -1326,6 +1367,18 @@ Parser::functionArguments(ParseNode **listp)
|
|||||||
}
|
}
|
||||||
#endif /* JS_HAS_DESTRUCTURING */
|
#endif /* JS_HAS_DESTRUCTURING */
|
||||||
|
|
||||||
|
case TOK_TRIPLEDOT:
|
||||||
|
{
|
||||||
|
hasRest = true;
|
||||||
|
tt = tokenStream.getToken();
|
||||||
|
if (tt != TOK_NAME) {
|
||||||
|
if (tt != TOK_ERROR)
|
||||||
|
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_REST_NAME);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* Fall through */
|
||||||
|
}
|
||||||
|
|
||||||
case TOK_NAME:
|
case TOK_NAME:
|
||||||
{
|
{
|
||||||
RootedVar<PropertyName*> name(context, tokenStream.currentToken().name());
|
RootedVar<PropertyName*> name(context, tokenStream.currentToken().name());
|
||||||
@ -1519,10 +1572,13 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||||||
|
|
||||||
/* Now parse formal argument list and compute fun->nargs. */
|
/* Now parse formal argument list and compute fun->nargs. */
|
||||||
ParseNode *prelude = NULL;
|
ParseNode *prelude = NULL;
|
||||||
if (!functionArguments(&prelude))
|
bool hasRest;
|
||||||
|
if (!functionArguments(&prelude, hasRest))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
fun->setArgCount(funsc.bindings.numArgs());
|
fun->setArgCount(funsc.bindings.numArgs());
|
||||||
|
if (hasRest)
|
||||||
|
fun->setHasRest();
|
||||||
|
|
||||||
#if JS_HAS_DESTRUCTURING
|
#if JS_HAS_DESTRUCTURING
|
||||||
/*
|
/*
|
||||||
|
@ -146,6 +146,8 @@ struct Parser : private AutoGCRooter
|
|||||||
enum FunctionBodyType { StatementListBody, ExpressionBody };
|
enum FunctionBodyType { StatementListBody, ExpressionBody };
|
||||||
ParseNode *functionBody(FunctionBodyType type);
|
ParseNode *functionBody(FunctionBodyType type);
|
||||||
|
|
||||||
|
bool checkForArgumentsAndRest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* JS parsers, from lowest to highest precedence.
|
* JS parsers, from lowest to highest precedence.
|
||||||
@ -208,7 +210,7 @@ struct Parser : private AutoGCRooter
|
|||||||
* Additional JS parsers.
|
* Additional JS parsers.
|
||||||
*/
|
*/
|
||||||
enum FunctionType { Getter, Setter, Normal };
|
enum FunctionType { Getter, Setter, Normal };
|
||||||
bool functionArguments(ParseNode **list);
|
bool functionArguments(ParseNode **list, bool &hasRest);
|
||||||
|
|
||||||
ParseNode *functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind);
|
ParseNode *functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind);
|
||||||
|
|
||||||
|
@ -1519,12 +1519,18 @@ TokenStream::getTokenInternal()
|
|||||||
numStart = userbuf.addressOfNextRawChar() - 2;
|
numStart = userbuf.addressOfNextRawChar() - 2;
|
||||||
goto decimal_dot;
|
goto decimal_dot;
|
||||||
}
|
}
|
||||||
#if JS_HAS_XML_SUPPORT
|
|
||||||
if (c == '.') {
|
if (c == '.') {
|
||||||
|
qc = getCharIgnoreEOL();
|
||||||
|
if (qc == '.') {
|
||||||
|
tt = TOK_TRIPLEDOT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ungetCharIgnoreEOL(qc);
|
||||||
|
#if JS_HAS_XML_SUPPORT
|
||||||
tt = TOK_DBLDOT;
|
tt = TOK_DBLDOT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
ungetCharIgnoreEOL(c);
|
ungetCharIgnoreEOL(c);
|
||||||
tt = TOK_DOT;
|
tt = TOK_DOT;
|
||||||
goto out;
|
goto out;
|
||||||
@ -2157,6 +2163,7 @@ TokenKindToString(TokenKind tt)
|
|||||||
case TOK_INC: return "TOK_INC";
|
case TOK_INC: return "TOK_INC";
|
||||||
case TOK_DEC: return "TOK_DEC";
|
case TOK_DEC: return "TOK_DEC";
|
||||||
case TOK_DOT: return "TOK_DOT";
|
case TOK_DOT: return "TOK_DOT";
|
||||||
|
case TOK_TRIPLEDOT: return "TOK_TRIPLEDOT";
|
||||||
case TOK_LB: return "TOK_LB";
|
case TOK_LB: return "TOK_LB";
|
||||||
case TOK_RB: return "TOK_RB";
|
case TOK_RB: return "TOK_RB";
|
||||||
case TOK_LC: return "TOK_LC";
|
case TOK_LC: return "TOK_LC";
|
||||||
|
@ -47,6 +47,7 @@ enum TokenKind {
|
|||||||
TOK_MOD, /* modulus */
|
TOK_MOD, /* modulus */
|
||||||
TOK_INC, TOK_DEC, /* increment/decrement (++ --) */
|
TOK_INC, TOK_DEC, /* increment/decrement (++ --) */
|
||||||
TOK_DOT, /* member operator (.) */
|
TOK_DOT, /* member operator (.) */
|
||||||
|
TOK_TRIPLEDOT, /* for rest arguments (...) */
|
||||||
TOK_LB, TOK_RB, /* left and right brackets */
|
TOK_LB, TOK_RB, /* left and right brackets */
|
||||||
TOK_LC, TOK_RC, /* left and right curlies (braces) */
|
TOK_LC, TOK_RC, /* left and right curlies (braces) */
|
||||||
TOK_LP, TOK_RP, /* left and right parentheses */
|
TOK_LP, TOK_RP, /* left and right parentheses */
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
function f1(...arguments) {
|
||||||
|
assertEq("1,2,3", arguments.toString());
|
||||||
|
}
|
||||||
|
f1(1, 2, 3);
|
||||||
|
function f2(arguments, ...rest) {
|
||||||
|
assertEq(arguments, 42);
|
||||||
|
assertEq("1,2,3", rest.toString());
|
||||||
|
}
|
||||||
|
f2(42, 1, 2, 3);
|
15
js/src/jit-test/tests/arguments/rest-basic.js
Normal file
15
js/src/jit-test/tests/arguments/rest-basic.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
function check(expected, ...rest) {
|
||||||
|
assertEq(expected.toString(), rest.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEq(check.length, 1);
|
||||||
|
check([]);
|
||||||
|
check(['a', 'b'], 'a', 'b');
|
||||||
|
check(['a', 'b', 'c', 'd'], 'a', 'b', 'c', 'd');
|
||||||
|
check.apply(null, [['a', 'b'], 'a', 'b'])
|
||||||
|
check.call(null, ['a', 'b'], 'a', 'b')
|
||||||
|
|
||||||
|
var g = newGlobal('new-compartment');
|
||||||
|
g.eval("function f(...rest) { return rest; }");
|
||||||
|
var a = g.f(1, 2, 3);
|
||||||
|
assertEq(a instanceof g.Array, true);
|
17
js/src/jit-test/tests/arguments/rest-debugger.js
Normal file
17
js/src/jit-test/tests/arguments/rest-debugger.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
var g = newGlobal('new-compartment');
|
||||||
|
g.eval("function f(...x) {}");
|
||||||
|
var dbg = new Debugger;
|
||||||
|
var gw = dbg.addDebuggee(g);
|
||||||
|
var fw = gw.getOwnPropertyDescriptor("f").value;
|
||||||
|
assertEq(fw.parameterNames.toString(), "x");
|
||||||
|
|
||||||
|
var g = newGlobal('new-compartment');
|
||||||
|
g.eval("function f(...rest) { debugger; }");
|
||||||
|
var dbg = Debugger(g);
|
||||||
|
dbg.onDebuggerStatement = function (frame) {
|
||||||
|
var result = frame.eval("arguments");
|
||||||
|
assertEq("throw" in result, true);
|
||||||
|
var result2 = frame.evalWithBindings("exc instanceof SyntaxError", {exc: result.throw});
|
||||||
|
assertEq(result2.return, true);
|
||||||
|
};
|
||||||
|
g.f();
|
2
js/src/jit-test/tests/arguments/rest-decompile.js
Normal file
2
js/src/jit-test/tests/arguments/rest-decompile.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
g = (function (...rest) { return rest; });
|
||||||
|
assertEq(g.toString(), "function (...rest) {\n return rest;\n}");
|
@ -0,0 +1,8 @@
|
|||||||
|
"use strict";
|
||||||
|
load(libdir + "asserts.js");
|
||||||
|
assertThrowsInstanceOf(function () {
|
||||||
|
eval("(function (...arguments) {})");
|
||||||
|
}, SyntaxError);
|
||||||
|
assertThrowsInstanceOf(function () {
|
||||||
|
eval("(function (...eval) {})");
|
||||||
|
}, SyntaxError);
|
30
js/src/jit-test/tests/arguments/rest-disallow-arguments.js
Normal file
30
js/src/jit-test/tests/arguments/rest-disallow-arguments.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
load(libdir + "asserts.js");
|
||||||
|
var ieval = eval;
|
||||||
|
|
||||||
|
// Now for a tour of the various ways you can access arguments.
|
||||||
|
assertThrowsInstanceOf(function () {
|
||||||
|
ieval("function x(...rest) { arguments; }");
|
||||||
|
}, SyntaxError)
|
||||||
|
assertThrowsInstanceOf(function () {
|
||||||
|
Function("...rest", "arguments;");
|
||||||
|
}, SyntaxError);
|
||||||
|
assertThrowsInstanceOf(function (...rest) {
|
||||||
|
eval("arguments;");
|
||||||
|
}, SyntaxError);
|
||||||
|
assertThrowsInstanceOf(function (...rest) {
|
||||||
|
eval("arguments = 42;");
|
||||||
|
}, SyntaxError);
|
||||||
|
|
||||||
|
function g(...rest) {
|
||||||
|
assertThrowsInstanceOf(h, Error);
|
||||||
|
}
|
||||||
|
function h() {
|
||||||
|
g.arguments;
|
||||||
|
}
|
||||||
|
g();
|
||||||
|
|
||||||
|
// eval() is evil, but you can still use it with rest parameters!
|
||||||
|
function still_use_eval(...rest) {
|
||||||
|
eval("x = 4");
|
||||||
|
}
|
||||||
|
still_use_eval();
|
3
js/src/jit-test/tests/arguments/rest-in-Function.js
Normal file
3
js/src/jit-test/tests/arguments/rest-in-Function.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
h = Function("a", "b", "c", "...rest", "return rest.toString();");
|
||||||
|
assertEq(h.length, 3);
|
||||||
|
assertEq(h(1, 2, 3, 4, 5), "4,5");
|
12
js/src/jit-test/tests/arguments/rest-invalid-syntax.js
Normal file
12
js/src/jit-test/tests/arguments/rest-invalid-syntax.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
load(libdir + "asserts.js");
|
||||||
|
var ieval = eval;
|
||||||
|
var offenders = [["..."], ["...rest"," x"], ["...rest", "[x]"],
|
||||||
|
["...rest", "...rest2"]];
|
||||||
|
for (var arglist of offenders) {
|
||||||
|
assertThrowsInstanceOf(function () {
|
||||||
|
ieval("function x(" + arglist.join(", ") + ") {}");
|
||||||
|
}, SyntaxError);
|
||||||
|
assertThrowsInstanceOf(function () {
|
||||||
|
Function.apply(null, arglist.concat("return 0;"));
|
||||||
|
}, SyntaxError);
|
||||||
|
}
|
7
js/src/jit-test/tests/arguments/rest-nested-arguments.js
Normal file
7
js/src/jit-test/tests/arguments/rest-nested-arguments.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
function f(...rest) {
|
||||||
|
function nested() {
|
||||||
|
return arguments.length;
|
||||||
|
}
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
assertEq(f()(1, 2, 3), 3);
|
7
js/src/jit-test/tests/arguments/rest-nested.js
Normal file
7
js/src/jit-test/tests/arguments/rest-nested.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
function f(...rest) {
|
||||||
|
function nested () {
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
assertEq(f(1, 2, 3)().toString(), [1, 2, 3].toString());
|
9
js/src/jit-test/tests/arguments/rest-underflow.js
Normal file
9
js/src/jit-test/tests/arguments/rest-underflow.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
function f(a, b, c, ...rest) {
|
||||||
|
assertEq(a, 1);
|
||||||
|
assertEq(b, undefined);
|
||||||
|
assertEq(c, undefined);
|
||||||
|
assertEq(Array.isArray(rest), true);
|
||||||
|
assertEq(rest.length, 0);
|
||||||
|
assertEq(Object.getPrototypeOf(rest), Array.prototype);
|
||||||
|
}
|
||||||
|
f(1);
|
@ -345,3 +345,7 @@ MSG_DEF(JSMSG_NOT_ITERABLE, 291, 1, JSEXN_TYPEERR, "{0} is not iterabl
|
|||||||
MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 292, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'url' property")
|
MSG_DEF(JSMSG_QUERY_LINE_WITHOUT_URL, 292, 0, JSEXN_TYPEERR, "findScripts query object has 'line' property, but no 'url' property")
|
||||||
MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 293, 0, JSEXN_TYPEERR, "findScripts query object has 'innermost' property without both 'url' and 'line' properties")
|
MSG_DEF(JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL, 293, 0, JSEXN_TYPEERR, "findScripts query object has 'innermost' property without both 'url' and 'line' properties")
|
||||||
MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND, 294, 0, JSEXN_TYPEERR, "variable not found in environment")
|
MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND, 294, 0, JSEXN_TYPEERR, "variable not found in environment")
|
||||||
|
MSG_DEF(JSMSG_PARAMETER_AFTER_REST, 295, 0, JSEXN_SYNTAXERR, "parameter after rest parameter")
|
||||||
|
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")
|
||||||
|
@ -2192,7 +2192,7 @@ class AutoIdRooter : private AutoGCRooter
|
|||||||
|
|
||||||
#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT)
|
#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT)
|
||||||
|
|
||||||
/* 0x0100 is unused */
|
#define JSFUN_HAS_REST 0x0100 /* function has a rest (...) parameter */
|
||||||
#define JSFUN_CONSTRUCTOR 0x0200 /* native that can be called as a ctor
|
#define JSFUN_CONSTRUCTOR 0x0200 /* native that can be called as a ctor
|
||||||
without creating a this object */
|
without creating a this object */
|
||||||
|
|
||||||
|
@ -102,6 +102,10 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, Value *vp)
|
|||||||
StackFrame *fp = iter.fp();
|
StackFrame *fp = iter.fp();
|
||||||
|
|
||||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
|
if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
|
||||||
|
if (fun->hasRest()) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_FUNCTION_ARGUMENTS_AND_REST);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/* Warn if strict about f.arguments or equivalent unqualified uses. */
|
/* Warn if strict about f.arguments or equivalent unqualified uses. */
|
||||||
if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage,
|
if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage,
|
||||||
NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) {
|
NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) {
|
||||||
@ -291,7 +295,7 @@ fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
|||||||
|
|
||||||
Value v;
|
Value v;
|
||||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
|
if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
|
||||||
v.setInt32(fun->nargs);
|
v.setInt32(fun->nargs - fun->hasRest());
|
||||||
else
|
else
|
||||||
v.setString(fun->atom ? fun->atom : cx->runtime->emptyString);
|
v.setString(fun->atom ? fun->atom : cx->runtime->emptyString);
|
||||||
|
|
||||||
@ -1003,6 +1007,8 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
|||||||
Bindings bindings(cx);
|
Bindings bindings(cx);
|
||||||
Bindings::StackRoot bindingsRoot(cx, &bindings);
|
Bindings::StackRoot bindingsRoot(cx, &bindings);
|
||||||
|
|
||||||
|
bool hasRest = false;
|
||||||
|
|
||||||
const char *filename;
|
const char *filename;
|
||||||
unsigned lineno;
|
unsigned lineno;
|
||||||
JSPrincipals *originPrincipals;
|
JSPrincipals *originPrincipals;
|
||||||
@ -1092,8 +1098,28 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
|||||||
* Check that it's a name. This also implicitly guards against
|
* Check that it's a name. This also implicitly guards against
|
||||||
* TOK_ERROR, which was already reported.
|
* TOK_ERROR, which was already reported.
|
||||||
*/
|
*/
|
||||||
if (tt != TOK_NAME)
|
if (hasRest) {
|
||||||
return OnBadFormal(cx, tt);
|
ReportCompileErrorNumber(cx, &ts, NULL, JSREPORT_ERROR,
|
||||||
|
JSMSG_PARAMETER_AFTER_REST);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tt != TOK_NAME) {
|
||||||
|
if (tt == TOK_TRIPLEDOT) {
|
||||||
|
hasRest = true;
|
||||||
|
tt = ts.getToken();
|
||||||
|
if (tt != TOK_NAME) {
|
||||||
|
if (tt != TOK_ERROR)
|
||||||
|
ReportCompileErrorNumber(cx, &ts, NULL,
|
||||||
|
JSREPORT_ERROR,
|
||||||
|
JSMSG_NO_REST_NAME);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return OnBadFormal(cx, tt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for a duplicate parameter name. */
|
/* Check for a duplicate parameter name. */
|
||||||
RootedVar<PropertyName*> name(cx, ts.currentToken().name());
|
RootedVar<PropertyName*> name(cx, ts.currentToken().name());
|
||||||
@ -1156,6 +1182,9 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
|||||||
if (!fun)
|
if (!fun)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (hasRest)
|
||||||
|
fun->setHasRest();
|
||||||
|
|
||||||
bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals,
|
bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals,
|
||||||
&bindings, chars, length, filename, lineno,
|
&bindings, chars, length, filename, lineno,
|
||||||
cx->findVersion());
|
cx->findVersion());
|
||||||
|
@ -63,6 +63,7 @@ struct JSFunction : public JSObject
|
|||||||
} u;
|
} u;
|
||||||
js::HeapPtrAtom atom; /* name for diagnostics and decompiling */
|
js::HeapPtrAtom atom; /* name for diagnostics and decompiling */
|
||||||
|
|
||||||
|
bool hasRest() const { return flags & JSFUN_HAS_REST; }
|
||||||
bool isInterpreted() const { return kind() >= JSFUN_INTERPRETED; }
|
bool isInterpreted() const { return kind() >= JSFUN_INTERPRETED; }
|
||||||
bool isNative() const { return !isInterpreted(); }
|
bool isNative() const { return !isInterpreted(); }
|
||||||
bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
|
bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
|
||||||
@ -85,6 +86,11 @@ struct JSFunction : public JSObject
|
|||||||
this->nargs = nargs;
|
this->nargs = nargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setHasRest() {
|
||||||
|
JS_ASSERT(!hasRest());
|
||||||
|
this->flags |= JSFUN_HAS_REST;
|
||||||
|
}
|
||||||
|
|
||||||
/* uint16_t representation bounds number of call object dynamic slots. */
|
/* uint16_t representation bounds number of call object dynamic slots. */
|
||||||
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
|
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
|
||||||
|
|
||||||
|
@ -3642,6 +3642,21 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||||||
pushed[0].addType(cx, Type::MagicArgType());
|
pushed[0].addType(cx, Type::MagicArgType());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case JSOP_REST: {
|
||||||
|
TypeSet *types = script->analysis()->bytecodeTypes(pc);
|
||||||
|
types->addSubset(cx, &pushed[0]);
|
||||||
|
if (script->hasGlobal()) {
|
||||||
|
TypeObject *rest = TypeScript::InitObject(cx, script, pc, JSProto_Array);
|
||||||
|
if (!rest)
|
||||||
|
return false;
|
||||||
|
types->addType(cx, Type::ObjectType(rest));
|
||||||
|
} else {
|
||||||
|
types->addType(cx, Type::UnknownType());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
case JSOP_SETPROP: {
|
case JSOP_SETPROP: {
|
||||||
jsid id = GetAtomId(cx, script, pc, 0);
|
jsid id = GetAtomId(cx, script, pc, 0);
|
||||||
poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
|
poppedTypes(pc, 1)->addSetProperty(cx, script, pc, poppedTypes(pc, 0), id);
|
||||||
|
@ -1516,7 +1516,6 @@ ADD_EMPTY_CASE(JSOP_UNUSED12)
|
|||||||
ADD_EMPTY_CASE(JSOP_UNUSED13)
|
ADD_EMPTY_CASE(JSOP_UNUSED13)
|
||||||
ADD_EMPTY_CASE(JSOP_UNUSED14)
|
ADD_EMPTY_CASE(JSOP_UNUSED14)
|
||||||
ADD_EMPTY_CASE(JSOP_UNUSED15)
|
ADD_EMPTY_CASE(JSOP_UNUSED15)
|
||||||
ADD_EMPTY_CASE(JSOP_UNUSED16)
|
|
||||||
ADD_EMPTY_CASE(JSOP_UNUSED17)
|
ADD_EMPTY_CASE(JSOP_UNUSED17)
|
||||||
ADD_EMPTY_CASE(JSOP_UNUSED18)
|
ADD_EMPTY_CASE(JSOP_UNUSED18)
|
||||||
ADD_EMPTY_CASE(JSOP_UNUSED19)
|
ADD_EMPTY_CASE(JSOP_UNUSED19)
|
||||||
@ -2814,6 +2813,7 @@ END_VARLEN_CASE
|
|||||||
}
|
}
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_ARGUMENTS)
|
BEGIN_CASE(JSOP_ARGUMENTS)
|
||||||
|
JS_ASSERT(!regs.fp()->fun()->hasRest());
|
||||||
if (script->needsArgsObj()) {
|
if (script->needsArgsObj()) {
|
||||||
ArgumentsObject *obj = ArgumentsObject::create(cx, regs.fp());
|
ArgumentsObject *obj = ArgumentsObject::create(cx, regs.fp());
|
||||||
if (!obj)
|
if (!obj)
|
||||||
@ -2824,6 +2824,17 @@ BEGIN_CASE(JSOP_ARGUMENTS)
|
|||||||
}
|
}
|
||||||
END_CASE(JSOP_ARGUMENTS)
|
END_CASE(JSOP_ARGUMENTS)
|
||||||
|
|
||||||
|
BEGIN_CASE(JSOP_REST)
|
||||||
|
{
|
||||||
|
JSObject *rest = regs.fp()->createRestParameter(cx);
|
||||||
|
if (!rest)
|
||||||
|
goto error;
|
||||||
|
PUSH_COPY(ObjectValue(*rest));
|
||||||
|
if (!SetInitializerObjectType(cx, script, regs.pc, rest))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
END_CASE(JSOP_REST)
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_CALLALIASEDVAR)
|
BEGIN_CASE(JSOP_CALLALIASEDVAR)
|
||||||
BEGIN_CASE(JSOP_GETALIASEDVAR)
|
BEGIN_CASE(JSOP_GETALIASEDVAR)
|
||||||
{
|
{
|
||||||
|
@ -5537,6 +5537,8 @@ js_DecompileFunction(JSPrinter *jp)
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
js_puts(jp, ", ");
|
js_puts(jp, ", ");
|
||||||
|
|
||||||
|
if (i == unsigned(fun->nargs) - 1 && fun->hasRest())
|
||||||
|
js_puts(jp, "...");
|
||||||
JSAtom *param = GetArgOrVarAtom(jp, i);
|
JSAtom *param = GetArgOrVarAtom(jp, i);
|
||||||
|
|
||||||
#if JS_HAS_DESTRUCTURING
|
#if JS_HAS_DESTRUCTURING
|
||||||
|
@ -506,7 +506,8 @@ OPDEF(JSOP_UNUSED24, 220,"unused24", NULL, 1, 0, 0, 0, JOF_BYTE)
|
|||||||
OPDEF(JSOP_UNUSED25, 221,"unused25", NULL, 1, 0, 0, 0, JOF_BYTE)
|
OPDEF(JSOP_UNUSED25, 221,"unused25", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||||
OPDEF(JSOP_UNUSED29, 222,"unused29", NULL, 1, 0, 0, 0, JOF_BYTE)
|
OPDEF(JSOP_UNUSED29, 222,"unused29", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||||
OPDEF(JSOP_UNUSED30, 223,"unused30", NULL, 1, 0, 0, 0, JOF_BYTE)
|
OPDEF(JSOP_UNUSED30, 223,"unused30", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||||
OPDEF(JSOP_UNUSED16, 224,"unused16", NULL, 1, 0, 0, 0, JOF_BYTE)
|
|
||||||
|
OPDEF(JSOP_REST, 224, "rest", NULL, 1, 0, 1, 0, JOF_BYTE|JOF_TYPESET)
|
||||||
|
|
||||||
/* Pop the stack, convert to a jsid (int or string), and push back. */
|
/* Pop the stack, convert to a jsid (int or string), and push back. */
|
||||||
OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE)
|
OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||||
|
@ -56,6 +56,7 @@ ArgumentsObject *
|
|||||||
ArgumentsObject::create(JSContext *cx, uint32_t argc, HandleObject callee)
|
ArgumentsObject::create(JSContext *cx, uint32_t argc, HandleObject callee)
|
||||||
{
|
{
|
||||||
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
|
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
|
||||||
|
JS_ASSERT(!callee->toFunction()->hasRest());
|
||||||
|
|
||||||
RootedVarObject proto(cx, callee->global().getOrCreateObjectPrototype(cx));
|
RootedVarObject proto(cx, callee->global().getOrCreateObjectPrototype(cx));
|
||||||
if (!proto)
|
if (!proto)
|
||||||
|
@ -171,6 +171,15 @@ StackFrame::initFixupFrame(StackFrame *prev, StackFrame::Flags flags, void *ncod
|
|||||||
u.nactual = nactual;
|
u.nactual = nactual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline JSObject *
|
||||||
|
StackFrame::createRestParameter(JSContext *cx)
|
||||||
|
{
|
||||||
|
JS_ASSERT(fun()->hasRest());
|
||||||
|
unsigned nformal = fun()->nargs - 1, nactual = numActualArgs();
|
||||||
|
unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
|
||||||
|
return NewDenseCopiedArray(cx, nrest, actualArgs() + nformal);
|
||||||
|
}
|
||||||
|
|
||||||
inline Value &
|
inline Value &
|
||||||
StackFrame::canonicalActualArg(unsigned i) const
|
StackFrame::canonicalActualArg(unsigned i) const
|
||||||
{
|
{
|
||||||
|
@ -502,6 +502,8 @@ class StackFrame
|
|||||||
|
|
||||||
inline void initInlineFrame(JSFunction *fun, StackFrame *prevfp, jsbytecode *prevpc);
|
inline void initInlineFrame(JSFunction *fun, StackFrame *prevfp, jsbytecode *prevpc);
|
||||||
|
|
||||||
|
inline JSObject *createRestParameter(JSContext *cx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frame slots
|
* Frame slots
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user