diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 5ce457661ab..23c69b0b889 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -374,6 +374,27 @@ class NodeBuilder setResult(node, dst); } + bool newNode(ASTType type, TokenPos *pos, + const char *childName1, Value child1, + const char *childName2, Value child2, + const char *childName3, Value child3, + const char *childName4, Value child4, + const char *childName5, Value child5, + const char *childName6, Value child6, + const char *childName7, Value child7, + Value *dst) { + JSObject *node; + return newNode(type, pos, &node) && + setProperty(node, childName1, child1) && + setProperty(node, childName2, child2) && + setProperty(node, childName3, child3) && + setProperty(node, childName4, child4) && + setProperty(node, childName5, child5) && + setProperty(node, childName6, child6) && + setProperty(node, childName7, child7) && + setResult(node, dst); + } + bool listNode(ASTType type, const char *propName, NodeVector &elts, TokenPos *pos, Value *dst) { Value array; if (!newArray(elts, &array)) @@ -434,8 +455,9 @@ class NodeBuilder bool identifier(Value name, TokenPos *pos, Value *dst); bool function(ASTType type, TokenPos *pos, - Value id, NodeVector &args, Value body, - bool isGenerator, bool isExpression, Value *dst); + Value id, NodeVector &args, NodeVector &defaults, + Value body, Value rest, bool isGenerator, bool isExpression, + Value *dst); bool variableDeclarator(Value id, Value init, TokenPos *pos, Value *dst); @@ -1327,13 +1349,16 @@ NodeBuilder::arrayPattern(NodeVector &elts, TokenPos *pos, Value *dst) bool NodeBuilder::function(ASTType type, TokenPos *pos, - Value id, NodeVector &args, Value body, + Value id, NodeVector &args, NodeVector &defaults, + Value body, Value rest, bool isGenerator, bool isExpression, Value *dst) { - Value array; + Value array, defarray; if (!newArray(args, &array)) return false; + if (!newArray(defaults, &defarray)) + return false; Value cb = callbacks[type]; if (!cb.isNull()) { @@ -1344,7 +1369,9 @@ NodeBuilder::function(ASTType type, TokenPos *pos, return newNode(type, pos, "id", id, "params", array, + "defaults", defarray, "body", body, + "rest", rest, "generator", BooleanValue(isGenerator), "expression", BooleanValue(isExpression), dst); @@ -1557,7 +1584,7 @@ class ASTSerializer bool xmls(ParseNode *pn, NodeVector &elts); bool leftAssociate(ParseNode *pn, Value *dst); bool functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, ParseNode *pnbody, - NodeVector &args); + NodeVector &args, NodeVector &defaults, Value *rest); bool sourceElement(ParseNode *pn, Value *dst); @@ -1612,7 +1639,8 @@ class ASTSerializer bool objectPattern(ParseNode *pn, VarDeclKind *pkind, Value *dst); bool function(ParseNode *pn, ASTType type, Value *dst); - bool functionArgsAndBody(ParseNode *pn, NodeVector &args, Value *body); + bool functionArgsAndBody(ParseNode *pn, NodeVector &args, NodeVector &defaults, + Value *body, Value *rest); bool functionBody(ParseNode *pn, TokenPos *pos, Value *dst); bool comprehensionBlock(ParseNode *pn, Value *dst); @@ -2935,18 +2963,26 @@ ASTSerializer::function(ParseNode *pn, ASTType type, Value *dst) return false; NodeVector args(cx); + NodeVector defaults(cx); ParseNode *argsAndBody = pn->pn_body->isKind(PNK_UPVARS) ? pn->pn_body->pn_tree : pn->pn_body; Value body; - return functionArgsAndBody(argsAndBody, args, &body) && - builder.function(type, &pn->pn_pos, id, args, body, isGenerator, isExpression, dst); + Value rest; + if (func->hasRest()) + rest.setUndefined(); + else + rest.setNull(); + return functionArgsAndBody(argsAndBody, args, defaults, &body, &rest) && + builder.function(type, &pn->pn_pos, id, args, defaults, body, + rest, isGenerator, isExpression, dst); } bool -ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, Value *body) +ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, + NodeVector &defaults, Value *body, Value *rest) { ParseNode *pnargs; ParseNode *pnbody; @@ -2977,7 +3013,7 @@ ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, Value *body) /* Serialize the arguments and body. */ switch (pnbody->getKind()) { case PNK_RETURN: /* expression closure, no destructured args */ - return functionArgs(pn, pnargs, NULL, pnbody, args) && + return functionArgs(pn, pnargs, NULL, pnbody, args, defaults, rest) && expression(pnbody->pn_kid, body); case PNK_SEQ: /* expression closure with destructured args */ @@ -2985,7 +3021,7 @@ ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, Value *body) ParseNode *pnstart = pnbody->pn_head->pn_next; LOCAL_ASSERT(pnstart && pnstart->isKind(PNK_RETURN)); - return functionArgs(pn, pnargs, pndestruct, pnbody, args) && + return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && expression(pnstart->pn_kid, body); } @@ -2995,7 +3031,7 @@ ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, Value *body) ? pnbody->pn_head->pn_next : pnbody->pn_head; - return functionArgs(pn, pnargs, pndestruct, pnbody, args) && + return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) && functionBody(pnstart, &pnbody->pn_pos, body); } @@ -3006,7 +3042,8 @@ ASTSerializer::functionArgsAndBody(ParseNode *pn, NodeVector &args, Value *body) bool ASTSerializer::functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, - ParseNode *pnbody, NodeVector &args) + ParseNode *pnbody, NodeVector &args, NodeVector &defaults, + Value *rest) { uint32_t i = 0; ParseNode *arg = pnargs ? pnargs->pn_head : NULL; @@ -3038,14 +3075,25 @@ ASTSerializer::functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestr * index in the formals list, so we rely on the ability to * ask destructuring args their index above. */ - if (!identifier(arg, &node) || !args.append(node)) + if (!identifier(arg, &node)) return false; + if (rest->isUndefined() && arg->pn_next == pnbody) + rest->setObject(node.toObject()); + else if (!args.append(node)) + return false; + if (arg->pn_dflags & PND_DEFAULT) { + ParseNode *expr = arg->isDefn() ? arg->expr() : arg->pn_kid->pn_right; + Value def; + if (!expression(expr, &def) || !defaults.append(def)) + return false; + } arg = arg->pn_next; } else { LOCAL_NOT_REACHED("missing function argument"); } ++i; } + JS_ASSERT(!rest->isUndefined()); return true; } diff --git a/js/src/tests/js1_8_5/extensions/reflect-parse.js b/js/src/tests/js1_8_5/extensions/reflect-parse.js index e68b87af734..c5b8da46f3a 100644 --- a/js/src/tests/js1_8_5/extensions/reflect-parse.js +++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js @@ -25,14 +25,18 @@ function returnStmt(expr) Pattern({ type: "ReturnStatement", argument: expr }) function yieldExpr(expr) Pattern({ type: "YieldExpression", argument: expr }) function lit(val) Pattern({ type: "Literal", value: val }) var thisExpr = Pattern({ type: "ThisExpression" }); -function funDecl(id, params, body) Pattern({ type: "FunctionDeclaration", - id: id, - params: params, - body: body, - generator: false }) +function funDecl(id, params, body, defaults=[], rest=null) Pattern( + { type: "FunctionDeclaration", + id: id, + params: params, + defaults: defaults, + body: body, + rest: rest, + generator: false }) function genFunDecl(id, params, body) Pattern({ type: "FunctionDeclaration", id: id, params: params, + defaults: [], body: body, generator: true }) function varDecl(decls) Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" }) @@ -213,6 +217,14 @@ assertDecl("function foo() { }", assertDecl("function foo() { return 42 }", funDecl(ident("foo"), [], blockStmt([returnStmt(lit(42))]))); +assertDecl("function foo(...rest) { }", + funDecl(ident("foo"), [], blockStmt([]), [], ident("rest"))); + +assertDecl("function foo(a=4) { }", funDecl(ident("foo"), [ident("a")], blockStmt([]), [lit(4)])); +assertDecl("function foo(a, b=4) { }", funDecl(ident("foo"), [ident("a"), ident("b")], blockStmt([]), [lit(4)])); +assertDecl("function foo(a, b=4, ...rest) { }", + funDecl(ident("foo"), [ident("a"), ident("b")], blockStmt([]), [lit(4)], ident("rest"))); + // Bug 591437: rebound args have their defs turned into uses assertDecl("function f(a) { function a() { } }",