Bug 1066238 - Part 1: Parser support for static class methods. (r=jorendorff)
authorEric Faust <efaustbmo@gmail.com>
Thu, 26 Feb 2015 15:05:23 -0800
changeset 231107 5bda72dc35cb196b5b0d0027d6b37a2c9beb211d
parent 231106 6fb710e791fa5c299b55047e4e14f659e61cfdda
child 231108 4ca8f8762fd9bbecd4f89534d54b7b0ac05a746a
push id28344
push userryanvm@gmail.com
push dateFri, 27 Feb 2015 18:20:08 +0000
treeherdermozilla-central@9dd9d1e5b43c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1066238
milestone39.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 1066238 - Part 1: Parser support for static class methods. (r=jorendorff)
js/src/frontend/FullParseHandler.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/jsatom.cpp
js/src/vm/CommonPropertyNames.h
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -344,26 +344,26 @@ class FullParseHandler
 
         ParseNode *propdef = newBinary(PNK_COLON, key, fn, op);
         if (!propdef)
             return false;
         literal->append(propdef);
         return true;
     }
 
-    bool addClassMethodDefinition(ParseNode *methodList, ParseNode *key, ParseNode *fn, JSOp op)
+    bool addClassMethodDefinition(ParseNode *methodList, ParseNode *key, ParseNode *fn, JSOp op,
+                                  bool isStatic)
     {
         MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
         MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
                    key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
                    key->isKind(PNK_STRING) ||
                    key->isKind(PNK_COMPUTED_NAME));
 
