Bug 1255925 - Give a name to getters/setters and integer-named methods. r=efaust
authorTom Schuster <evilpies@gmail.com>
Wed, 13 Apr 2016 13:43:43 +0200
changeset 330884 0a5cf306560d9eebb30ccbc04d2f14e1d2c1cca1
parent 330883 aceaeee7103fca35df23559f00a2dd8c56b1f285
child 330885 ff56abc1768b5d50177b69e8edb43dd6cf5ac998
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1255925
milestone48.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 1255925 - Give a name to getters/setters and integer-named methods. r=efaust
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/basic/function-tosource-getset.js
js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js
js/src/tests/ecma_6/Class/methodName.js
js/src/tests/ecma_6/Object/accessor-name.js
js/src/tests/js1_8_5/reflect-parse/classes.js
js/src/tests/js1_8_5/reflect-parse/methodDefn.js
js/src/vm/CommonPropertyNames.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1988,16 +1988,35 @@ template <>
 bool
 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler>* outerpc,
                                           FunctionSyntaxKind kind)
 {
     FunctionBox* funbox = pc->sc->asFunctionBox();
     return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
 }
 
+template <typename ParseHandler>
+JSAtom*
+Parser<ParseHandler>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
+{
+    RootedAtom prefix(context);
+    if (propType == PropertyType::Setter || propType == PropertyType::SetterNoExpressionClosure) {
+        prefix = context->names().setPrefix;
+    } else {
+        MOZ_ASSERT(propType == PropertyType::Getter || propType == PropertyType::GetterNoExpressionClosure);
+        prefix = context->names().getPrefix;
+    }
+
+    RootedString str(context, ConcatStrings<CanGC>(context, prefix, propAtom));
+    if (!str)
+        return nullptr;
+
+    return AtomizeString(context, str);
+}
+
 /*
  * defineArg is called for both the arguments of a regular function definition
  * and the arguments specified by the Function constructor.
  *
  * The 'disallowDuplicateArgs' bool indicates whether the use of another
  * feature (destructuring or default arguments) disables duplicate arguments.
  * (ECMA-262 requires us to support duplicate parameter names, but, for newer
  * features, we consider the code to have "opted in" to higher standards and
@@ -2391,25 +2410,26 @@ Parser<FullParseHandler>::bindBodyLevelF
 
 template <>
 bool
 Parser<FullParseHandler>::bindLexicalFunctionName(HandlePropertyName funName,
                                                   ParseNode* pn);
 
 template <>
 bool
-Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
+Parser<FullParseHandler>::checkFunctionDefinition(HandleAtom funAtom,
                                                   ParseNode** pn_, FunctionSyntaxKind kind,
                                                   bool* pbodyProcessed,
                                                   ParseNode** assignmentForAnnexBOut)
 {
     ParseNode*& pn = *pn_;
     *pbodyProcessed = false;
 
     if (kind == Statement) {
+        RootedPropertyName funName(context, funAtom->asPropertyName());
         MOZ_ASSERT(assignmentForAnnexBOut);
         *assignmentForAnnexBOut = nullptr;
 
         // In sloppy mode, ES6 Annex B.3.2 allows labelled function
         // declarations. Otherwise it is a parse error.
         bool bodyLevelFunction = pc->atBodyLevel();
         if (!bodyLevelFunction) {
             StmtInfoPC* stmt = pc->innermostStmt();
@@ -2644,27 +2664,28 @@ Parser<ParseHandler>::addFreeVariablesFr
     }
 
     PropagateTransitiveParseFlags(lazy, pc->sc);
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
+Parser<SyntaxParseHandler>::checkFunctionDefinition(HandleAtom funAtom,
                                                     Node* pn, FunctionSyntaxKind kind,
                                                     bool* pbodyProcessed,
                                                     Node* assignmentForAnnexBOut)
 {
     *pbodyProcessed = false;
 
     /* Function statements add a binding to the enclosing scope. */
     bool bodyLevel = pc->atBodyLevel();
 
     if (kind == Statement) {
+        RootedPropertyName funName(context, funAtom->asPropertyName());
         *assignmentForAnnexBOut = null();
 
         if (!bodyLevel) {
             // Block-scoped functions cannot yet be parsed lazily.
             return abortIfSyntaxParser();
         }
 
         /*
@@ -2763,17 +2784,17 @@ Parser<ParseHandler>::templateLiteral(Yi
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHandling,
-                                  HandlePropertyName funName, FunctionSyntaxKind kind,
+                                  HandleAtom funName, FunctionSyntaxKind kind,
                                   GeneratorKind generatorKind, InvokedPrediction invoked,
                                   Node* assignmentForAnnexBOut)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
 
     /* Make a TOK_FUNCTION node. */
     Node pn = handler.newFunctionDefinition();
     if (!pn)
@@ -7215,31 +7236,33 @@ Parser<FullParseHandler>::classDefinitio
             propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor;
         } else if (isStatic && propAtom == context->names().prototype) {
             report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
             return null();
         }
 
         // FIXME: Implement ES6 function "name" property semantics
         // (bug 883377).
