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

[JSC] Stop generating default parameter code if class constructor is called without 'new'
https://bugs.webkit.org/show_bug.cgi?id=227547
rdar://78821453

Reviewed by Mark Lam.

JSTests:

  • stress/calling-non-callable-constructors.js: Added.

(shouldThrow):

Source/JavaScriptCore:

We already do not generate body bytecode when class constructor is called without 'new' because many features including "super()" assume
that they generate bytecode only when it is called as a constructor. But we are not doing that for default parameters' bytecode generation.
This patch stops generating bytecode for default parameters if class constructor is called without 'new'.

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::BytecodeGenerator):

  • runtime/ConstructorKind.h:
File:
1 edited

Legend:

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

    r279447 r279448  
    153153        return ParserError(ParserError::OutOfMemory);
    154154
    155     m_codeBlock->setThisRegister(m_thisRegister.virtualRegister());
    156 
    157     emitLogShadowChickenPrologueIfNecessary();
    158    
    159     // If we have declared a variable named "arguments" and we are using arguments then we should
    160     // perform that assignment now.
    161     if (m_needToInitializeArguments)
    162         initializeVariable(variable(propertyNames().arguments), m_argumentsRegister);
    163 
    164     if (m_restParameter)
    165         m_restParameter->emit(*this);
    166 
    167     {
    168         RefPtr<RegisterID> temp = newTemporary();
    169         RefPtr<RegisterID> tolLevelScope;
    170         for (auto functionPair : m_functionsToInitialize) {
    171             FunctionMetadataNode* metadata = functionPair.first;
    172             FunctionVariableType functionType = functionPair.second;
    173             emitNewFunction(temp.get(), metadata);
    174             if (functionType == NormalFunctionVariable)
    175                 initializeVariable(variable(metadata->ident()), temp.get());
    176             else if (functionType == TopLevelFunctionVariable) {
    177                 if (!tolLevelScope) {
    178                     // We know this will resolve to the top level scope or global object because our parser/global initialization code
    179                     // doesn't allow let/const/class variables to have the same names as functions.
    180                     // This is a top level function, and it's an error to ever create a top level function
    181                     // name that would resolve to a lexical variable. E.g:
    182                     // ```
    183                     //     function f() {
    184                     //         {
    185                     //             let x;
    186                     //             {
    187                     //             //// error thrown here
    188                     //                  eval("function x(){}");
    189                     //             }
    190                     //         }
    191                     //     }
    192                     // ```
    193                     // Therefore, we're guaranteed to have this resolve to a top level variable.
    194                     RefPtr<RegisterID> tolLevelObjectScope = emitResolveScope(nullptr, Variable(metadata->ident()));
    195                     tolLevelScope = newBlockScopeVariable();
    196                     move(tolLevelScope.get(), tolLevelObjectScope.get());
    197                 }
    198                 emitPutToScope(tolLevelScope.get(), Variable(metadata->ident()), temp.get(), ThrowIfNotFound, InitializationMode::NotInitialization);
    199             } else
    200                 RELEASE_ASSERT_NOT_REACHED();
    201         }
    202     }
    203    
    204     bool callingClassConstructor = false;
     155    bool callingNonCallableConstructor = false;
    205156    switch (constructorKind()) {
    206157    case ConstructorKind::None:
     158        break;
    207159    case ConstructorKind::Naked:
    208         break;
    209160    case ConstructorKind::Base:
    210161    case ConstructorKind::Extends:
    211         callingClassConstructor = !isConstructor();
     162        callingNonCallableConstructor = !isConstructor();
    212163        break;
    213164    }
    214     if (!callingClassConstructor)
     165
     166    m_codeBlock->setThisRegister(m_thisRegister.virtualRegister());
     167
     168    emitLogShadowChickenPrologueIfNecessary();
     169   
     170    if (!callingNonCallableConstructor) {
     171        // If we have declared a variable named "arguments" and we are using arguments then we should
     172        // perform that assignment now.
     173        if (m_needToInitializeArguments)
     174            initializeVariable(variable(propertyNames().arguments), m_argumentsRegister);
     175
     176        if (m_restParameter)
     177            m_restParameter->emit(*this);
     178
     179        {
     180            RefPtr<RegisterID> temp = newTemporary();
     181            RefPtr<RegisterID> tolLevelScope;
     182            for (auto functionPair : m_functionsToInitialize) {
     183                FunctionMetadataNode* metadata = functionPair.first;
     184                FunctionVariableType functionType = functionPair.second;
     185                emitNewFunction(temp.get(), metadata);
     186                if (functionType == NormalFunctionVariable)
     187                    initializeVariable(variable(metadata->ident()), temp.get());
     188                else if (functionType == TopLevelFunctionVariable) {
     189                    if (!tolLevelScope) {
     190                        // We know this will resolve to the top level scope or global object because our parser/global initialization code
     191                        // doesn't allow let/const/class variables to have the same names as functions.
     192                        // This is a top level function, and it's an error to ever create a top level function
     193                        // name that would resolve to a lexical variable. E.g:
     194                        // ```
     195                        //     function f() {
     196                        //         {
     197                        //             let x;
     198                        //             {
     199                        //             //// error thrown here
     200                        //                  eval("function x(){}");
     201                        //             }
     202                        //         }
     203                        //     }
     204                        // ```
     205                        // Therefore, we're guaranteed to have this resolve to a top level variable.
     206                        RefPtr<RegisterID> tolLevelObjectScope = emitResolveScope(nullptr, Variable(metadata->ident()));
     207                        tolLevelScope = newBlockScopeVariable();
     208                        move(tolLevelScope.get(), tolLevelObjectScope.get());
     209                    }
     210                    emitPutToScope(tolLevelScope.get(), Variable(metadata->ident()), temp.get(), ThrowIfNotFound, InitializationMode::NotInitialization);
     211                } else
     212                    RELEASE_ASSERT_NOT_REACHED();
     213            }
     214        }
     215   
    215216        m_scopeNode->emitBytecode(*this);
    216     else {
     217    } else {
    217218        // At this point we would have emitted an unconditional throw followed by some nonsense that's
    218219        // just an artifact of how this generator is structured. That code never runs, but it confuses
     
    442443
    443444    emitCheckTraps();
     445
     446    switch (constructorKind()) {
     447    case ConstructorKind::None:
     448        break;
     449    case ConstructorKind::Naked:
     450        if (!isConstructor()) {
     451            emitThrowTypeError("Cannot call a constructor without |new|");
     452            return;
     453        }
     454        break;
     455    case ConstructorKind::Base:
     456    case ConstructorKind::Extends:
     457        if (!isConstructor()) {
     458            emitThrowTypeError("Cannot call a class constructor without |new|");
     459            return;
     460        }
     461        break;
     462    }
    444463   
    445464    if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode())) {
     
    700719    }
    701720
     721    case SourceParseMode::ArrowFunctionMode:
     722        break;
     723
    702724    default: {
    703         if (SourceParseMode::ArrowFunctionMode != parseMode) {
    704             if (isConstructor()) {
    705                 if (m_newTargetRegister)
    706                     move(m_newTargetRegister, &m_thisRegister);
    707                 switch (constructorKind()) {
    708                 case ConstructorKind::Naked:
    709                     // Naked constructor not create |this| automatically.
    710                     break;
    711                 case ConstructorKind::None:
    712                 case ConstructorKind::Base:
    713                     emitCreateThis(&m_thisRegister);
    714                     if (Options::usePrivateMethods() && privateBrandRequirement() == PrivateBrandRequirement::Needed)
    715                         emitInstallPrivateBrand(&m_thisRegister);
    716 
    717                     emitInstanceFieldInitializationIfNeeded(&m_thisRegister, &m_calleeRegister, m_scopeNode->position(), m_scopeNode->position(), m_scopeNode->position());
    718                     break;
    719                 case ConstructorKind::Extends:
    720                     moveEmptyValue(&m_thisRegister);
    721                     break;
     725        if (isConstructor()) {
     726            if (m_newTargetRegister)
     727                move(m_newTargetRegister, &m_thisRegister);
     728            switch (constructorKind()) {
     729            case ConstructorKind::Naked:
     730                // Naked constructor not create |this| automatically.
     731                break;
     732            case ConstructorKind::None:
     733            case ConstructorKind::Base:
     734                emitCreateThis(&m_thisRegister);
     735                if (Options::usePrivateMethods() && privateBrandRequirement() == PrivateBrandRequirement::Needed)
     736                    emitInstallPrivateBrand(&m_thisRegister);
     737
     738                emitInstanceFieldInitializationIfNeeded(&m_thisRegister, &m_calleeRegister, m_scopeNode->position(), m_scopeNode->position(), m_scopeNode->position());
     739                break;
     740            case ConstructorKind::Extends:
     741                moveEmptyValue(&m_thisRegister);
     742                break;
     743            }
     744        } else {
     745            switch (constructorKind()) {
     746            case ConstructorKind::None: {
     747                bool shouldEmitToThis = false;
     748                if (functionNode->usesThis() || usesEval() || m_scopeNode->doAnyInnerArrowFunctionsUseThis() || m_scopeNode->doAnyInnerArrowFunctionsUseEval())
     749                    shouldEmitToThis = true;
     750                else if ((functionNode->usesSuperProperty() || m_scopeNode->doAnyInnerArrowFunctionsUseSuperProperty()) && !ecmaMode.isStrict()) {
     751                    // We must emit to_this when we're not in strict mode because we
     752                    // will convert |this| to an object, and that object may be passed
     753                    // to a strict function as |this|. This is observable because that
     754                    // strict function's to_this will just return the object.
     755                    //
     756                    // We don't need to emit this for strict-mode code because
     757                    // strict-mode code may call another strict function, which will
     758                    // to_this if it directly uses this; this is OK, because we defer
     759                    // to_this until |this| is used directly. Strict-mode code might
     760                    // also call a sloppy mode function, and that will to_this, which
     761                    // will defer the conversion, again, until necessary.
     762                    shouldEmitToThis = true;
    722763                }
    723             } else {
    724                 switch (constructorKind()) {
    725                 case ConstructorKind::None: {
    726                     bool shouldEmitToThis = false;
    727                     if (functionNode->usesThis() || usesEval() || m_scopeNode->doAnyInnerArrowFunctionsUseThis() || m_scopeNode->doAnyInnerArrowFunctionsUseEval())
    728                         shouldEmitToThis = true;
    729                     else if ((functionNode->usesSuperProperty() || m_scopeNode->doAnyInnerArrowFunctionsUseSuperProperty()) && !ecmaMode.isStrict()) {
    730                         // We must emit to_this when we're not in strict mode because we
    731                         // will convert |this| to an object, and that object may be passed
    732                         // to a strict function as |this|. This is observable because that
    733                         // strict function's to_this will just return the object.
    734                         //
    735                         // We don't need to emit this for strict-mode code because
    736                         // strict-mode code may call another strict function, which will
    737                         // to_this if it directly uses this; this is OK, because we defer
    738                         // to_this until |this| is used directly. Strict-mode code might
    739                         // also call a sloppy mode function, and that will to_this, which
    740                         // will defer the conversion, again, until necessary.
    741                         shouldEmitToThis = true;
    742                     }
    743 
    744                     if (shouldEmitToThis)
    745                         emitToThis();
    746                     break;
    747                 }
    748                 case ConstructorKind::Naked:
    749                     emitThrowTypeError("Cannot call a constructor without |new|");
    750                     break;
    751                 case ConstructorKind::Base:
    752                 case ConstructorKind::Extends:
    753                     emitThrowTypeError("Cannot call a class constructor without |new|");
    754                     break;
    755                 }
     764
     765                if (shouldEmitToThis)
     766                    emitToThis();
     767                break;
     768            }
     769            case ConstructorKind::Naked:
     770            case ConstructorKind::Base:
     771            case ConstructorKind::Extends:
     772                RELEASE_ASSERT_NOT_REACHED();
     773                break;
    756774            }
    757775        }
Note: See TracChangeset for help on using the changeset viewer.