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);
|
||||
}
|
||||
~ProbesManager() { Probes::compileScriptEnd(filename, lineno); }
|
||||
};
|
||||
};
|
||||
ProbesManager probesManager(filename, lineno);
|
||||
|
||||
/*
|
||||
@ -231,6 +231,9 @@ frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!parser.checkForArgumentsAndRest())
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Nowadays the threaded interpreter needs a stop instruction, so we
|
||||
* do have to emit that here.
|
||||
|
@ -5890,6 +5890,19 @@ 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. */
|
||||
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);
|
||||
break;
|
||||
|
@ -669,22 +669,36 @@ Parser::functionBody(FunctionBodyType type)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
|
||||
* forces an 'arguments' binding.
|
||||
*/
|
||||
if (tc->sc->bindingsAccessedDynamically() && !tc->sc->bindings.hasBinding(context, arguments)) {
|
||||
bool hasRest = tc->sc->fun()->hasRest();
|
||||
BindingKind bindKind = tc->sc->bindings.lookup(context, arguments, NULL);
|
||||
switch (bindKind) {
|
||||
case NONE:
|
||||
/* 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))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that all possible 'arguments' bindings have been added, note whether
|
||||
* 'arguments' has a local binding and whether it unconditionally needs an
|
||||
* arguments object.
|
||||
*/
|
||||
BindingKind bindKind = tc->sc->bindings.lookup(context, arguments, NULL);
|
||||
if (bindKind == VARIABLE || bindKind == CONSTANT) {
|
||||
/* 'arguments' is now bound, so fall through. */
|
||||
case VARIABLE:
|
||||
case CONSTANT:
|
||||
if (hasRest) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARGUMENTS_AND_REST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
/* Dynamic scope access destroys all hope of optimization. */
|
||||
@ -705,13 +719,34 @@ Parser::functionBody(FunctionBodyType type)
|
||||
tc->sc->setFunDefinitelyNeedsArgsObj();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARGUMENT:
|
||||
break;
|
||||
}
|
||||
|
||||
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|.
|
||||
// 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
|
||||
@ -1250,13 +1285,15 @@ LeaveFunction(ParseNode *fn, Parser *parser, PropertyName *funName = NULL,
|
||||
}
|
||||
|
||||
bool
|
||||
Parser::functionArguments(ParseNode **listp)
|
||||
Parser::functionArguments(ParseNode **listp, bool &hasRest)
|
||||
{
|
||||
if (tokenStream.getToken() != TOK_LP) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
hasRest = false;
|
||||
|
||||
if (!tokenStream.matchToken(TOK_RP)) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
JSAtom *duplicatedArg = NULL;
|
||||
@ -1265,6 +1302,10 @@ Parser::functionArguments(ParseNode **listp)
|
||||
#endif
|
||||
|
||||
do {
|
||||
if (hasRest) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PARAMETER_AFTER_REST);
|
||||
return false;
|
||||
}
|
||||
switch (TokenKind tt = tokenStream.getToken()) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
@ -1326,6 +1367,18 @@ Parser::functionArguments(ParseNode **listp)
|
||||
}
|
||||
#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:
|
||||
{
|
||||
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. */
|
||||
ParseNode *prelude = NULL;
|
||||
if (!functionArguments(&prelude))
|
||||
bool hasRest;
|
||||
if (!functionArguments(&prelude, hasRest))
|
||||
return NULL;
|
||||
|
||||
fun->setArgCount(funsc.bindings.numArgs());
|
||||
if (hasRest)
|
||||
fun->setHasRest();
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
|
@ -146,6 +146,8 @@ struct Parser : private AutoGCRooter
|
||||
enum FunctionBodyType { StatementListBody, ExpressionBody };
|
||||
ParseNode *functionBody(FunctionBodyType type);
|
||||
|
||||
bool checkForArgumentsAndRest();
|
||||
|
||||
private:
|
||||
/*
|
||||
* JS parsers, from lowest to highest precedence.
|
||||
@ -208,7 +210,7 @@ struct Parser : private AutoGCRooter
|
||||
* Additional JS parsers.
|
||||
*/
|
||||
enum FunctionType { Getter, Setter, Normal };
|
||||
bool functionArguments(ParseNode **list);
|
||||
bool functionArguments(ParseNode **list, bool &hasRest);
|
||||
|
||||
ParseNode *functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind);
|
||||
|
||||
|
@ -1519,12 +1519,18 @@ TokenStream::getTokenInternal()
|
||||
numStart = userbuf.addressOfNextRawChar() - 2;
|
||||
goto decimal_dot;
|
||||
}
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (c == '.') {
|
||||
qc = getCharIgnoreEOL();
|
||||
if (qc == '.') {
|
||||
tt = TOK_TRIPLEDOT;
|
||||
goto out;
|
||||
}
|
||||
ungetCharIgnoreEOL(qc);
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
tt = TOK_DBLDOT;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ungetCharIgnoreEOL(c);
|
||||
tt = TOK_DOT;
|
||||
goto out;
|
||||
@ -2157,6 +2163,7 @@ TokenKindToString(TokenKind tt)
|
||||
case TOK_INC: return "TOK_INC";
|
||||
case TOK_DEC: return "TOK_DEC";
|
||||
case TOK_DOT: return "TOK_DOT";
|
||||
case TOK_TRIPLEDOT: return "TOK_TRIPLEDOT";
|
||||
case TOK_LB: return "TOK_LB";
|
||||
case TOK_RB: return "TOK_RB";
|
||||
case TOK_LC: return "TOK_LC";
|
||||
|
@ -47,6 +47,7 @@ enum TokenKind {
|
||||
TOK_MOD, /* modulus */
|
||||
TOK_INC, TOK_DEC, /* increment/decrement (++ --) */
|
||||
TOK_DOT, /* member operator (.) */
|
||||
TOK_TRIPLEDOT, /* for rest arguments (...) */
|
||||
TOK_LB, TOK_RB, /* left and right brackets */
|
||||
TOK_LC, TOK_RC, /* left and right curlies (braces) */
|
||||
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_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_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)
|
||||
|
||||
/* 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
|
||||
without creating a this object */
|
||||
|
||||
|
@ -102,6 +102,10 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, Value *vp)
|
||||
StackFrame *fp = iter.fp();
|
||||
|
||||
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. */
|
||||
if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage,
|
||||
NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) {
|
||||
@ -291,7 +295,7 @@ fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
||||
|
||||
Value v;
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
|
||||
v.setInt32(fun->nargs);
|
||||
v.setInt32(fun->nargs - fun->hasRest());
|
||||
else
|
||||
v.setString(fun->atom ? fun->atom : cx->runtime->emptyString);
|
||||
|
||||
@ -1003,6 +1007,8 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
Bindings bindings(cx);
|
||||
Bindings::StackRoot bindingsRoot(cx, &bindings);
|
||||
|
||||
bool hasRest = false;
|
||||
|
||||
const char *filename;
|
||||
unsigned lineno;
|
||||
JSPrincipals *originPrincipals;
|
||||
@ -1092,8 +1098,28 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
* Check that it's a name. This also implicitly guards against
|
||||
* TOK_ERROR, which was already reported.
|
||||
*/
|
||||
if (tt != TOK_NAME)
|
||||
return OnBadFormal(cx, tt);
|
||||
if (hasRest) {
|
||||
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. */
|
||||
RootedVar<PropertyName*> name(cx, ts.currentToken().name());
|
||||
@ -1156,6 +1182,9 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
if (!fun)
|
||||
return false;
|
||||
|
||||
if (hasRest)
|
||||
fun->setHasRest();
|
||||
|
||||
bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals,
|
||||
&bindings, chars, length, filename, lineno,
|
||||
cx->findVersion());
|
||||
|
@ -63,6 +63,7 @@ struct JSFunction : public JSObject
|
||||
} u;
|
||||
js::HeapPtrAtom atom; /* name for diagnostics and decompiling */
|
||||
|
||||
bool hasRest() const { return flags & JSFUN_HAS_REST; }
|
||||
bool isInterpreted() const { return kind() >= JSFUN_INTERPRETED; }
|
||||
bool isNative() const { return !isInterpreted(); }
|
||||
bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
|
||||
@ -85,6 +86,11 @@ struct JSFunction : public JSObject
|
||||
this->nargs = nargs;
|
||||
}
|
||||
|
||||
void setHasRest() {
|
||||
JS_ASSERT(!hasRest());
|
||||
this->flags |= JSFUN_HAS_REST;
|
||||
}
|
||||
|
||||
/* uint16_t representation bounds number of call object dynamic slots. */
|
||||
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());
|
||||
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: {
|
||||
jsid id = GetAtomId(cx, script, pc, 0);
|
||||
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_UNUSED14)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED15)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED16)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED17)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED18)
|
||||
ADD_EMPTY_CASE(JSOP_UNUSED19)
|
||||
@ -2814,6 +2813,7 @@ END_VARLEN_CASE
|
||||
}
|
||||
|
||||
BEGIN_CASE(JSOP_ARGUMENTS)
|
||||
JS_ASSERT(!regs.fp()->fun()->hasRest());
|
||||
if (script->needsArgsObj()) {
|
||||
ArgumentsObject *obj = ArgumentsObject::create(cx, regs.fp());
|
||||
if (!obj)
|
||||
@ -2824,6 +2824,17 @@ BEGIN_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_GETALIASEDVAR)
|
||||
{
|
||||
|
@ -5537,6 +5537,8 @@ js_DecompileFunction(JSPrinter *jp)
|
||||
if (i > 0)
|
||||
js_puts(jp, ", ");
|
||||
|
||||
if (i == unsigned(fun->nargs) - 1 && fun->hasRest())
|
||||
js_puts(jp, "...");
|
||||
JSAtom *param = GetArgOrVarAtom(jp, i);
|
||||
|
||||
#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_UNUSED29, 222,"unused29", 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. */
|
||||
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)
|
||||
{
|
||||
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(!callee->toFunction()->hasRest());
|
||||
|
||||
RootedVarObject proto(cx, callee->global().getOrCreateObjectPrototype(cx));
|
||||
if (!proto)
|
||||
|
@ -171,6 +171,15 @@ StackFrame::initFixupFrame(StackFrame *prev, StackFrame::Flags flags, void *ncod
|
||||
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 &
|
||||
StackFrame::canonicalActualArg(unsigned i) const
|
||||
{
|
||||
|
@ -502,6 +502,8 @@ class StackFrame
|
||||
|
||||
inline void initInlineFrame(JSFunction *fun, StackFrame *prevfp, jsbytecode *prevpc);
|
||||
|
||||
inline JSObject *createRestParameter(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Frame slots
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user