Skip to content

Commit 341a8c9

Browse files
author
Suwei Chen
committed
[MERGE #2327 @suwc] Issue#829: class extends null should bind this
Merge pull request #2327 from suwc:build/suwc/buddy A class that extends null should have 'this' binding in its constructor same way as a base class. Add new function attribute 'BaseConstructorKind' to capture 'extends null' cases during class definition evaluation. Add new opcode for branching on 'extends null' scenario for 'this' binding. Fixes #829
2 parents ee8e57e + 6a94785 commit 341a8c9

19 files changed

+247
-143
lines changed

lib/Backend/FlowGraph.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ FlowGraph::RunPeeps()
323323
case Js::OpCode::BrFncCachedScopeNeq:
324324
case Js::OpCode::BrOnObject_A:
325325
case Js::OpCode::BrOnClassConstructor:
326+
case Js::OpCode::BrOnBaseConstructorKind:
326327
if (tryUnsignedCmpPeep)
327328
{
328329
this->UnsignedCmpPeep(instr);

lib/Backend/JnHelperMethodList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ HELPERCALL(ScrObj_OP_IsInst, Js::JavascriptOperators::OP_IsInst, AttrCanThrow)
4444
HELPERCALL(Op_IsIn, Js::JavascriptOperators::IsIn, AttrCanThrow)
4545
HELPERCALL(Op_IsObject, Js::JavascriptOperators::IsObject, AttrCanThrow)
4646
HELPERCALL(Op_IsClassConstructor, Js::JavascriptOperators::IsClassConstructor, AttrCanThrow)
47+
HELPERCALL(Op_IsBaseConstructorKind, Js::JavascriptOperators::IsBaseConstructorKind, AttrCanThrow)
4748
HELPERCALL(Op_LoadHeapArguments, Js::JavascriptOperators::LoadHeapArguments, 0)
4849
HELPERCALL(Op_LoadHeapArgsCached, Js::JavascriptOperators::LoadHeapArgsCached, 0)
4950
HELPERCALL(OP_InitCachedScope, Js::JavascriptOperators::OP_InitCachedScope, 0)

lib/Backend/Lower.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,6 +2154,10 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
21542154
}
21552155
break;
21562156

2157+
case Js::OpCode::BrOnBaseConstructorKind:
2158+
this->LowerBrOnClassConstructor(instr, IR::HelperOp_IsBaseConstructorKind);
2159+
break;
2160+
21572161
case Js::OpCode::BrOnClassConstructor:
21582162
this->LowerBrOnClassConstructor(instr, IR::HelperOp_IsClassConstructor);
21592163
break;

lib/Backend/LowerMDShared.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ LowererMD::LowerCondBranch(IR::Instr * instr)
954954
case Js::OpCode::BrNotNull_A:
955955
case Js::OpCode::BrOnObject_A:
956956
case Js::OpCode::BrOnClassConstructor:
957+
case Js::OpCode::BrOnBaseConstructorKind:
957958
Assert(!opndSrc1->IsFloat64());
958959
AssertMsg(instr->GetSrc2() == nullptr, "Expected 1 src on boolean branch");
959960
instrPrev = IR::Instr::New(Js::OpCode::TEST, this->m_func);

lib/Backend/arm/LowerMD.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,6 +2710,7 @@ LowererMD::LowerCondBranch(IR::Instr * instr)
27102710
case Js::OpCode::BrNotNull_A:
27112711
case Js::OpCode::BrOnObject_A:
27122712
case Js::OpCode::BrOnClassConstructor:
2713+
case Js::OpCode::BrOnBaseConstructorKind:
27132714
Assert(!opndSrc1->IsFloat64());
27142715
AssertMsg(opndSrc1->IsRegOpnd(),"NYI for other operands");
27152716
AssertMsg(instr->GetSrc2() == nullptr, "Expected 1 src on boolean branch");

lib/Runtime/Base/FunctionInfo.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ namespace Js
3939
Module = 0x20000, // The function is the function body wrapper for a module
4040
EnclosedByGlobalFunc = 0x40000,
4141
CanDefer = 0x80000,
42-
AllowDirectSuper = 0x100000
42+
AllowDirectSuper = 0x100000,
43+
BaseConstructorKind = 0x200000
4344
};
4445
FunctionInfo(JavascriptMethod entryPoint, Attributes attributes = None, LocalFunctionId functionId = Js::Constants::NoFunctionId, FunctionProxy* functionBodyImpl = nullptr);
4546

@@ -133,6 +134,8 @@ namespace Js
133134
bool GetEnclosedByGlobalFunc() const { return (attributes & Attributes::EnclosedByGlobalFunc) != 0; }
134135
void SetAllowDirectSuper() { attributes = (Attributes)(attributes | Attributes::AllowDirectSuper); }
135136
bool GetAllowDirectSuper() const { return (attributes & Attributes::AllowDirectSuper) != 0; }
137+
void SetBaseConstructorKind() { attributes = (Attributes)(attributes | Attributes::BaseConstructorKind); }
138+
bool GetBaseConstructorKind() const { return (attributes & Attributes::BaseConstructorKind) != 0; }
136139