-        // For now, there's no such thing as static methods.
-        ParseNode *classMethod = new_<ClassMethod>(key, fn, op, false);
+        ParseNode *classMethod = new_<ClassMethod>(key, fn, op, isStatic);
         if (!classMethod)
             return false;
         methodList->append(classMethod);
         return true;
     }
 
     ParseNode *newYieldExpression(uint32_t begin, ParseNode *value, ParseNode *gen,
                                   JSOp op = JSOP_YIELD) {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8024,18 +8024,29 @@ Parser<ParseHandler>::propertyList(PropL
     RootedAtom atom(context);
     for (;;) {
         TokenKind ltok;
         if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
             return null();
         if (ltok == TOK_RC)
             break;
 
-        if (type == ClassBody && ltok == TOK_SEMI)
-            continue;
+        bool isStatic = false;
+        if (type == ClassBody) {
+            if (ltok == TOK_SEMI)
+                continue;
+
+            if (ltok == TOK_NAME &&
+                tokenStream.currentName() == context->names().static_)
+            {
+                isStatic = true;
+                if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+                    return null();
+            }
+        }
 
         bool isGenerator = false;
         if (ltok == TOK_MUL) {
             isGenerator = true;
             if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
                 return null();
         }
 
@@ -8138,31 +8149,45 @@ Parser<ParseHandler>::propertyList(PropL
                 propname = stringLiteral();
                 if (!propname)
                     return null();
             }
             break;
           }
 
           default:
-            report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
-            return null();
+            // There is never a case in which |static *(| can make a meaningful method definition.
+            if (isStatic && !isGenerator) {
+                // Turns out it wasn't static. Put it back and pretend it was a name all along.
+                isStatic = false;
+                tokenStream.ungetToken();
+                atom = tokenStream.currentName();
+                propname = handler.newObjectLiteralPropertyName(atom->asPropertyName(), pos());
+                if (!propname)
+                    return null();
+            } else {
+                report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+                return null();
+            }
         }
 
         if (type == ClassBody) {
-            if (atom == context->names().constructor) {
+            if (!isStatic && atom == context->names().constructor) {
                 if (isGenerator || op != JSOP_INITPROP) {
                     report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF);
                     return null();
                 }
                 if (seenConstructor) {
                     report(ParseError, false, propname, JSMSG_DUPLICATE_PROPERTY, "constructor");
                     return null();
                 }
                 seenConstructor = true;
+            } else if (isStatic && atom == context->names().prototype) {
+                report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF);
+                return null();
             }
         }
 
         if (op == JSOP_INITPROP) {
             TokenKind tt;
             if (!tokenStream.getToken(&tt))
                 return null();
 
@@ -8226,27 +8251,27 @@ Parser<ParseHandler>::propertyList(PropL
                 if (!nameExpr)
                     return null();
 
                 if (!handler.addShorthand(propList, propname, nameExpr))
                     return null();
             } else if (tt == TOK_LP) {
                 tokenStream.ungetToken();
                 if (!methodDefinition(type, propList, propname, Normal, Method,
-                                      isGenerator ? StarGenerator : NotGenerator, op)) {
+                                      isGenerator ? StarGenerator : NotGenerator, isStatic, op)) {
                     return null();
                 }
             } else {
                 report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
                 return null();
             }
         } else {
             /* NB: Getter function in { get x(){} } is unnamed. */
             if (!methodDefinition(type, propList, propname, op == JSOP_INITPROP_GETTER ? Getter : Setter,
-                                  Expression, NotGenerator, op)) {
+                                  Expression, NotGenerator, isStatic, op)) {
                 return null();
             }
         }
 
         if (type == ObjectLiteral) {
             TokenKind tt;
             if (!tokenStream.getToken(&tt))
                 return null();
@@ -8268,30 +8293,31 @@ Parser<ParseHandler>::propertyList(PropL
     handler.setEndPosition(propList, pos().end);
     return propList;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::methodDefinition(PropListType listType, Node propList, Node propname,
                                        FunctionType type, FunctionSyntaxKind kind,
-                                       GeneratorKind generatorKind, JSOp op)
+                                       GeneratorKind generatorKind,
+                                       bool isStatic, JSOp op)
 {
     RootedPropertyName funName(context);
     if (kind == Method && tokenStream.isCurrentTokenType(TOK_NAME))
         funName = tokenStream.currentName();
     else
         funName = nullptr;
 
     Node fn = functionDef(funName, type, kind, generatorKind);
     if (!fn)
         return false;
 
     if (listType == ClassBody)
-        return handler.addClassMethodDefinition(propList, propname, fn, op);
+        return handler.addClassMethodDefinition(propList, propname, fn, op, isStatic);
 
     MOZ_ASSERT(listType == ObjectLiteral);
     return handler.addObjectMethodDefinition(propList, propname, fn, op);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::primaryExpr(TokenKind tt, InvokedPrediction invoked)
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -572,17 +572,18 @@ class Parser : private JS::AutoGCRooter,
     Node unaryExpr(InvokedPrediction invoked = PredictUninvoked);
     Node memberExpr(TokenKind tt, bool allowCallSyntax,
                     InvokedPrediction invoked = PredictUninvoked);
     Node primaryExpr(TokenKind tt, InvokedPrediction invoked = PredictUninvoked);
     Node parenExprOrGeneratorComprehension();
     Node exprInParens();
 
     bool methodDefinition(PropListType listType, Node propList, Node propname, FunctionType type,
-                          FunctionSyntaxKind kind, GeneratorKind generatorKind, JSOp Op);
+                          FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                          bool isStatic, JSOp Op);
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(FunctionSyntaxKind kind, FunctionType type, Node *list, Node funcpn,
                            bool *hasRest);
 
     Node functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind,
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -186,17 +186,17 @@ class SyntaxParseHandler
     void addArrayElement(Node literal, Node element) { }
 
     Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
     Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     bool addShorthand(Node literal, Node name, Node expr) { return true; }
     bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
-    bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
+    bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
     Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
     Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
     // Statements
 
     Node newStatementList(unsigned blockid, const TokenPos &pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler> *pc) {}
     bool prependInitialYield(Node stmtList, Node gen) { return true; }
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -78,17 +78,16 @@ const char js_instanceof_str[]      = "i
 const char js_interface_str[]       = "interface";
 const char js_new_str[]             = "new";
 const char js_package_str[]         = "package";
 const char js_private_str[]         = "private";
 const char js_protected_str[]       = "protected";
 const char js_public_str[]          = "public";
 const char js_send_str[]            = "send";
 const char js_setter_str[]          = "setter";
-const char js_static_str[]          = "static";
 const char js_super_str[]           = "super";
 const char js_switch_str[]          = "switch";
 const char js_this_str[]            = "this";
 const char js_try_str[]             = "try";
 const char js_typeof_str[]          = "typeof";
 const char js_void_str[]            = "void";
 const char js_while_str[]           = "while";
 const char js_with_str[]            = "with";
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -175,16 +175,17 @@
     macro(revoke, revoke, "revoke") \
     macro(scripts, scripts, "scripts") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(set, set, "set") \
     macro(shape, shape, "shape") \
     macro(signMask, signMask, "signMask") \
     macro(source, source, "source") \
     macro(stack, stack, "stack") \
+    macro(static, static_, "static") \
     macro(sticky, sticky, "sticky") \
     macro(strings, strings, "strings") \
     macro(StructType, StructType, "StructType") \
     macro(style, style, "style") \
     macro(test, test, "test") \
     macro(throw, throw_, "throw") \
     macro(timestamp, timestamp, "timestamp") \
     macro(timeZone, timeZone, "timeZone") \