-        RootedPropertyName funName(context);
+        RootedAtom funName(context);
         switch (propType) {
           case PropertyType::GetterNoExpressionClosure:
           case PropertyType::SetterNoExpressionClosure:
-            funName = nullptr;
+            if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+                funName = prefixAccessorName(propType, propAtom);
+                if (!funName)
+                    return null();
+            }
             break;
           case PropertyType::Constructor:
           case PropertyType::DerivedConstructor:
             funName = name;
             break;
           default:
-            if (tokenStream.isCurrentTokenType(TOK_NAME))
-                funName = tokenStream.currentName();
-            else
-                funName = nullptr;
+            if (!tokenStream.isCurrentTokenType(TOK_RB))
+                funName = propAtom;
         }
         ParseNode* fn = methodDefinition(yieldHandling, propType, funName);
         if (!fn)
             return null();
 
         JSOp op = JSOpFromPropertyType(propType);
         if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic))
             return null();
@@ -9356,28 +9379,27 @@ Parser<ParseHandler>::objectLiteral(Yiel
                     possibleError->checkForExprErrors();
                     return null();
                 }
             }
 
         } else {
             // FIXME: Implement ES6 function "name" property semantics
             // (bug 883377).
-            RootedPropertyName funName(context);
-            switch (propType) {
-              case PropertyType::Getter:
-              case PropertyType::Setter:
-                funName = nullptr;
-                break;
-              default:
-                if (tokenStream.isCurrentTokenType(TOK_NAME))
-                    funName = tokenStream.currentName();
-                else
-                    funName = nullptr;
+            RootedAtom funName(context);
+            if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+                funName = propAtom;
+
+                if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
+                    funName = prefixAccessorName(propType, propAtom);
+                    if (!funName)
+                        return null();
+                }
             }
+
             Node fn = methodDefinition(yieldHandling, propType, funName);
             if (!fn)
                 return null();
 
             JSOp op = JSOpFromPropertyType(propType);
             if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
                 return null();
         }
@@ -9394,17 +9416,17 @@ Parser<ParseHandler>::objectLiteral(Yiel
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::methodDefinition(YieldHandling yieldHandling, PropertyType propType,
-                                       HandlePropertyName funName)
+                                       HandleAtom funName)
 {
     FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType);
     GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType);
     return functionDef(InAllowed, yieldHandling, funName, kind, generatorKind);
 }
 
 template <typename ParseHandler>
 bool
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -870,26 +870,25 @@ class Parser : private JS::AutoGCRooter,
                       TripledotHandling tripledotHandling,
                       PossibleError* possibleError);
     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                       TripledotHandling tripledotHandling);
 
     bool tryNewTarget(Node& newTarget);
     bool checkAndMarkSuperScope();
 
-    Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
-                          HandlePropertyName funName);
+    Node methodDefinition(YieldHandling yieldHandling, PropertyType propType, HandleAtom funName);
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
                            Node funcpn, bool* hasRest);
 
-    Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name,
+    Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandleAtom name,
                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                      InvokedPrediction invoked = PredictUninvoked,
                      Node* assignmentForAnnexBOut = nullptr);
     bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
                              FunctionSyntaxKind kind, GeneratorKind generatorKind,
                              Directives inheritedDirectives, Directives* newDirectives);
 
     Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin);
@@ -947,17 +946,17 @@ class Parser : private JS::AutoGCRooter,
     bool checkFunctionArguments();
 
     bool defineFunctionThis();
     Node newThisName();
 
     bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
     bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn);
     bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn);
-    bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
+    bool checkFunctionDefinition(HandleAtom funAtom, Node* pn, FunctionSyntaxKind kind,
                                  bool* pbodyProcessed, Node* assignmentForAnnexBOut);
     bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
 
     // Use when the current token is TOK_NAME and is known to be 'let'.
     bool shouldParseLetDeclaration(bool* parseDeclOut);
 
     // Use when the lookahead token is TOK_NAME and is known to be 'let'. If a
@@ -1054,16 +1053,18 @@ class Parser : private JS::AutoGCRooter,
 
     bool reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name);
     bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
     DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler>* pc, JSAtom* atom);
 
     bool leaveFunction(Node fn, ParseContext<ParseHandler>* outerpc,
                        FunctionSyntaxKind kind = Expression);
 
