From: Dave Herman dherman, bug 574132: Prototype Harmony's rest parameters, r=? diff --git a/js/src/js.msg b/js/src/js.msg --- a/js/src/js.msg +++ b/js/src/js.msg @@ -325,8 +325,9 @@ MSG_DEF(JSMSG_DEFINE_ARRAY_LENGTH_UNSUPP MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array index property") MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 244, 0, JSEXN_ERR, "invalid or out-of-range index") MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0") MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 246, 0, JSEXN_ERR, "invalid arguments") MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blocked by CSP") MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function") MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler") MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument") +MSG_DEF(JSMSG_MISPLACED_REST_PARAM, 251, 0, JSEXN_SYNTAXERR, "rest-parameter must be the last formal parameter") diff --git a/js/src/jsemit.h b/js/src/jsemit.h --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -236,32 +236,44 @@ struct JSStmtInfo { * closure (i.e., a closure that needs only its global object for free variable * resolution, thanks to JSOP_{GET,CALL}UPVAR), because this function contains * a closure that needs one or more scope objects surrounding it (i.e., Call * object for a heavyweight outer function). See bug 560234. */ #define TCF_FUN_ENTRAINS_SCOPES 0x400000 /* + * Flag signifying that the current function's formal parameter list ends with + * a rest-parameter. + */ +#define TCF_FUN_REST_PARAMETER 0x800000 + +/* * Flags to check for return; vs. return expr; in a function. */ #define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID) /* * Sticky deoptimization flags to propagate from FunctionBody. */ #define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \ TCF_FUN_USES_ARGUMENTS | \ TCF_FUN_PARAM_ARGUMENTS | \ TCF_FUN_HEAVYWEIGHT | \ TCF_FUN_IS_GENERATOR | \ TCF_FUN_USES_OWN_NAME | \ TCF_HAS_SHARPS | \ TCF_STRICT_MODE_CODE) +/* + * Flags that disable the implicit binding of |arguments|. + */ +#define TCF_FUN_NO_ARGUMENTS (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_REST_PARAMETER) + + struct JSTreeContext { /* tree context for semantic checks */ uint32 flags; /* statement state flags, see above */ uint16 ngvars; /* max. no. of global variables/regexps */ uint32 bodyid; /* block number of program/function body */ uint32 blockidGen; /* preincremented block number generator */ JSStmtInfo *topStmt; /* top of statement info stack */ JSStmtInfo *topScopeStmt; /* top lexical scope statement */ JSObject *blockChain; /* compile time block scope chain (NB: one @@ -341,17 +353,17 @@ struct JSTreeContext { /* t bool compileAndGo() { return !!(flags & TCF_COMPILE_N_GO); } bool inFunction() { return !!(flags & TCF_IN_FUNCTION); } bool compiling() { return !!(flags & TCF_COMPILING); } /* * Test whether we're in a function that implicitly binds |arguments|. */ bool inArgumentsFunction() { - return !!((flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION); + return !!((flags & (TCF_IN_FUNCTION | TCF_FUN_NO_ARGUMENTS)) == TCF_IN_FUNCTION); } }; /* * Return true if we need to check for conditions that elicit * JSOPTION_STRICT warnings or strict mode errors. */ inline bool JSTreeContext::needStrictChecks() { diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -2698,20 +2698,26 @@ Parser::functionDef(uintN lambda, bool n if (!funbox) return NULL; JSFunction *fun = (JSFunction *) funbox->object; if (op != JSOP_NOP) fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; + bool rest = false; + /* Now parse formal argument list and compute fun->nargs. */ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); if (!tokenStream.matchToken(TOK_RP)) { do { + if (rest) { + reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISPLACED_REST_PARAM); + return NULL; + } tt = tokenStream.getToken(); switch (tt) { #if JS_HAS_DESTRUCTURING case TOK_LB: case TOK_LC: { BindData data; JSParseNode *lhs, *rhs; @@ -2766,16 +2772,26 @@ Parser::functionDef(uintN lambda, bool n list->pn_type = TOK_COMMA; list->makeEmpty(); } list->append(item); break; } #endif /* JS_HAS_DESTRUCTURING */ + case TOK_ELLIPSIS: + rest = true; + funtc.flags |= TCF_FUN_REST_PARAMETER; + tt = tokenStream.getToken(); + if (tt != TOK_NAME) { + reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL); + return NULL; + } + /* FALL THROUGH */ + case TOK_NAME: { JSAtom *atom = tokenStream.currentToken().t_atom; if (!DefineArg(pn, atom, fun->nargs, &funtc)) return NULL; #ifdef JS_HAS_DESTRUCTURING /* * ECMA-262 requires us to support duplicate parameter names, but if the diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -1279,22 +1279,30 @@ TokenStream::getTokenInternal() case '{': tt = TOK_LC; break; case '}': tt = TOK_RC; break; case '(': tt = TOK_LP; break; case ')': tt = TOK_RP; break; case ',': tt = TOK_COMMA; break; case '?': tt = TOK_HOOK; break; case '.': + if (matchChar(c)) { + if (matchChar(c)) { + tt = TOK_ELLIPSIS; + } else { #if JS_HAS_XML_SUPPORT - if (matchChar(c)) - tt = TOK_DBLDOT; - else + tt = TOK_DBLDOT; +#else + tt = TOK_DOT; + ungetChar(c); #endif + } + } else { tt = TOK_DOT; + } break; case ':': #if JS_HAS_XML_SUPPORT if (matchChar(c)) { tt = TOK_DBLCOLON; break; } diff --git a/js/src/jsscan.h b/js/src/jsscan.h --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -138,16 +138,17 @@ enum TokenKind { TOK_LET = 81, /* let keyword */ TOK_SEQ = 82, /* synthetic sequence of statements, not a block */ TOK_FORHEAD = 83, /* head of for(;;)-style loop */ TOK_ARGSBODY = 84, /* formal args in list + body at end */ TOK_UPVARS = 85, /* lexical dependencies as JSAtomList of definitions paired with a parse tree full of uses of those names */ + TOK_ELLIPSIS = 86, /* rest-param/spread operator (...) */ TOK_RESERVED, /* reserved keywords */ TOK_LIMIT /* domain size */ }; static inline bool TokenKindIsXML(TokenKind tt) { return tt == TOK_AT || tt == TOK_DBLCOLON || tt == TOK_ANYNAME;