Ignore:
Timestamp:
Nov 17, 2020, 1:37:39 PM (5 years ago)
Author:
[email protected]
Message:

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

Patch by Xan López <Xan Lopez> on 2020-11-17
Reviewed by Yusuke Suzuki.

JSTests:

Enable static public fields, and import new stress tests from the
V8 project. Also split the support code into a separate file
(harmony-support.js) to avoid duplication.

  • test262/config.yaml: enable static public fields.
  • stress/class-fields-harmony.js: use the new support file.
  • stress/class-fields-static-harmony.js: Added.
  • stress/resources/harmony-support.js: Added.

Source/JavaScriptCore:

Add support for static public class fields. We can reuse most of
the existing machinery available for instance fields. Like
instance fields, static fields are initialized with a synthetic
function. This is done to allow us to trivially follow the scoping
rules in the spec. As it happens with instance fields this could
be inlined in a future patch.

A lot of small changes in many files are just a matter of doing
s/instance/class/ for variables related to class fields, which
before were assuming there are only instance fields implemented.

  • bytecode/UnlinkedFunctionExecutable.cpp:

(JSC::generateUnlinkedFunctionCodeBlock): do s/instanceField/classField/.

  • bytecode/UnlinkedFunctionExecutable.h: ditto.
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitNewClassFieldInitializerFunction): ditto.

  • bytecompiler/BytecodeGenerator.h: ditto, plus add a parameter

for static field locations in emitDefineClassElements.

  • bytecompiler/NodesCodegen.cpp:

(JSC::PropertyListNode::emitBytecode): save static fields
locations when going through the property list.
(JSC::PropertyListNode::emitSaveComputedFieldName): consider
static fields here too.
(JSC::ClassExprNode::emitBytecode): call the initializer for
static fields as the very last action of the class creation.

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createDefineField): field nodes can be static
too now.

  • parser/NodeConstructors.h:

(JSC::DefineFieldNode::DefineFieldNode): ditto.

  • parser/Nodes.h: ditto.
  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseInner): s/instanceField/classField/
(JSC::Parser<LexerType>::parseClass): consider static fields.
(JSC::Parser<LexerType>::parseInstanceFieldInitializerSourceElements):
s/instanceField/classField/, and consider static fields.

  • parser/Parser.h:

(JSC::Parser<LexerType>::parse): s/instanceField/classField/
(JSC::parse): ditto.

  • runtime/JSFunction.cpp:

(JSC::JSFunction::setFunctionName): s/instanceField/classField/

  • runtime/OptionsList.h: add option to enable/disable static public fields.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r269801 r269922  
    187187static RegisterID* emitHomeObjectForCallee(BytecodeGenerator& generator)
    188188{
    189     if ((generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) && generator.parseMode() != SourceParseMode::InstanceFieldInitializerMode) {
     189    if ((generator.isDerivedClassContext() || generator.isDerivedConstructorContext()) && generator.parseMode() != SourceParseMode::ClassFieldInitializerMode) {
    190190        RegisterID* derivedConstructor = generator.emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment();
    191191        return generator.emitGetById(generator.newTemporary(), derivedConstructor, generator.propertyNames().builtinNames().homeObjectPrivateName());
     
    578578}
    579579
    580 RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector<JSTextPosition>* instanceFieldLocations)
     580RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dstOrConstructor, RegisterID* prototype, Vector<JSTextPosition>* instanceFieldLocations, Vector<JSTextPosition>* staticFieldLocations)
    581581{
    582582    PropertyListNode* p = this;
     
    593593            ASSERT(instanceFieldLocations);
    594594            instanceFieldLocations->append(p->position());
     595            continue;
     596        }
     597
     598        if (p->isStaticClassField()) {
     599            ASSERT(staticFieldLocations);
     600            staticFieldLocations->append(p->position());
    595601            continue;
    596602        }
     
    650656                ASSERT(node->m_type & PropertyNode::Constant);
    651657                instanceFieldLocations->append(p->position());
     658                continue;
     659            }
     660
     661            if (p->isStaticClassField()) {
     662                ASSERT(staticFieldLocations);
     663                staticFieldLocations->append(p->position());
    652664                continue;
    653665            }
     
    745757void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, RegisterID* newObj, PropertyNode& node)
    746758{
    747     // Private fields are handled in the synthetic instanceFieldInitializer function, not here.
     759    // Private fields are handled in a synthetic classFieldInitializer function, not here.
    748760    ASSERT(!(node.type() & PropertyNode::Private));
    749761
     
    801813{
    802814    ASSERT(node.isComputedClassField());
    803     RefPtr<RegisterID> propertyExpr;
    804815
    805816    // The 'name' refers to a synthetic private name in the class scope, where the property key is saved for later use.
     
    808819    ASSERT(!var.local());
    809820
    810     propertyExpr = generator.emitNode(node.m_expression);
    811     RegisterID* propertyName = generator.emitToPropertyKey(generator.newTemporary(), propertyExpr.get());
     821    RefPtr<RegisterID> propertyExpr = generator.emitNode(node.m_expression);
     822    RefPtr<RegisterID> propertyName = generator.emitToPropertyKey(generator.newTemporary(), propertyExpr.get());
     823
     824    if (node.isStaticClassField()) {
     825        Ref<Label> validPropertyNameLabel = generator.newLabel();
     826        RefPtr<RegisterID> prototypeString = generator.emitLoad(nullptr, JSValue(generator.addStringConstant(generator.propertyNames().prototype)));
     827        generator.emitJumpIfFalse(generator.emitBinaryOp<OpStricteq>(generator.newTemporary(), prototypeString.get(), propertyName.get(), OperandTypes(ResultType::stringType(), ResultType::stringType())), validPropertyNameLabel.get());
     828        generator.emitThrowTypeError("Cannot declare a static field named 'prototype'");
     829        generator.emitLabel(validPropertyNameLabel.get());
     830    }
    812831
    813832    RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var);
    814     generator.emitPutToScope(scope.get(), var, propertyName, ThrowIfNotFound, InitializationMode::ConstInitialization);
     833    generator.emitPutToScope(scope.get(), var, propertyName.get(), ThrowIfNotFound, InitializationMode::ConstInitialization);
    815834}
    816835
     
    49474966    generator.emitCallDefineProperty(constructor.get(), prototypeNameRegister.get(), prototype.get(), nullptr, nullptr, 0, m_position);
    49484967
     4968    Vector<JSTextPosition> staticFieldLocations;
    49494969    if (m_classElements) {
    49504970        m_classElements->emitDeclarePrivateFieldNames(generator, generator.scopeRegister());
    49514971
    49524972        Vector<JSTextPosition> instanceFieldLocations;
    4953         generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get(), instanceFieldLocations);
     4973        generator.emitDefineClassElements(m_classElements, constructor.get(), prototype.get(), instanceFieldLocations, staticFieldLocations);
    49544974        if (!instanceFieldLocations.isEmpty()) {
    4955             RefPtr<RegisterID> instanceFieldInitializer = generator.emitNewInstanceFieldInitializerFunction(generator.newTemporary(), WTFMove(instanceFieldLocations), m_classHeritage);
     4975            RefPtr<RegisterID> instanceFieldInitializer = generator.emitNewClassFieldInitializerFunction(generator.newTemporary(), WTFMove(instanceFieldLocations), m_classHeritage);
    49564976
    49574977            // FIXME: Skip this if the initializer function isn't going to need a home object (no eval or super properties)
     
    49634983    }
    49644984
    4965     if (m_needsLexicalScope) {
    4966         if (!m_name.isNull()) {
    4967             Variable classNameVar = generator.variable(m_name);
    4968             RELEASE_ASSERT(classNameVar.isResolved());
    4969             RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar);
    4970             generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization);
    4971         }
     4985    if (m_needsLexicalScope && !m_name.isNull()) {
     4986        Variable classNameVar = generator.variable(m_name);
     4987        RELEASE_ASSERT(classNameVar.isResolved());
     4988        RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, classNameVar);
     4989        generator.emitPutToScope(scope.get(), classNameVar, constructor.get(), ThrowIfNotFound, InitializationMode::Initialization);
     4990    }
     4991
     4992    if (!staticFieldLocations.isEmpty()) {
     4993        RefPtr<RegisterID> staticFieldInitializer = generator.emitNewClassFieldInitializerFunction(generator.newTemporary(), WTFMove(staticFieldLocations), m_classHeritage);
     4994        // FIXME: Skip this if the initializer function isn't going to need a home object (no eval or super properties)
     4995        // https://bugs.webkit.org/show_bug.cgi?id=196867
     4996        emitPutHomeObject(generator, staticFieldInitializer.get(), constructor.get());
     4997
     4998        CallArguments args(generator, nullptr);
     4999        generator.move(args.thisRegister(), constructor.get());
     5000        generator.emitCall(generator.newTemporary(), staticFieldInitializer.get(), NoExpectedFunction, args, position(), position(), position(), DebuggableCall::No);
     5001    }
     5002
     5003    if (m_needsLexicalScope)
    49725004        generator.popLexicalScope(this);
    4973     }
    49745005
    49755006    return generator.move(generator.finalDestination(dst, constructor.get()), constructor.get());
Note: See TracChangeset for help on using the changeset viewer.