137140
protected:
138141
JavascriptMethod originalEntryPoint;

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,16 +2113,31 @@ void ByteCodeGenerator::LoadThisObject(FuncInfo *funcInfo, bool thisLoadedFromPa
21132113
{
21142114
if (this->scriptContext->GetConfig()->IsES6ClassAndExtendsEnabled() && funcInfo->IsClassConstructor())
21152115
{
2116-
// Derived class constructors initialize 'this' to be Undecl
2116+
// Derived class constructors initialize 'this' to be Undecl except "extends null" cases
21172117
// - we'll check this value during a super call and during 'this' access
2118-
// Base class constructors initialize 'this' to a new object using new.target
2118+
//
2119+
// Base class constructors or "extends null" cases initialize 'this' to a new object using new.target
21192120
if (funcInfo->IsBaseClassConstructor())
21202121
{
21212122
EmitBaseClassConstructorThisObject(funcInfo);
21222123
}
21232124
else
21242125
{
2125-
this->m_writer.Reg1(Js::OpCode::InitUndecl, funcInfo->thisPointerRegister);
2126+
Js::ByteCodeLabel thisLabel = this->Writer()->DefineLabel();
2127+
Js::ByteCodeLabel skipLabel = this->Writer()->DefineLabel();
2128+
2129+
Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister();
2130+
this->Writer()->Reg1(Js::OpCode::LdFuncObj, tmpReg);
2131+
this->Writer()->BrReg1(Js::OpCode::BrOnBaseConstructorKind, thisLabel, tmpReg); // branch when [[ConstructorKind]]=="base"
2132+
funcInfo->ReleaseTmpRegister(tmpReg);
2133+
2134+
this->m_writer.Reg1(Js::OpCode::InitUndecl, funcInfo->thisPointerRegister); // not "extends null" case
2135+
this->Writer()->Br(Js::OpCode::Br, skipLabel);
2136+
2137+
this->Writer()->MarkLabel(thisLabel);
2138+
EmitBaseClassConstructorThisObject(funcInfo); // "extends null" case
2139+
2140+
this->Writer()->MarkLabel(skipLabel);
21262141
}
21272142
}
21282143
else if (!funcInfo->IsGlobalFunction() || (this->flags & fscrEval))
@@ -2411,12 +2426,6 @@ void ByteCodeGenerator::EmitClassConstructorEndCode(FuncInfo *funcInfo)
24112426
EmitScopeSlotLoadThis(funcInfo, funcInfo->thisPointerRegister);
24122427
this->Writer()->Reg2(Js::OpCode::Ld_A, ByteCodeGenerator::ReturnRegister, funcInfo->thisPointerRegister);
24132428
}
2414-
else
2415-
{
2416-
// This is the case where we don't have any references to this or super in the constructor or any nested lambda functions.
2417-
// We know 'this' must be undecl so let's just emit the ReferenceError as part of the fallthrough.
2418-
EmitUseBeforeDeclarationRuntimeError(this, ByteCodeGenerator::ReturnRegister);
2419-
}
24202429
}
24212430

24222431
void ByteCodeGenerator::EmitBaseClassConstructorThisObject(FuncInfo *funcInfo)

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2973,10 +2973,10 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat
29732973
top->AssignUndefinedConstRegister();
29742974

29752975
top->AssignNewTargetRegister();
2976+
top->AssignThisRegister();
29762977

29772978
if (top->GetCallsEval() || top->GetChildCallsEval())
29782979
{
2979-
top->AssignThisRegister();
29802980
top->SetIsThisLexicallyCaptured();
29812981
top->SetIsNewTargetLexicallyCaptured();
29822982
top->SetIsSuperLexicallyCaptured();

lib/Runtime/ByteCode/OpCodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ MACRO_EXTEND_WMS( InitClassMemberGet, ElementC, OpSideEffect
318318
MACRO_EXTEND_WMS( InitClassMemberSetComputedName,ElementI, OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut) // Class member in set syntax with computed property name
319319
MACRO_EXTEND_WMS( InitClassMemberGetComputedName,ElementI, OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut) // Class member in get syntax with computed property name
320320
MACRO_EXTEND_WMS( BrOnClassConstructor, BrReg1, None) // Branch if argument is a class constructor
321+
MACRO_EXTEND_WMS( BrOnBaseConstructorKind, BrReg1, None) // Branch if argument's [[ConstructorKind]] is 'base'
321322

322323
MACRO_BACKEND_ONLY( ArgIn_A, Empty, None) // Copy from "in slot" to "local slot", unchecked
323324
MACRO_WMS( ArgIn0, Reg1, OpByteCodeOnly) // Copy from "in slot" to "local slot", unchecked

lib/Runtime/Language/InterpreterHandler.inl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ EXDEF3_WMS(CUSTOM, InitClassMemberGetComputedName, OP_InitClass
219219
EXDEF3_WMS(CUSTOM, InitClassMemberGet, OP_InitClassMemberGet, ElementC)
220220
EXDEF3_WMS(CUSTOM, InitClassMemberSetComputedName, OP_InitClassMemberSetComputedName, ElementI)
221221
EXDEF2_WMS(BRB, BrOnClassConstructor, OP_BrOnClassConstructor)
222+
EXDEF2_WMS(BRB, BrOnBaseConstructorKind, OP_BrOnBaseConstructorKind)
222223
DEF3_WMS(GET_ELEM_LOCALSLOTNonVar,LdLocalSlot, OP_LdSlot, ElementSlotI1)
223224
EXDEF3_WMS(GET_ELEM_PARAMSLOTNonVar,LdParamSlot, OP_LdSlot, ElementSlotI1)
224225
DEF3_WMS(GET_ELEM_INNERSLOTNonVar,LdInnerSlot, OP_LdInnerSlot, ElementSlotI2)

0 commit comments

Comments
 (0)