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.
Location:
trunk/Source/JavaScriptCore/bytecompiler
Files:
3 edited

Legend:

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

    r269115 r269922  
    382382
    383383    bool shouldCaptureAllOfTheThings = shouldEmitDebugHooks() || codeBlock->usesEval();
    384     bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())) && parseMode != SourceParseMode::InstanceFieldInitializerMode;
     384    bool needsArguments = ((functionNode->usesArguments() && !codeBlock->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())) && parseMode != SourceParseMode::ClassFieldInitializerMode;
    385385
    386386    if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) {
     
    614614
    615615        bool shouldCreateArgumensVariable = !haveParameterNamedArguments
    616             && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::InstanceFieldInitializerMode).contains(m_codeBlock->parseMode());
     616            && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::ClassFieldInitializerMode).contains(m_codeBlock->parseMode());
    617617        shouldCreateArgumentsVariableInParameterScope = shouldCreateArgumensVariable && !isSimpleParameterList;
    618618        // Do not create arguments variable in case of Arrow function. Value will be loaded from parent scope
     
    31343134}
    31353135
    3136 RegisterID* BytecodeGenerator::emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived)
     3136RegisterID* BytecodeGenerator::emitNewClassFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& classFieldLocations, bool isDerived)
    31373137{
    31383138    DerivedContextType newDerivedContextType;
     
    31473147
    31483148    auto variablesUnderTDZ = getVariablesUnderTDZ();
    3149     SourceParseMode parseMode = SourceParseMode::InstanceFieldInitializerMode;
     3149    SourceParseMode parseMode = SourceParseMode::ClassFieldInitializerMode;
    31503150    ConstructAbility constructAbility = ConstructAbility::CannotConstruct;
    31513151
     
    31543154    metadata.finishParsing(m_scopeNode->source(), Identifier(), FunctionMode::MethodDefinition);
    31553155    auto initializer = UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), &metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(variablesUnderTDZ), newDerivedContextType, NeedsClassFieldInitializer::No);
    3156     initializer->setInstanceFieldLocations(WTFMove(instanceFieldLocations));
     3156    initializer->setClassFieldLocations(WTFMove(classFieldLocations));
    31573157
    31583158    unsigned index = m_codeBlock->addFunctionExpr(initializer);
     
    44754475RegisterID* BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment(const Identifier& identifier)
    44764476{
    4477     ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode || m_codeBlock->parseMode() == SourceParseMode::InstanceFieldInitializerMode);
     4477    ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode || m_codeBlock->parseMode() == SourceParseMode::ClassFieldInitializerMode);
    44784478
    44794479    return emitResolveScope(nullptr, variable(identifier, ThisResolutionType::Scoped));
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r269115 r269922  
    583583        }
    584584
    585         RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype, Vector<JSTextPosition>& instanceFieldLocations)
     585        RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype, Vector<JSTextPosition>& instanceFieldLocations, Vector<JSTextPosition>& staticFieldLocations)
    586586        {
    587587            ASSERT(constructor->refCount() && prototype->refCount());
     
    590590            if (UNLIKELY(n->needsDebugHook()))
    591591                emitDebugHook(n);
    592             return n->emitBytecode(*this, constructor, prototype, &instanceFieldLocations);
     592            return n->emitBytecode(*this, constructor, prototype, &instanceFieldLocations, &staticFieldLocations);
    593593        }
    594594
     
    777777        RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*);
    778778        RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource, NeedsClassFieldInitializer);
    779         RegisterID* emitNewInstanceFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& instanceFieldLocations, bool isDerived);
     779        RegisterID* emitNewClassFieldInitializerFunction(RegisterID* dst, Vector<JSTextPosition>&& classFieldLocations, bool isDerived);
    780780        RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*);
    781781        RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*);
  • 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.