Ignore:
Timestamp:
Jan 29, 2015, 2:59:19 PM (10 years ago)
Author:
[email protected]
Message:

Implement ES6 class syntax without inheritance support
https://bugs.webkit.org/show_bug.cgi?id=140918

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Added the most basic support for ES6 class syntax. After this patch, we support basic class definition like:
class A {

constructor() { }
someMethod() { }

}

We'll add the support for "extends" keyword and automatically generating a constructor in follow up patches.
We also don't support block scoping of a class declaration.

We support both class declaration and class expression. A class expression is implemented by the newly added
ClassExprNode AST node. A class declaration is implemented by ClassDeclNode, which is a thin wrapper around
AssignResolveNode.

Tests: js/class-syntax-declaration.html

js/class-syntax-expression.html

  • bytecompiler/NodesCodegen.cpp:

(JSC::ObjectLiteralNode::emitBytecode): Create a new object instead of delegating the work to PropertyListNode.
Also fixed the 5-space indentation.
(JSC::PropertyListNode::emitBytecode): Don't create a new object now that ObjectLiteralNode does this.
(JSC::ClassDeclNode::emitBytecode): Added. Just let the AssignResolveNode node emit the byte code.
(JSC::ClassExprNode::emitBytecode): Create the class constructor and add static methods to the constructor by
emitting the byte code for PropertyListNode. Add instance methods to the class's prototype object the same way.

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createClassExpr): Added. Creates a ClassExprNode.
(JSC::ASTBuilder::createClassDeclStatement): Added. Creates a AssignResolveNode and wraps it by a ClassDeclNode.

  • parser/NodeConstructors.h:

(JSC::ClassDeclNode::ClassDeclNode): Added.
(JSC::ClassExprNode::ClassExprNode): Added.

  • parser/Nodes.h:

(JSC::ClassExprNode): Added.
(JSC::ClassDeclNode): Added.

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseStatement): Added the support for class declaration.
(JSC::stringForFunctionMode): Return "method" for MethodMode.
(JSC::Parser<LexerType>::parseClassDeclaration): Added. Uses parseClass to create a class expression and wraps
it with ClassDeclNode as described above.
(JSC::Parser<LexerType>::parseClass): Parses a class expression.
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parseGetterSetter): Extracted from parseProperty to share the code between parseProperty
and parseClass.
(JSC::Parser<LexerType>::parsePrimaryExpression): Added the support for class expression.

  • parser/Parser.h:

(FunctionParseMode): Added MethodMode.

  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createClassExpr): Added.
(JSC::SyntaxChecker::createClassDeclStatement): Added.

LayoutTests:

