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 293013 0a5cf306560d9eebb30ccbc04d2f14e1d2c1cca1
parent 293012 aceaeee7103fca35df23559f00a2dd8c56b1f285
child 293014 ff56abc1768b5d50177b69e8edb43dd6cf5ac998
push idunknown
push userunknown
push dateunknown
reviewersefaust
bugs1255925
milestone48.0a1
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") \