Bug 924688 - Implement ES6 computed property names. r=jorendorff
authorGuptha Rajagopal <gupta.rajagopal@gmail.com>
Fri, 08 Aug 2014 09:15:00 -0400
changeset 198757 7079b7552946
parent 198756 affd7637e02e
child 198758 e13073de5179
push id27284
push userryanvm@gmail.com
push dateSat, 09 Aug 2014 15:25:31 +0000
treeherdermozilla-central@ad8cb646fad6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs924688
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 924688 - Implement ES6 computed property names. r=jorendorff
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
js/src/jit-test/tests/class/compProp.js
js/src/js.msg
js/src/jsast.tbl
js/src/jsreflect.cpp
js/src/tests/ecma_6/Class/browser.js
js/src/tests/ecma_6/Class/compPropDestr.js
js/src/tests/ecma_6/Class/compPropNames.js
js/src/tests/ecma_6/Class/shell.js
js/src/tests/js1_8_5/extensions/reflect-parse.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3237,37 +3237,41 @@ EmitDestructuringOpsHelper(ExclusiveCont
         } else {
             JS_ASSERT(pn->isKind(PNK_OBJECT));
             JS_ASSERT(pn2->isKind(PNK_COLON) || pn2->isKind(PNK_SHORTHAND));
 
             ParseNode *key = pn2->pn_left;
             if (key->isKind(PNK_NUMBER)) {
                 if (!EmitNumberOp(cx, key->pn_dval, bce))
                     return false;
-            } else {
-                MOZ_ASSERT(key->isKind(PNK_STRING) || key->isKind(PNK_NAME));
+            } else if (key->isKind(PNK_NAME) || key->isKind(PNK_STRING)) {
                 PropertyName *name = key->pn_atom->asPropertyName();
 
                 // The parser already checked for atoms representing indexes and
                 // used PNK_NUMBER instead, but also watch for ids which TI treats
                 // as indexes for simplification of downstream analysis.
                 jsid id = NameToId(name);
                 if (id != types::IdToTypeId(id)) {
                     if (!EmitTree(cx, bce, key))
                         return false;
                 } else {
                     if (!EmitAtomOp(cx, name, JSOP_GETPROP, bce))
                         return false;
                     doElemOp = false;
                 }
+            } else {
+                JS_ASSERT(key->isKind(PNK_COMPUTED_NAME));
+                if (!EmitTree(cx, bce, key->pn_kid))
+                    return false;
             }
 
             pn3 = pn2->pn_right;
         }
 
