Ignore:
Timestamp:
Jan 15, 2020, 4:09:50 PM (6 years ago)
Author:
[email protected]
Message:

[JSC] Add support for public class fields
https://bugs.webkit.org/show_bug.cgi?id=174212

Reviewed by Yusuke Suzuki.

JSTests:

New syntax invalidates some test expectations:

"async <linefeed> MethodDefinition" is no longer an unexpected "async"
token. It is now an instance field named "async" with no initializer,
and an automatic semicolon, followed by MethodDefinition.

"get|set GeneratorMethodDefinition"'s error message has changed, due to "get"
being valid class field names.

Many class-syntax tests relating to automatic semicolon insertion are
no longer valid, as a line containing nothing but an identifier is now
a valid class element.

  • stress/async-await-syntax.js:
  • stress/class-fields-bytecode-cache.js: Added.
  • stress/class-fields-computed-to-property-key.js: Added.
  • stress/class-fields-function-name.js: Added.
  • stress/class-fields-harmony.js: Added.
  • stress/class-fields-proxy-define-property.js: Added.
  • stress/class-fields-stress-instance.js: Added.
  • stress/generator-syntax.js:
  • stress/method-name.js:
  • test262/config.yaml:

Source/JavaScriptCore:

Implements the instance class fields proposal (https://tc39.es/proposal-class-fields/),
minus support for private fields (split into a separate patch).

In summary, class fields are initialized by a synthetic JSFunction. In its unlinked state,
the UnlinkedFunctionExecutable for the function includes an ordered list of JSTokenLocations
pointing to the start of each class field in the class. Each of these fields are parsed and
included as DefineFieldNodes, which implement the appropriate DefineField behaviour in the
proposal. This synthetic function is only created, and only loaded, if there are class fields
present. The decision to use a synthetic function was for simplicity. There are a number of
factors which make inlining the initialization complicated, though we may opt to do this in
the future. For reference, the complexities are: instance fields and constructor in different
currently in different parsing arenas, distinct scopes between the 2 which require work to manage,
and complexity in doing to this work for child classes, where the location of initialization can
depend, and in some cases occur more than once.

Computed property fields require a new bytecode, op_to_property_key, as an implementation
detail. It is necessary in the proposal to convert computed properties to property keys
during class evaluation, rather than during field initialization. Additionally, we allocate
the class lexical scope when computed class fields are used (previously, only when there was
a class name), as a location to keep the computed property keys. They can be loaded from the
scope via indexed keys.

To illustrate computed field names in action, consider the following pseudocode:

<during class evaluation>
1) fieldName = emitNode({expr})
2) fieldName = emitToPropertyKey(fieldName)
3) classScope[numComputedNames++] = fieldName

<during class field initialization>
1) fieldName = emitGetFromScope(classScope, computedFieldNameIndex++)
2) value = emitNode({initializer})
3) instance[fieldName] = value

The feature is currently hidden behind the feature flag JSC::Options::useClassFields.

LayoutTests:

New syntax invalidates some test expectations:

"async <linefeed> MethodDefinition" is no longer an unexpected "async"
token. It is now an instance field named "async" with no initializer,
and an automatic semicolon, followed by MethodDefinition.

"get|set GeneratorMethodDefinition"'s error message has changed, due to "get"
being valid class field names.

Many class-syntax tests relating to automatic semicolon insertion are
no longer valid, as a line containing nothing but an identifier is now
a valid class element.

  • js/class-syntax-semicolon-expected.txt:
  • js/script-tests/class-syntax-semicolon.js:
File:
1 edited

Legend:

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

    r253987 r254653  
    201201        , m_superBinding(static_cast<unsigned>(superBinding))
    202202        , m_constructorKind(static_cast<unsigned>(constructorKind))
     203        , m_needsClassFieldInitializer(static_cast<unsigned>(NeedsClassFieldInitializer::No))
    203204        , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
    204205        , m_parseMode(mode)
     
    224225        , m_superBinding(static_cast<unsigned>(superBinding))
    225226        , m_constructorKind(static_cast<unsigned>(constructorKind))
     227        , m_needsClassFieldInitializer(static_cast<unsigned>(NeedsClassFieldInitializer::No))
    226228        , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
    227229        , m_parseMode(mode)
     
    329331}
    330332
     333// FIXME: calculate this feature once when parsing the property list.
     334// https://bugs.webkit.org/show_bug.cgi?id=206174
     335bool PropertyListNode::shouldCreateLexicalScopeForClass(PropertyListNode* list)
     336{
     337    while (list) {
     338        if (list->m_node->isComputedClassField())
     339            return true;
     340        list = list->m_next;
     341    }
     342    return false;
     343}
     344
     345// ------------------------------ ClassExprNode -----------------------------
     346
     347// FIXME: calculate this feature once when parsing the property list.
     348// https://bugs.webkit.org/show_bug.cgi?id=206174
     349bool PropertyListNode::hasInstanceFields() const
     350{
     351    for (auto list = this; list; list = list->m_next) {
     352        if (list->m_node->isInstanceClassField())
     353            return true;
     354    }
     355    return false;
     356}
     357
    331358VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables)
    332359{
Note: See TracChangeset for help on using the changeset viewer.