Ignore:
Timestamp:
Jun 30, 2021, 7:03:55 PM (4 years ago)
Author:
[email protected]
Message:

[JSC] Private names should be handled by usedVariables mechanism
https://bugs.webkit.org/show_bug.cgi?id=227476
rdar://76049469

Reviewed by Saam Barati.
Source/JavaScriptCore:

Private name handling in the current parser has many problems.

  1. The parser backtracks when it sees destructuring assignment, arrow function etc. In that case, the discarded code must not have any effect on the outside of that code. However, private name handling is annotating "used" of the upper scopes, which is wrong.
  2. In class expression, private name lookup intentionally skips the class-scope when parsing class heritage. But this is not correct since CodeBlock will perform lookup on the normal scope chain and this will look into the class-scope inconsistently. This means that we could encounter different private name at runtime. (it is tested in the added test).
  3. We skip inner function parsing when it is parsed previously. At that case, we must preserve private name annotation, but restored function information does not preserve that.

This patch changes how private name is handled.

  1. We were anyway defining #XXX variables which holds private symbols. So we track "use" information by the mechanism used for usual variables. We remove Used / Declared bits from PrivateNameEntry since they are not necessary at runtime, and these information is handled / tracked in Parser's Scope. For backtracking, we already have a mechanism to roll-back m_usedVariables, so using variable mechanism automatically fixes the problem.
  2. We define class-head-scope separately from class-scope. class-heritage expression can see class name, but it cannot use private names. Previously, our implementation attempted to achieve that by hacky way: skipping this class-scope for private names only while parsing class-heritage. But this was wrong since it does not consider CodeBlock's linking phase as described in the problem (2). Instead, we just define class-head-scope which holds class constructor name.
  3. We clean up popScopeInternal to populate lexical-variables and function-stack. Previously, we are stealing them before popping the scope when necessary, but this is a hack and a bit wrong since scope's popping operation needs to access these information in some cases. Instead, popScopeInternal populates them after popping the scope.
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::pushClassHeadLexicalScope):
(JSC::BytecodeGenerator::popClassHeadLexicalScope):

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ClassExprNode::emitBytecode):

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createClassExpr):
(JSC::ASTBuilder::createBlockStatement):
(JSC::ASTBuilder::createForLoop):
(JSC::ASTBuilder::createForInLoop):
(JSC::ASTBuilder::createForOfLoop):
(JSC::ASTBuilder::createTryStatement):
(JSC::ASTBuilder::createSwitchStatement):

  • parser/NodeConstructors.h:

(JSC::ForNode::ForNode):
(JSC::TryNode::TryNode):
(JSC::ClassExprNode::ClassExprNode):
(JSC::SwitchNode::SwitchNode):
(JSC::BlockNode::BlockNode):
(JSC::EnumerationNode::EnumerationNode):
(JSC::ForInNode::ForInNode):
(JSC::ForOfNode::ForOfNode):

  • parser/Nodes.cpp:

(JSC::ScopeNode::ScopeNode):
(JSC::ProgramNode::ProgramNode):
(JSC::ModuleProgramNode::ModuleProgramNode):
(JSC::EvalNode::EvalNode):
(JSC::FunctionNode::FunctionNode):
(JSC::VariableEnvironmentNode::VariableEnvironmentNode):

  • parser/Nodes.h:

(JSC::VariableEnvironmentNode::VariableEnvironmentNode): Deleted.

  • parser/Parser.cpp:

(JSC::isPrivateFieldName):
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseSwitchStatement):
(JSC::Parser<LexerType>::parseTryStatement):
(JSC::Parser<LexerType>::parseBlockStatement):
(JSC::Parser<LexerType>::parseFunctionDeclarationStatement):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseBinaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression):
(JSC::Parser<LexerType>::usePrivateName): Deleted.

  • parser/Parser.h:

(JSC::Scope::finalizeLexicalEnvironment):
(JSC::Scope::takeLexicalEnvironment):
(JSC::Scope::takeDeclaredVariables):
(JSC::Scope::takeFunctionDeclarations):
(JSC::Scope::forEachUsedVariable):
(JSC::Scope::usePrivateName):
(JSC::Scope::currentUsedVariablesSize):
(JSC::Parser::popScopeInternal):
(JSC::Parser::popScope):
(JSC::Parser<LexerType>::parse):
(JSC::Scope::copyUndeclaredPrivateNamesTo): Deleted.
(JSC::Scope::hasUsedButUndeclaredPrivateNames const): Deleted.
(JSC::Parser::privateNameScope): Deleted.
(JSC::Parser::copyUndeclaredPrivateNamesToOuterScope): Deleted.

  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createClassExpr):
(JSC::SyntaxChecker::createBlockStatement):
(JSC::SyntaxChecker::createForLoop):
(JSC::SyntaxChecker::createForInLoop):
(JSC::SyntaxChecker::createForOfLoop):
(JSC::SyntaxChecker::createTryStatement):
(JSC::SyntaxChecker::createSwitchStatement):

  • parser/VariableEnvironment.cpp:

(JSC::VariableEnvironmentEntry::dump const):
(JSC::VariableEnvironment::declarePrivateField):
(JSC::VariableEnvironment::declarePrivateAccessor):
(JSC::VariableEnvironment::declarePrivateMethod):
(JSC::VariableEnvironment::dump const):

  • parser/VariableEnvironment.h:

(JSC::VariableEnvironment::declarePrivateField):
(JSC::VariableEnvironment::privateNameEnvironment):
(JSC::VariableEnvironment::addPrivateNamesFrom):
(JSC::PrivateNameEntry::isUsed const): Deleted.
(JSC::PrivateNameEntry::isDeclared const): Deleted.
(JSC::PrivateNameEntry::setIsUsed): Deleted.
(JSC::PrivateNameEntry::setIsDeclared): Deleted.
(JSC::VariableEnvironment::usePrivateName): Deleted.
(JSC::VariableEnvironment::copyPrivateNamesTo const): Deleted.
(JSC::VariableEnvironment::copyUndeclaredPrivateNamesTo const): Deleted.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/VariableEnvironment.h

    r278591 r279447  
    7575    }
    7676
     77    void dump(PrintStream&) const;
     78
    7779private:
    7880    enum Traits : uint16_t {
     
    108110    PrivateNameEntry(uint16_t traits = 0) { m_bits = traits; }
    109111
    110     ALWAYS_INLINE bool isUsed() const { return m_bits & IsUsed; }
    111     ALWAYS_INLINE bool isDeclared() const { return m_bits & IsDeclared; }
    112112    ALWAYS_INLINE bool isMethod() const { return m_bits & IsMethod; }
    113113    ALWAYS_INLINE bool isSetter() const { return m_bits & IsSetter; }
     
    118118    bool isPrivateMethodOrAccessor() const { return isMethod() || isSetter() || isGetter(); }
    119119
    120     ALWAYS_INLINE void setIsUsed() { m_bits |= IsUsed; }
    121     ALWAYS_INLINE void setIsDeclared() { m_bits |= IsDeclared; }
    122 
    123120    uint16_t bits() const { return m_bits; }
    124121
     
    130127    enum Traits : uint16_t {
    131128        None = 0,
    132         IsUsed = 1 << 0,
    133         IsDeclared = 1 << 1,
    134         IsMethod = 1 << 2,
    135         IsGetter = 1 << 3,
    136         IsSetter = 1 << 4,
    137         IsStatic = 1 << 5,
     129        IsMethod = 1 << 0,
     130        IsGetter = 1 << 1,
     131        IsSetter = 1 << 2,
     132        IsStatic = 1 << 3,
    138133    };
    139134
     
    207202
    208203    ALWAYS_INLINE Map::AddResult declarePrivateField(const Identifier& identifier) { return declarePrivateField(identifier.impl()); }
    209     ALWAYS_INLINE void usePrivateName(const Identifier& identifier) { usePrivateName(identifier.impl()); }
    210204
    211205    bool declarePrivateMethod(const Identifier& identifier) { return declarePrivateMethod(identifier.impl()); }
     
    233227    PrivateDeclarationResult declarePrivateGetter(const RefPtr<UniquedStringImpl>& identifier, PrivateNameEntry::Traits modifierTraits = PrivateNameEntry::Traits::None);
    234228
    235     Map::AddResult declarePrivateField(const RefPtr<UniquedStringImpl>& identifier)
    236     {
    237         auto& meta = getOrAddPrivateName(identifier.get());
    238         meta.setIsDeclared();
    239         auto entry = VariableEnvironmentEntry();
    240         entry.setIsPrivateField();
    241         entry.setIsConst();
    242         entry.setIsCaptured();
    243         return m_map.add(identifier, entry);
    244     }
    245     void usePrivateName(const RefPtr<UniquedStringImpl>& identifier)
    246     {
    247         auto& meta = getOrAddPrivateName(identifier.get());
    248         meta.setIsUsed();
    249         if (meta.isDeclared())
    250             find(identifier)->value.setIsCaptured();
    251     }
     229    Map::AddResult declarePrivateField(const RefPtr<UniquedStringImpl>&);
    252230
    253231    ALWAYS_INLINE PrivateNamesRange privateNames() const
     
    263241            return 0;
    264242        return m_rareData->m_privateNames.size();
     243    }
     244
     245    ALWAYS_INLINE PrivateNameEnvironment* privateNameEnvironment()
     246    {
     247        if (!m_rareData)
     248            return nullptr;
     249        return &m_rareData->m_privateNames;
    265250    }
    266251
     
    305290    }
    306291
    307     ALWAYS_INLINE void copyPrivateNamesTo(VariableEnvironment& other) const
    308     {
    309         if (!m_rareData)
    310             return;
    311         if (!other.m_rareData)
    312             other.m_rareData = WTF::makeUnique<VariableEnvironment::RareData>();
    313         if (privateNamesSize() > 0) {
    314             for (auto entry : privateNames()) {
    315                 if (!(entry.value.isUsed() && entry.value.isDeclared()))
    316                     other.m_rareData->m_privateNames.add(entry.key, entry.value);
    317             }
    318         }
    319     }
    320 
    321292    ALWAYS_INLINE void addPrivateNamesFrom(const PrivateNameEnvironment* privateNameEnvironment)
    322293    {
     
    327298            m_rareData = makeUnique<VariableEnvironment::RareData>();
    328299
    329         for (auto entry : *privateNameEnvironment) {
    330             ASSERT(entry.value.isDeclared());
     300        for (auto entry : *privateNameEnvironment)
    331301            m_rareData->m_privateNames.add(entry.key, entry.value);
    332         }
    333     }
    334 
    335     ALWAYS_INLINE void copyUndeclaredPrivateNamesTo(VariableEnvironment& outer) const {
    336         // Used by the Parser to transfer recorded uses of PrivateNames from an
    337         // inner PrivateNameEnvironment into an outer one, in case a PNE is used
    338         // earlier in the source code than it is defined.
    339         if (privateNamesSize() > 0) {
    340             for (auto entry : privateNames()) {
    341                 if (entry.value.isUsed() && !entry.value.isDeclared())
    342                     outer.getOrAddPrivateName(entry.key.get()).setIsUsed();
    343             }
    344         }
    345302    }
    346303
     
    357314        PrivateNameEnvironment m_privateNames;
    358315    };
     316
     317    void dump(PrintStream&) const;
    359318
    360319private:
Note: See TracChangeset for help on using the changeset viewer.