+
         if (doElemOp) {
             /*
              * Ok, get the value of the matching property name.  This leaves
              * that value on top of the value being destructured, so the stack
              * is one deeper than when we started.
              */
             if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
                 return false;
@@ -6074,27 +6078,31 @@ EmitObject(ExclusiveContext *cx, Bytecod
 
         /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
         ParseNode *pn3 = pn2->pn_left;
         bool isIndex = false;
         if (pn3->isKind(PNK_NUMBER)) {
             if (!EmitNumberOp(cx, pn3->pn_dval, bce))
                 return false;
             isIndex = true;
-        } else {
+        } else if (pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING)) {
             // The parser already checked for atoms representing indexes and
             // used PNK_NUMBER instead, but also watch for ids which TI treats
             // as indexes for simpliciation of downstream analysis.
-            JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
             jsid id = NameToId(pn3->pn_atom->asPropertyName());
             if (id != types::IdToTypeId(id)) {
                 if (!EmitTree(cx, bce, pn3))
                     return false;
                 isIndex = true;
             }
+        } else {
+            JS_ASSERT(pn3->isKind(PNK_COMPUTED_NAME));
+            if (!EmitTree(cx, bce, pn3->pn_kid))
+                return false;
+            isIndex = true;
         }
 
         /* Emit code for the property initializer. */
         if (!EmitTree(cx, bce, pn2->pn_right))
             return false;
 
         JSOp op = pn2->getOp();
         JS_ASSERT(op == JSOP_INITPROP ||
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -85,16 +85,21 @@ class FullParseHandler
     ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
     void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
     const Token &currentToken() { return tokenStream.currentToken(); }
 
     ParseNode *newName(PropertyName *name, uint32_t blockid, const TokenPos &pos) {
         return new_<NameNode>(PNK_NAME, JSOP_NAME, name, blockid, pos);
     }
 
+    ParseNode *newComputedName(ParseNode *expr, uint32_t begin, uint32_t end) {
+        TokenPos pos(begin, end);
+        return new_<UnaryNode>(PNK_COMPUTED_NAME, JSOP_NOP, pos, expr);
+    }
+
     Definition *newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
         Definition *dn =
             (Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, blockid, pos);
         if (!dn)
             return nullptr;
         dn->setDefn(true);
         dn->pn_dflags |= PND_PLACEHOLDER;
         return dn;
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -84,16 +84,17 @@ class UpvarCookie
     F(ELEM) \
     F(ARRAY) \
     F(ELISION) \
     F(STATEMENTLIST) \
     F(LABEL) \
     F(OBJECT) \
     F(CALL) \
     F(NAME) \
+    F(COMPUTED_NAME) \
     F(NUMBER) \
     F(STRING) \
     F(TEMPLATE_STRING_LIST) \
     F(TEMPLATE_STRING) \
     F(TAGGED_TEMPLATE) \
     F(CALLSITEOBJ) \
     F(REGEXP) \
     F(TRUE) \
@@ -388,16 +389,18 @@ enum ParseNodeKind
  *                          [,,] holes are represented by PNK_ELISION nodes
  *                          pn_xflags: PN_ENDCOMMA if extra comma at end
  * PNK_OBJECT   list        pn_head: list of pn_count binary PNK_COLON nodes
  * PNK_COLON    binary      key-value pair in object initializer or
  *                          destructuring lhs
  *                          pn_left: property id, pn_right: value
  * PNK_SHORTHAND binary     Same fields as PNK_COLON. This is used for object
  *                          literal properties using shorthand ({x}).
+ * PNK_COMPUTED_NAME unary  ES6 ComputedPropertyName.
+ *                          pn_kid: the AssignmentExpression inside the square brackets
  * PNK_NAME,    name        pn_atom: name, string, or object atom
  * PNK_STRING               pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT
  *                          If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR
  *                          with pn_cookie telling (staticLevel, slot) (see
  *                          jsscript.h's UPVAR macros) and pn_dflags telling
  *                          const-ness and static analysis results
  * PNK_TEMPLATE_STRING_LIST pn_head: list of alternating expr and template strings
  *              list
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -7254,16 +7254,30 @@ Parser<ParseHandler>::objectLiteral()
         switch (ltok) {
           case TOK_NUMBER:
             atom = DoubleToAtom(context, tokenStream.currentToken().number());
             if (!atom)
                 return null();
             propname = newNumber(tokenStream.currentToken());
             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);
+              if (!propname)
+                  return null();
+              handler.setListFlag(literal, PNX_NONCONST);
+              break;
+          }
+
           case TOK_NAME: {
             atom = tokenStream.currentName();
             if (atom == context->names().get) {
                 op = JSOP_INITPROP_GETTER;
             } else if (atom == context->names().set) {
                 op = JSOP_INITPROP_SETTER;
             } else {
                 propname = handler.newIdentifier(atom, pos());
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -56,16 +56,20 @@ class SyntaxParseHandler
 
     void trace(JSTracer *trc) {}
 
     Node newName(PropertyName *name, uint32_t blockid, const TokenPos &pos) {
         lastAtom = name;
         return NodeName;
     }
 
+    Node newComputedName(Node expr, uint32_t start, uint32_t end) {
+        return NodeName;
+    }
+
     DefinitionNode newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
         return Definition::PLACEHOLDER;
     }
 
     Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeString; }
     Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { return NodeGeneric; }
     Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/class/compProp.js
@@ -0,0 +1,15 @@
+load(libdir + "asserts.js");
+
+function f(tag) { return {[tag]: 1}; }
+a = [];
+for (var i = 0; i < 2000; i++)
+    a[i] = f("first");
+
+for (var i = 0; i < 2000; i++)
+    assertEq(a[i].first, 1);
+
+for (var i = 0; i < 2000; i++)
+    a[i] = f("second");
+
+for (var i = 0; i < 2000; i++)
+    assertEq(a[i].second, 1);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -438,8 +438,9 @@ MSG_DEF(JSMSG_INVALID_ARG_TYPE,         
 MSG_DEF(JSMSG_TERMINATED,               384, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP, 385, 1, JSEXN_ERR, "No such property on self-hosted object: {0}")
 MSG_DEF(JSMSG_PROXY_EXTENSIBILITY,      386, 0, JSEXN_TYPEERR, "proxy must report same extensiblitity as target")
 MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT,   387, 0, JSEXN_TYPEERR, "proxy [[Construct]] must return an object")
 MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF,  388, 0, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined")
 MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC,      389, 0, JSEXN_TYPEERR, "proxy can't report existing configurable property as non-configurable")
 MSG_DEF(JSMSG_PROXY_REVOKED,            390, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
 MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR,     391, 0, JSEXN_SYNTAXERR, "missing } in template string")
+MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR,    392, 0, JSEXN_SYNTAXERR, "missing ] in computed property name")
--- a/js/src/jsast.tbl
+++ b/js/src/jsast.tbl
@@ -68,10 +68,11 @@ ASTDEF(AST_CATCH,                 "Catch
 ASTDEF(AST_COMP_BLOCK,            "ComprehensionBlock",             "comprehensionBlock")
 
 ASTDEF(AST_ARRAY_PATT,            "ArrayPattern",                   "arrayPattern")
 ASTDEF(AST_OBJECT_PATT,           "ObjectPattern",                  "objectPattern")
 ASTDEF(AST_PROP_PATT,             "Property",                       "propertyPattern")
 ASTDEF(AST_TEMPLATE_LITERAL,      "TemplateLiteral",                "templateLiteral")
 ASTDEF(AST_TAGGED_TEMPLATE,       "TaggedTemplate",                 "taggedTemplate")
 ASTDEF(AST_CALL_SITE_OBJ,         "CallSiteObject",                 "callSiteObject")
+ASTDEF(AST_COMPUTED_NAME,         "ComputedName",                   "computedName")
 
 /* AST_LIMIT = last + 1 */
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -442,16 +442,35 @@ class NodeBuilder
 
     bool newNode(ASTType type, TokenPos *pos,
                  const char *childName1, HandleValue child1,
                  const char *childName2, HandleValue child2,
                  const char *childName3, HandleValue child3,
                  const char *childName4, HandleValue child4,
                  const char *childName5, HandleValue child5,
                  const char *childName6, HandleValue child6,
+                 MutableHandleValue dst) {
+        RootedObject node(cx);
+        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) &&
+               setResult(node, dst);
+    }
+
+    bool newNode(ASTType type, TokenPos *pos,
+                 const char *childName1, HandleValue child1,
+                 const char *childName2, HandleValue child2,
+                 const char *childName3, HandleValue child3,
+                 const char *childName4, HandleValue child4,
+                 const char *childName5, HandleValue child5,
+                 const char *childName6, HandleValue child6,
                  const char *childName7, HandleValue child7,
                  MutableHandleValue dst) {
         RootedObject node(cx);
         return newNode(type, pos, &node) &&
                setProperty(node, childName1, child1) &&
                setProperty(node, childName2, child2) &&
                setProperty(node, childName3, child3) &&
                setProperty(node, childName4, child4) &&
@@ -633,16 +652,18 @@ class NodeBuilder
 
     bool taggedTemplate(HandleValue callee, NodeVector &args, TokenPos *pos,
                         MutableHandleValue dst);
 
     bool callSiteObj(NodeVector &raw, NodeVector &cooked, TokenPos *pos, MutableHandleValue dst);
 
     bool spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst);
 