Added two tests for class declarations and class expressions.

  • TestExpectations:
  • js/class-syntax-declaration-expected.txt: Added.
  • js/class-syntax-declaration.html: Added.
  • js/class-syntax-expression-expected.txt: Added.
  • js/class-syntax-expression.html: Added.
  • js/script-tests/class-syntax-declaration.js: Added.
  • js/script-tests/class-syntax-expression.js: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r179159 r179371  
    11511151        result = parseConstDeclaration(context);
    11521152        break;
     1153#if ENABLE(ES6_CLASS_SYNTAX)
     1154    case CLASSTOKEN:
     1155        failIfFalse(m_statementDepth == 1, "Class declaration is not allowed in a lexically nested statement");
     1156        result = parseClassDeclaration(context);
     1157        break;
     1158#endif
    11531159    case FUNCTION:
    11541160        failIfFalseIfStrict(m_statementDepth == 1, "Strict mode does not allow function declarations in a lexically nested statement");
     
    12681274    case FunctionMode:
    12691275        return "function";
     1276#if ENABLE(ES6_CLASS_SYNTAX)
     1277    case MethodMode:
     1278        return "method";
     1279#endif
    12701280    }
    12711281    RELEASE_ASSERT_NOT_REACHED();
     
    14041414    return context.createFuncDeclStatement(location, info, functionKeywordStart);
    14051415}
     1416
     1417#if ENABLE(ES6_CLASS_SYNTAX)
     1418template <typename LexerType>
     1419template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context)
     1420{
     1421    ASSERT(match(CLASSTOKEN));
     1422    JSTokenLocation location(tokenLocation());
     1423    JSTextPosition classStart = tokenStartPosition();
     1424    unsigned classStartLine = tokenLine();
     1425
     1426    TreeClassExpression classExpr = parseClass(context, FunctionNeedsName);
     1427    failIfFalse(classExpr, "Failed to parse class");
     1428
     1429    JSTextPosition classEnd = lastTokenEndPosition();
     1430    unsigned classEndLine = tokenLine();
     1431
     1432    return context.createClassDeclStatement(location, classExpr, classStart, classEnd, classStartLine, classEndLine);
     1433}
     1434
     1435template <typename LexerType>
     1436template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(TreeBuilder& context, FunctionRequirements requirements)
     1437{
     1438    ASSERT(match(CLASSTOKEN));
     1439    JSTokenLocation location(tokenLocation());
     1440    next();
     1441
     1442    AutoPopScopeRef classScope(this, pushScope());
     1443    classScope->setStrictMode();
     1444
     1445    const Identifier* className = nullptr;
     1446    if (match(IDENT)) {
     1447        className = m_token.m_data.ident;
     1448        next();
     1449        failIfFalse(classScope->declareVariable(className), "'", className->impl(), "' is not a valid class name");
     1450    } else if (requirements == FunctionNeedsName) {
     1451        semanticFailureDueToKeyword("class name");
     1452        failDueToUnexpectedToken();
     1453    } else
     1454        className = &m_vm->propertyNames->nullIdentifier;
     1455    ASSERT(className);
     1456
     1457    TreeExpression parentClass = 0;
     1458    if (consume(EXTENDS)) {
     1459        parentClass = parsePrimaryExpression(context);
     1460        failIfFalse(parentClass, "Cannot parse the parent class name");
     1461        failWithMessage("Inheritance is not supported yet");
     1462    }
     1463
     1464    consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings, "Expected opening '{' at the start of a class body");
     1465
     1466    TreeExpression constructor = 0;
     1467    TreePropertyList staticMethods = 0;
     1468    TreePropertyList instanceMethods = 0;
     1469    TreePropertyList instanceMethodsTail = 0;
     1470    TreePropertyList staticMethodsTail = 0;
     1471    while (!match(CLOSEBRACE)) {
     1472        if (match(SEMICOLON))
     1473            next();
     1474
     1475        JSTokenLocation methodLocation(tokenLocation());
     1476        unsigned methodStart = tokenStart();
     1477
     1478        bool isStaticMethod = match(STATICTOKEN);
     1479        if (isStaticMethod)
     1480            next();
     1481
     1482        matchOrFail(IDENT, "Expected an indentifier");
     1483
     1484        const CommonIdentifiers& propertyNames = *m_vm->propertyNames;
     1485        const Identifier& ident = *m_token.m_data.ident;
     1486        bool isGetter = ident == propertyNames.get;
     1487        bool isSetter = ident == propertyNames.set;
     1488
     1489        TreeProperty property;
     1490        const bool alwaysStrictInsideClass = true;
     1491        if (isGetter || isSetter) {
     1492            semanticFailIfTrue(isStaticMethod, "Cannot declare a static", stringForFunctionMode(isGetter ? GetterMode : SetterMode));
     1493            nextExpectIdentifier(LexerFlagsIgnoreReservedWords);
     1494            property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart);
     1495            failIfFalse(property, "Cannot parse this method");
     1496        } else {
     1497            ParserFunctionInfo<TreeBuilder> methodInfo;
     1498            failIfFalse((parseFunctionInfo(context, FunctionNeedsName, FunctionMode, false, methodInfo)), "Cannot parse this method");
     1499            failIfFalse(methodInfo.name, "method must have a name");
     1500            failIfFalse(declareVariable(methodInfo.name), "Cannot declare a method named '", methodInfo.name->impl(), "'");
     1501
     1502            bool isConstructor = !isStaticMethod && *methodInfo.name == propertyNames.constructor;
     1503            if (isConstructor)
     1504                methodInfo.name = className;
     1505
     1506            TreeExpression method = context.createFunctionExpr(methodLocation, methodInfo, methodStart);
     1507            if (isConstructor) {
     1508                semanticFailIfTrue(constructor, "Cannot declare multiple constructors in a single class");
     1509                constructor = method;
     1510                continue;
     1511            }
     1512
     1513            // FIXME: Syntax error when super() is called
     1514            semanticFailIfTrue(isStaticMethod && *methodInfo.name == propertyNames.prototype,
     1515                "Cannot declare a static method named 'prototype'");
     1516            property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, alwaysStrictInsideClass);
     1517        }
     1518
     1519        TreePropertyList& tail = isStaticMethod ? staticMethodsTail : instanceMethodsTail;
     1520        if (tail)
     1521            tail = context.createPropertyList(methodLocation, property, tail);
     1522        else {
     1523            tail = context.createPropertyList(methodLocation, property);
     1524            if (isStaticMethod)
     1525                staticMethods = tail;
     1526            else
     1527                instanceMethods = tail;
     1528        }
     1529    }
     1530
     1531    // FIXME: Create a Miranda function instead.
     1532    semanticFailIfFalse(constructor, "Class declaration without a constructor is not supported yet");
     1533
     1534    consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body");
     1535
     1536    return context.createClassExpr(location, *className, constructor, parentClass, instanceMethods, staticMethods);
     1537}
     1538#endif
    14061539
    14071540struct LabelInfo {
     
    18031936        else
    18041937            failWithMessage("Expected a ':' following the property name '", ident->impl(), "'");
    1805         const Identifier* stringPropertyName = 0;
    1806         double numericPropertyName = 0;
    1807         if (m_token.m_type == IDENT || m_token.m_type == STRING)
    1808             stringPropertyName = m_token.m_data.ident;
    1809         else if (m_token.m_type == NUMBER)
    1810             numericPropertyName = m_token.m_data.doubleValue;
    1811         else
    1812             failDueToUnexpectedToken();
    1813         JSTokenLocation location(tokenLocation());
    1814         next();
    1815         ParserFunctionInfo<TreeBuilder> info;
    1816         if (type == PropertyNode::Getter) {
    1817             failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
    1818             failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, info)), "Cannot parse getter definition");
    1819         } else {
    1820             failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition");
    1821             failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, info)), "Cannot parse setter definition");
    1822         }
    1823         if (stringPropertyName)
    1824             return context.createGetterOrSetterProperty(location, type, complete, stringPropertyName, info, getterOrSetterStartOffset);
    1825         return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, complete, numericPropertyName, info, getterOrSetterStartOffset);
     1938        return parseGetterSetter(context, complete, type, getterOrSetterStartOffset);
    18261939    }
    18271940    case NUMBER: {
     
    18501963        goto namedProperty;
    18511964    }
     1965}
     1966
     1967template <typename LexerType>
     1968template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset)
     1969{
     1970    const Identifier* stringPropertyName = 0;
     1971    double numericPropertyName = 0;
     1972    if (m_token.m_type == IDENT || m_token.m_type == STRING)
     1973        stringPropertyName = m_token.m_data.ident;
     1974    else if (m_token.m_type == NUMBER)
     1975        numericPropertyName = m_token.m_data.doubleValue;
     1976    else
     1977        failDueToUnexpectedToken();
     1978    JSTokenLocation location(tokenLocation());
     1979    next();
     1980    ParserFunctionInfo<TreeBuilder> info;
     1981    if (type == PropertyNode::Getter) {
     1982        failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
     1983        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, info)), "Cannot parse getter definition");
     1984    } else {
     1985        failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition");
     1986        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, info)), "Cannot parse setter definition");
     1987    }
     1988    if (stringPropertyName)
     1989        return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, getterOrSetterStartOffset);
     1990    return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, getterOrSetterStartOffset);
    18521991}
    18531992
     
    20372176        return context.createFunctionExpr(location, info, functionKeywordStart);
    20382177    }
     2178#if ENABLE(ES6_CLASS_SYNTAX)
     2179    case CLASSTOKEN:
     2180        return parseClass(context, FunctionNoRequirements);
     2181#endif
    20392182    case OPENBRACE:
    20402183        if (strictMode())
Note: See TracChangeset for help on using the changeset viewer.