+    JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
+
     TokenPos pos() const { return tokenStream.currentToken().pos; }
 
     bool asmJS(Node list);
 
     void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
 
     bool warnOnceAboutExprClosure();
 
--- a/js/src/jit-test/tests/basic/function-tosource-getset.js
+++ b/js/src/jit-test/tests/basic/function-tosource-getset.js
@@ -1,7 +1,7 @@
 var o = {get prop() a + b, set prop(x) a + b};
 var prop = Object.getOwnPropertyDescriptor(o, "prop");
-assertEq(prop.get.toString(), "function () a + b");
-assertEq(prop.get.toSource(), "(function () a + b)");
-assertEq(prop.set.toString(), "function (x) a + b");
-assertEq(prop.set.toSource(), "(function (x) a + b)");
+assertEq(prop.get.toString(), "function get prop() a + b");
+assertEq(prop.get.toSource(), "(function get prop() a + b)");
+assertEq(prop.set.toString(), "function set prop(x) a + b");
+assertEq(prop.set.toSource(), "(function set prop(x) a + b)");
 assertEq(o.toSource(), "({get prop () a + b, set prop (x) a + b})");
--- a/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js
+++ b/js/src/jit-test/tests/profiler/AutoEntryMonitor-01.js
@@ -15,21 +15,21 @@ function cold_and_warm(f, params, expect
   assertEq(arraysEqual(entryPoints(params), expected), true);
 }
 
 function entry1() { }
 cold_and_warm(entry1, { function: entry1 }, [ "entry1" ]);
 
 var getx = { get x() { } };
 cold_and_warm(Object.getOwnPropertyDescriptor(getx, 'x').get,
-              { object: getx, property: 'x' }, [ "getx.x" ]);
+              { object: getx, property: 'x' }, [ "get x" ]);
 
 var sety = { set y(v) { } };
 cold_and_warm(Object.getOwnPropertyDescriptor(sety, 'y').set,
-              { object: sety, property: 'y', value: 'glerk' }, [ "sety.y" ]);
+              { object: sety, property: 'y', value: 'glerk' }, [ "set y" ]);
 
 cold_and_warm(Object.prototype.toString, { ToString: {} }, []);
 
 var toS = { toString: function myToString() { return "string"; } };
 cold_and_warm(toS.toString, { ToString: toS }, [ "myToString" ]);
 
 cold_and_warm(undefined, { ToNumber: {} }, []);
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/methodName.js
@@ -0,0 +1,40 @@
+class TestClass {
+    get getter() { }
+    set setter(x) { }
+    method() { }
+
+    static get staticGetter() { }
+    static set staticSetter(x) { }
+    static staticMethod() { }
+
+    get 1() { }
+    set 2(v) { }
+    3() { }
+
+    static get 4() { }
+    static set 5(x) { }
+    static 6() { }
+}
+
+function name(obj, property, get) {
+    let desc = Object.getOwnPropertyDescriptor(obj, property);
+    return (get ? desc.get : desc.set).name;
+}
+
+assertEq(name(TestClass.prototype, "getter", true), "get getter");
+assertEq(name(TestClass.prototype, "setter", false), "set setter");
+assertEq(TestClass.prototype.method.name, "method");
+
+assertEq(name(TestClass, "staticGetter", true), "get staticGetter");
+assertEq(name(TestClass, "staticSetter", false), "set staticSetter");
+assertEq(TestClass.staticMethod.name, "staticMethod");
+
+assertEq(name(TestClass.prototype, "1", true), "get 1");
+assertEq(name(TestClass.prototype, "2", false), "set 2");
+assertEq(TestClass.prototype[3].name, "3");
+
+assertEq(name(TestClass, "4", true), "get 4");
+assertEq(name(TestClass, "5", false), "set 5");
+assertEq(TestClass[6].name, "6");
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/accessor-name.js
@@ -0,0 +1,36 @@
+function name(obj, property, get) {
+    let desc = Object.getOwnPropertyDescriptor(obj, property);
+    return (get ? desc.get : desc.set).name;
+}
+
+assertEq(name({get a() {}}, "a", true), "get a");
+assertEq(name({set a(v) {}}, "a", false), "set a");
+
+assertEq(name({get 123() {}}, "123", true), "get 123");
+assertEq(name({set 123(v) {}}, "123", false), "set 123");
+
+assertEq(name({get case() {}}, "case", true), "get case");
+assertEq(name({set case(v) {}}, "case", false), "set case");
+
+assertEq(name({get get() {}}, "get", true), "get get");
+assertEq(name({set set(v) {}}, "set", false), "set set");
+
+let o = {get a() { }, set a(v) {}};
+assertEq(name(o, "a", true), "get a");
+assertEq(name(o, "a", false), "set a");
+
+o = {get 123() { }, set 123(v) {}}
+assertEq(name(o, "123", true), "get 123");
+assertEq(name(o, "123", false), "set 123");
+
+o = {get case() { }, set case(v) {}}
+assertEq(name(o, "case", true), "get case");
+assertEq(name(o, "case", false), "set case");
+
+// Congratulations on implementing these!
+assertEq(name({get ["a"]() {}}, "a", true), "");
+assertEq(name({get [123]() {}}, "123", true), "");
+assertEq(name({set ["a"](v) {}}, "a", false), "");
+assertEq(name({set [123](v) {}}, "123", false), "");
+
+reportCompare(true, true);
--- a/js/src/tests/js1_8_5/reflect-parse/classes.js
+++ b/js/src/tests/js1_8_5/reflect-parse/classes.js
@@ -1,16 +1,27 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // Classes
 function testClasses() {
     function methodFun(id, kind, generator, args, body = []) {
         assertEq(generator && kind === "method", generator);
         assertEq(typeof id === 'string' || id === null, true);
-        let idN = typeof id === 'string' ? ident(id) : null;
-        let methodName = kind !== "method" ? null : idN;
+        let methodName;
+        switch (kind) {
+          case "method":
+            methodName = typeof id === 'string' ? ident(id) : null;
+            break;
+          case "get":
+          case "set":
+            methodName = ident(`${kind} ${typeof id === 'string' ? id : ""}`);
+            break;
+          default:
+            methodName = null;
+            break;
+        }
         return generator
                ? genFunExpr("es6", methodName, args.map(ident), blockStmt(body))
                : funExpr(methodName, args.map(ident), blockStmt(body));
     }
 
     function simpleMethod(id, kind, generator, args=[], isStatic=false) {
         return classMethod(ident(id),
                            methodFun(id, kind, generator, args),
--- a/js/src/tests/js1_8_5/reflect-parse/methodDefn.js
+++ b/js/src/tests/js1_8_5/reflect-parse/methodDefn.js
@@ -14,21 +14,21 @@ assertExpr("b = { *a() { } }", aExpr("="
 assertError("({ a() void 0 })", SyntaxError);
 assertError("({ a() 1 })", SyntaxError);
 assertError("({ a() false })", SyntaxError);
 
 // getters and setters
 
 assertExpr("({ get x() { return 42 } })",
            objExpr([ { key: ident("x"),
-                       value: funExpr(null, [], blockStmt([returnStmt(lit(42))])),
+                       value: funExpr(ident("get x"), [], blockStmt([returnStmt(lit(42))])),
                        kind: "get" } ]));
 assertExpr("({ set x(v) { return 42 } })",
            objExpr([ { key: ident("x"),
-                       value: funExpr(null, [ident("v")], blockStmt([returnStmt(lit(42))])),
+                       value: funExpr(ident("set x"), [ident("v")], blockStmt([returnStmt(lit(42))])),
                        kind: "set" } ]));
 
 // Bug 1073809 - Getter/setter syntax with generators
 assertExpr("({*get() { }})", objExpr([{ type: "Property", key: ident("get"), method: true,
                                         value: genFunExpr("es6", ident("get"), [], blockStmt([]))}]));
 assertExpr("({*set() { }})", objExpr([{ type: "Property", key: ident("set"), method: true,
                                         value: genFunExpr("es6", ident("set"), [], blockStmt([]))}]));
 assertError("({*get foo() { }})", SyntaxError);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -117,16 +117,17 @@
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
     macro(futexOK, futexOK, "ok") \
     macro(futexNotEqual, futexNotEqual, "not-equal") \
     macro(futexTimedOut, futexTimedOut, "timed-out") \
     macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \
     macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \
     macro(get, get, "get") \
+    macro(getPrefix, getPrefix, "get ") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
     macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
     macro(global, global, "global") \
     macro(Handle, Handle, "Handle") \
     macro(has, has, "has") \
     macro(hasOwn, hasOwn, "hasOwn") \
@@ -231,16 +232,17 @@
     macro(return, return_, "return") \
     macro(revoke, revoke, "revoke") \
     macro(script, script, "script") \
     macro(scripts, scripts, "scripts") \
     macro(second, second, "second") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(separator, separator, "separator") \
     macro(set, set, "set") \
+    macro(setPrefix, setPrefix, "set ") \
     macro(shape, shape, "shape") \
     macro(size, size, "size") \
     macro(source, source, "source") \
     macro(SpeciesConstructor, SpeciesConstructor, "SpeciesConstructor") \
     macro(stack, stack, "stack") \
     macro(star, star, "*") \
     macro(starDefaultStar, starDefaultStar, "*default*") \
     macro(startTimestamp, startTimestamp, "startTimestamp") \