diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index a5c844642d3c..1b7fc6495671 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -7086,6 +7086,22 @@ DoubleToAtom(ExclusiveContext *cx, double value) return ToAtom(cx, HandleValue::fromMarkedLocation(&tmp)); } +template +typename ParseHandler::Node +Parser::computedPropertyName(Node literal) +{ + uint32_t begin = pos().begin; + Node assignNode = assignExpr(); + if (!assignNode) + return null(); + MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR); + Node propname = handler.newComputedName(assignNode, begin, pos().end); + if (!propname) + return null(); + handler.setListFlag(literal, PNX_NONCONST); + return propname; +} + template typename ParseHandler::Node Parser::objectLiteral() @@ -7121,16 +7137,9 @@ Parser::objectLiteral() break; case TOK_LB: { - // Computed property name. - uint32_t begin = pos().begin; - Node assignNode = assignExpr(); - if (!assignNode) - return null(); - MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR); - propname = handler.newComputedName(assignNode, begin, pos().end); + propname = computedPropertyName(literal); if (!propname) return null(); - handler.setListFlag(literal, PNX_NONCONST); break; } @@ -7186,6 +7195,10 @@ Parser::objectLiteral() propname = newNumber(tokenStream.currentToken()); if (!propname) return null(); + } else if (tt == TOK_LB) { + propname = computedPropertyName(literal); + if (!propname) + return null(); } else { // Not an accessor property after all. tokenStream.ungetToken(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b68000575236..0e8bb3da633e 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -624,6 +624,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node pushLetScope(Handle blockObj, StmtInfoPC *stmt); bool noteNameUse(HandlePropertyName name, Node pn); Node objectLiteral(); + Node computedPropertyName(Node literal); Node arrayInitializer(); Node newRegExp(); diff --git a/js/src/tests/ecma_6/Class/compPropNames.js b/js/src/tests/ecma_6/Class/compPropNames.js index 2164204f9b31..a79f456846b2 100644 --- a/js/src/tests/ecma_6/Class/compPropNames.js +++ b/js/src/tests/ecma_6/Class/compPropNames.js @@ -54,6 +54,7 @@ assertEq(a.foo1, 1); assertEq(a.foo2, 2); assertEq(a.foo3, 3); +var expr = "abc"; syntaxError("({["); syntaxError("({[expr"); syntaxError("({[expr]"); @@ -174,8 +175,69 @@ assertEq(next.done, true); assertEq(next.value.hello, 2); assertEq(next.value.world, 3); -syntaxError("a = {get [expr]() { return 3; }, set[expr](v) { return 2; }}"); +// get and set. +expr = "abc"; +syntaxError("({get ["); +syntaxError("({get [expr()"); +syntaxError("({get [expr]()"); +syntaxError("({get [expr]()})"); +syntaxError("({get [expr] 0 ()})"); +syntaxError("({get [expr], 0(})"); +syntaxError("[get [expr]: 0()]"); +syntaxError("({get [expr](: name: 0})"); +syntaxError("({get [1, 2](): 3})"); +syntaxError("({get [1;](): 1})"); +syntaxError("({get [if (0) 0;](){}})"); +syntaxError("({set [(a)"); +syntaxError("({set [expr(a)"); +syntaxError("({set [expr](a){}"); +syntaxError("({set [expr]}(a)"); +syntaxError("({set [expr](a), 0})"); +syntaxError("[set [expr](a): 0]"); +syntaxError("({set [expr](a): name: 0})"); +syntaxError("({set [1, 2](a) {return 3;}})"); +syntaxError("({set [1;](a) {return 1}})"); +syntaxError("({set [if (0) 0;](a){}})"); +syntaxError("function f() { {get [x](): 1} }"); +syntaxError("function f() { get [x](): 1 }"); +syntaxError("function f() { {set [x](a): 1} }"); +syntaxError("function f() { set [x](a): 1 }"); +f1 = "abc"; +syntaxError('a = {get [f1@](){}, set [f1](a){}}'); // unexpected symbol at end of AssignmentExpression +syntaxError('a = {get@ [f1](){}, set [f1](a){}}'); // unexpected symbol after get +syntaxError('a = {get [f1](){}, set@ [f1](a){}}'); // unexpected symbol after set +expr = "hey"; +a = {get [expr]() { return 3; }, set[expr](v) { throw 2; }}; +assertEq(a.hey, 3); +assertThrowsValue(() => { a.hey = 5; }, 2); + +// Symbols with duplicate get and set. +expr = Symbol("hey"); +a = {get [expr]() { return 3; }, set[expr](v) { throw 2; }, + set [expr] (w) { throw 4; }, get[expr](){return 5; }}; +assertEq(a[expr], 5); +assertThrowsValue(() => { a[expr] = 7; }, 4); + +// expressions with side effects are called in the right order +log = ""; +obj = { + "a": log += 'a', + get [log += 'b']() {}, + [log += 'c']: log += 'd', + set [log += 'e'](a) {} +}; +assertEq(log, "abcde"); + +// assignment expressions, objects and regex in computed names +obj = { + get [a = "hey"]() { return 1; }, + get [a = {b : 4}.b]() { return 2; }, + set [/x/.source](a) { throw 3; } +} +assertEq(obj.hey, 1); +assertEq(obj[4], 2); +assertThrowsValue(() => { obj.x = 7; }, 3); reportCompare(0, 0, "ok"); 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 73e08bc65d20..5bdf84a04e2e 100644 --- a/js/src/tests/js1_8_5/extensions/reflect-parse.js +++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js @@ -402,6 +402,13 @@ var node = Reflect.parse("a = {[field1]: 5}"); Pattern({ body: [ { expression: { right: { properties: [ {key: { loc: { start: { line: 1, column: 5 }, end: { line: 1, column: 13 }}}}]}}}]}).match(node); +// Bug 1048384 - Getter/setter syntax with computed names +assertExpr("b = { get [meth]() { } }", aExpr("=", ident("b"), + objExpr([{ key: computedName(ident("meth")), value: funExpr(null, [], blockStmt([])), + method: false, kind: "get"}]))); +assertExpr("b = { set [meth](a) { } }", aExpr("=", ident("b"), + objExpr([{ key: computedName(ident("meth")), value: funExpr(null, [ident("a")], + blockStmt([])), method: false, kind: "set"}]))); // statements