+    bool computedName(HandleValue name, TokenPos *pos, MutableHandleValue dst);
+
     bool objectExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst);
 
     bool thisExpression(TokenPos *pos, MutableHandleValue dst);
 
     bool yieldExpression(HandleValue arg, YieldKind kind, TokenPos *pos, MutableHandleValue dst);
 
     bool comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos *pos,
                             MutableHandleValue dst);
@@ -1249,16 +1270,24 @@ NodeBuilder::taggedTemplate(HandleValue 
 
 bool
 NodeBuilder::templateLiteral(NodeVector &elts, TokenPos *pos, MutableHandleValue dst)
 {
     return listNode(AST_TEMPLATE_LITERAL, "elements", elts, pos, dst);
 }
 
 bool
+NodeBuilder::computedName(HandleValue name, TokenPos *pos, MutableHandleValue dst)
+{
+    return newNode(AST_COMPUTED_NAME, pos,
+                   "name", name,
+                   dst);
+}
+
+bool
 NodeBuilder::spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst)
 {
     return newNode(AST_SPREAD_EXPR, pos,
                    "expression", expr,
                    dst);
 }
 
 bool
@@ -2868,16 +2897,23 @@ ASTSerializer::expression(ParseNode *pn,
 
       case PNK_SPREAD:
       {
           RootedValue expr(cx);
           return expression(pn->pn_kid, &expr) &&
                  builder.spreadExpression(expr, &pn->pn_pos, dst);
       }
 
+      case PNK_COMPUTED_NAME:
+      {
+         RootedValue name(cx);
+         return expression(pn->pn_kid, &name) &&
+                builder.computedName(name, &pn->pn_pos, dst);
+      }
+
       case PNK_OBJECT:
       {
         NodeVector elts(cx);
         if (!elts.reserve(pn->pn_count))
             return false;
 
         for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
             JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
@@ -2957,16 +2993,18 @@ ASTSerializer::expression(ParseNode *pn,
       default:
         LOCAL_NOT_REACHED("unexpected expression type");
     }
 }
 
 bool
 ASTSerializer::propertyName(ParseNode *pn, MutableHandleValue dst)
 {
+    if (pn->isKind(PNK_COMPUTED_NAME))
+        return expression(pn, dst);
     if (pn->isKind(PNK_NAME))
         return identifier(pn, dst);
 
     LOCAL_ASSERT(pn->isKind(PNK_STRING) || pn->isKind(PNK_NUMBER));
 
     return literal(pn, dst);
 }
 
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/compPropDestr.js
@@ -0,0 +1,11 @@
+var BUGNUMBER = 924688;
+var summary = 'Computed Property Names';
+
+print(BUGNUMBER + ": " + summary);
+
+var key = "z";
+var { [key]: foo } = { z: "bar" };
+assertEq(foo, "bar");
+
+
+reportCompare(0, 0, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/compPropNames.js
@@ -0,0 +1,181 @@
+var BUGNUMBER = 924688;
+var summary = 'Computed Property Names';
+
+print(BUGNUMBER + ": " + summary);
+
+// Function definitions.
+function syntaxError (script) {
+    try {
+        Function(script);
+    } catch (e) {
+        if (e instanceof SyntaxError) {
+            return;
+        }
+    }
+    throw new Error('Expected syntax error: ' + script);
+}
+
+
+// Tests begin.
+
+assertThrowsInstanceOf(function() { var a = {[field1]: "a", [field2]: "b"}; }, ReferenceError);
+
+assertThrowsInstanceOf(function() {
+                           field1 = 1;
+                           var a = {[field1]: "a", [field2]: "b"};
+                       }, ReferenceError);
+
+var f1 = 1;
+var f2 = 2;
+var a = {[f1]: "a", [f2]: "b"};
+assertEq(a[1], "a");
+assertEq(a[2], "b");
+
+var a = {[f1]: "a", f2: "b"};
+assertEq(a[1], "a");
+assertEq(a.f2, "b");
+
+var a = {["f1"]: "a", [++f2]: "b"};
+assertEq(a.f1, "a");
+assertEq(a[3], "b");
+
+var a = {["f1"]: "a", f2: "b"};
+assertEq(a.f1, "a");
+assertEq(a.f2, "b");
+
+
+var i = 0;
+var a = {
+    ["foo" + ++i]: i,
+    ["foo" + ++i]: i,
+    ["foo" + ++i]: i
+};
+assertEq(a.foo1, 1);
+assertEq(a.foo2, 2);
+assertEq(a.foo3, 3);
+
+syntaxError("({[");
+syntaxError("({[expr");
+syntaxError("({[expr]");
+syntaxError("({[expr]})");
+syntaxError("({[expr] 0})");
+syntaxError("({[expr], 0})");
+syntaxError("[[expr]: 0]");
+syntaxError("({[expr]: name: 0})");
+syntaxError("({[1, 2]: 3})");  // because '1,2' is an Expression but not an AssignmentExpression
+syntaxError("({[1;]: 1})");    // and not an ExpressionStatement
+syntaxError("({[if (0) 0;]})");  // much less a Statement
+syntaxError("function f() { {[x]: 1} }");  // that's not even an ObjectLiteral
+syntaxError("function f() { [x]: 1 }");    // or that
+syntaxError('a = {[f1@]: "a", [f2]: "b"}'); // unexpected symbol at end of AssignmentExpression
+try { JSON.parse('{["a"]:4}'); } catch(e) {
+    if (!(e instanceof SyntaxError)) throw new Error('Expected syntax error');
+}
+
+// Property characteristics.
+a = { ["b"] : 4 };
+b = Object.getOwnPropertyDescriptor(a, "b");
+assertEq(b.configurable, true);
+assertEq(b.enumerable, true);
+assertEq(b.writable, true);
+assertEq(b.value, 4);
+
+// Setter and getter are not hit.
+Object.defineProperty(Object.prototype, "x", { set: function (x) { throw "FAIL"; },
+                                               get: function (x) { throw "FAIL"; } });
+var a = {["x"]: 0};
+assertEq(a.x, 0);
+
+a = {["x"]: 1, ["x"]: 2};
+assertEq(a.x, 2);
+a = {x: 1, ["x"]: 2};
+assertEq(a.x, 2);
+a = {["x"]: 1, x: 2};
+assertEq(a.x, 2);
+
+// Symbols
+var unique_sym = Symbol("1"), registered_sym = Symbol.for("2");
+a = { [unique_sym] : 2, [registered_sym] : 3 };
+assertEq(a[unique_sym], 2);
+assertEq(a[registered_sym], 3);
+
+// Same expression can be run several times to build objects with different property names.
+a = [];
+for (var i = 0; i < 3; i++) {
+    a[i] = {["foo" + i]: i};
+}
+assertEq(a[0].foo0, 0);
+assertEq(a[1].foo1, 1);
+assertEq(a[2].foo2, 2);
+
+// Following are stored in object's elements rather than slots.
+var i = 0;
+a = { [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i
+}
+for (var i = 1; i < 9; i++)
+    assertEq(a[i], i);
+syntaxError("a.1");
+syntaxError("a.2");
+syntaxError("a.3");
+syntaxError("a.4");
+syntaxError("a.5");
+syntaxError("a.6");
+syntaxError("a.7");
+syntaxError("a.8");
+
+// Adding a single large index.
+var i = 0;
+a = { [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [++i] : i,
+      [3000] : 2999
+}
+for (var i = 1; i < 9; i++)
+    assertEq(a[i], i);
+assertEq(a[3000], 2999);
+
+// Defining several properties using eval.
+var code = "({";
+for (i = 0; i < 1000; i++)
+    code += "['foo' + " + i + "]: 'ok', "
+code += "['bar']: 'ok'});";
+var obj = eval(code);
+for (i = 0; i < 1000; i++)
+    assertEq(obj["foo" + i], "ok");
+assertEq(obj["bar"], "ok");
+
+// Can yield in a computed property name which is in a generator.
+function* g() {
+    var a = { [yield 1]: 2, [yield 2]: 3};
+    return a;
+}
+
+var it = g();
+var next = it.next();
+assertEq(next.done, false);
+assertEq(next.value, 1);
+next = it.next("hello");
+assertEq(next.done, false);
+assertEq(next.value, 2);
+next = it.next("world");
+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; }}");
+
+
+
+reportCompare(0, 0, "ok");
new file mode 100644
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js
+++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js
@@ -87,16 +87,17 @@ function updExpr(op, arg, prefix) Patter
 function logExpr(op, left, right) Pattern({ type: "LogicalExpression", operator: op, left: left, right: right })
 
 function condExpr(test, cons, alt) Pattern({ type: "ConditionalExpression", test: test, consequent: cons, alternate: alt })
 function seqExpr(exprs) Pattern({ type: "SequenceExpression", expressions: exprs })
 function newExpr(callee, args) Pattern({ type: "NewExpression", callee: callee, arguments: args })
 function callExpr(callee, args) Pattern({ type: "CallExpression", callee: callee, arguments: args })
 function arrExpr(elts) Pattern({ type: "ArrayExpression", elements: elts })
 function objExpr(elts) Pattern({ type: "ObjectExpression", properties: elts })
+function computedName(elts) Pattern({ type: "ComputedName", name: elts })
 function templateLit(elts) Pattern({ type: "TemplateLiteral", elements: elts })
 function taggedTemplate(tagPart, templatePart) Pattern({ type: "TaggedTemplate", callee: tagPart,
                 arguments : templatePart })
 function template(raw, cooked, ...args) Pattern([{ type: "CallSiteObject", raw: raw, cooked:
 cooked}, ...args])
 function compExpr(body, blocks, filter) Pattern({ type: "ComprehensionExpression", body: body, blocks: blocks, filter: filter })
 function genExpr(body, blocks, filter) Pattern({ type: "GeneratorExpression", body: body, blocks: blocks, filter: filter })
 function graphExpr(idx, body) Pattern({ type: "GraphExpression", index: idx, expression: body })
@@ -374,16 +375,39 @@ assertExpr("typeof(0?0:a)", unExpr("type
 assertExpr("[x for each (x in y) if (false)]", compExpr(ident("x"), [compEachBlock(ident("x"), ident("y"))], lit(false)));
 
 // Bug 632056: constant-folding
 program([exprStmt(ident("f")),
          ifStmt(lit(1),
                 funDecl(ident("f"), [], blockStmt([])),
                 null)]).assert(Reflect.parse("f; if (1) function f(){}"));
 
+// Bug 924688: computed property names
+assertExpr('a= {[field1]: "a", [field2=1]: "b"}',
+          aExpr("=", ident("a"),
+                objExpr([{ key: computedName(ident("field1")), value: lit("a")},
+                         { key: computedName(aExpr("=", ident("field2"), lit(1))),
+                           value: lit("b")}])));
+
+assertExpr('a= {["field1"]: "a", field2 : "b"}',
+          aExpr("=", ident("a"),
+                objExpr([{ key: computedName(lit("field1")), value: lit("a") },
+                         { key: ident("field2"), value: lit("b") }])));
+
+assertExpr('a= {[1]: 1, 2 : 2}',
+          aExpr("=", ident("a"),
+                objExpr([{ key: computedName(lit(1)), value: lit(1) },
+                         { key: lit(2), value: lit(2) }])));
+
+// Bug 924688: computed property names - location information
+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);
+
+
 // statements
 
 assertStmt("throw 42", throwStmt(lit(42)));
 assertStmt("for (;;) break", forStmt(null, null, null, breakStmt(null)));
 assertStmt("for (x; y; z) break", forStmt(ident("x"), ident("y"), ident("z"), breakStmt(null)));
 assertStmt("for (var x; y; z) break", forStmt(varDecl([{ id: ident("x"), init: null }]), ident("y"), ident("z")));
 assertStmt("for (var x = 42; y; z) break", forStmt(varDecl([{ id: ident("x"), init: lit(42) }]), ident("y"), ident("z")));
 assertStmt("for (x; ; z) break", forStmt(ident("x"), null, ident("z"), breakStmt(null)));