Skip to content

Commit

Permalink
Fixes chakra-core#5477 - Forces computed property names within classe…
Browse files Browse the repository at this point in the history
…s to be executed in strict mode

The ECMA spec requires that a class definition is always strict mode code, this includes computed property names within classes. A flag, forceStrictModeForClassComputedPropertyName, is added into ByteCodeGenerator.h. When set, this flag will force bytecode opcodes to be emitted in strict mode when avaliable. This flag is set outside of emit calls under the condition that the bytecode being emitted corresponds to computed property names within classes.
  • Loading branch information
wyrichte committed Sep 13, 2018
1 parent 50eaeec commit 4f3f3bf
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 17 deletions.
33 changes: 28 additions & 5 deletions lib/Runtime/ByteCode/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4733,7 +4733,7 @@ void ByteCodeGenerator::EmitPropStore(Js::RegSlot rhsLocation, Symbol *sym, Iden
}
else
{
this->EmitPatchableRootProperty(GetStFldOpCode(funcInfo, true, isLetDecl, isConstDecl, false), rhsLocation, propertyId, false, true, funcInfo);
this->EmitPatchableRootProperty(GetStFldOpCode(funcInfo, true, isLetDecl, isConstDecl, false, forceStrictModeForClassComputedPropertyName), rhsLocation, propertyId, false, true, funcInfo);
}
}
else if (sym->GetIsFuncExpr())
Expand Down Expand Up @@ -5320,7 +5320,7 @@ void ByteCodeGenerator::EmitPropDelete(Js::RegSlot lhsLocation, Symbol *sym, Ide
if (this->flags & (fscrEval | fscrImplicitThis))
{
this->m_writer.ScopedProperty(Js::OpCode::ScopedDeleteFld, lhsLocation,
funcInfo->FindOrAddReferencedPropertyId(propertyId));
funcInfo->FindOrAddReferencedPropertyId(propertyId), forceStrictModeForClassComputedPropertyName);
}
else
{
Expand Down Expand Up @@ -6931,7 +6931,7 @@ void EmitAssignment(
{
uint cacheId = funcInfo->FindOrAddInlineCacheId(lhs->AsParseNodeBin()->pnode1->location, propertyId, false, true);
byteCodeGenerator->Writer()->PatchableProperty(
ByteCodeGenerator::GetStFldOpCode(funcInfo, false, false, false, false), rhsLocation, lhs->AsParseNodeBin()->pnode1->location, cacheId);
ByteCodeGenerator::GetStFldOpCode(funcInfo, false, false, false, false, byteCodeGenerator->forceStrictModeForClassComputedPropertyName), rhsLocation, lhs->AsParseNodeBin()->pnode1->location, cacheId);
}

break;
Expand Down Expand Up @@ -8347,7 +8347,19 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
// Transparently pass the name expr
// The Emit will replace this with a temp register if necessary to preserve the value.
nameNode->location = nameNode->AsParseNodeUni()->pnode1->location;

// Save the previous value of the flag to be restored later.
bool prevFlag = byteCodeGenerator->forceStrictModeForClassComputedPropertyName;

// Strict mode must be enforced on the evaluation of computed property names inside
// classes, thus enable the flag if the computed property name is a class member.
byteCodeGenerator->forceStrictModeForClassComputedPropertyName = isClassMember || prevFlag;

EmitBinaryOpnds(nameNode, exprNode, byteCodeGenerator, funcInfo);

// Restore the flag's previous value.
byteCodeGenerator->forceStrictModeForClassComputedPropertyName = prevFlag;

if (isFncDecl && !exprNode->AsParseNodeFnc()->IsClassConstructor())
{
EmitComputedFunctionNameVar(nameNode, exprNode->AsParseNodeFnc(), byteCodeGenerator);
Expand All @@ -8374,7 +8386,18 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
(isClassMember ? Js::OpCode::InitClassMemberSetComputedName : Js::OpCode::InitSetElemI) :
(isClassMember ? Js::OpCode::InitClassMemberComputedName : Js::OpCode::InitComputedProperty);

byteCodeGenerator->Writer()->Element(setOp, exprNode->location, objectLocation, nameNode->location, true);
// Save the previous value of the flag to be restored later.
bool prevFlag = byteCodeGenerator->forceStrictModeForClassComputedPropertyName;
byteCodeGenerator->forceStrictModeForClassComputedPropertyName = isClassMember || prevFlag;

// Strict mode must be enforced on the evaluation of computed property names inside
// classes, thus enable the flag if the computed property name is a class member.
byteCodeGenerator->Writer()->Element(setOp, exprNode->location, objectLocation, nameNode->location, true,
byteCodeGenerator->forceStrictModeForClassComputedPropertyName);

// Restore the flag's previous value.
byteCodeGenerator->forceStrictModeForClassComputedPropertyName = prevFlag;


funcInfo->ReleaseLoc(exprNode);
funcInfo->ReleaseLoc(nameNode);
Expand Down Expand Up @@ -10634,7 +10657,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
Js::PropertyId propertyId = pexpr->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
funcInfo->AcquireLoc(pnode);
byteCodeGenerator->Writer()->Property(Js::OpCode::DeleteFld, pnode->location, pexpr->AsParseNodeBin()->pnode1->location,
funcInfo->FindOrAddReferencedPropertyId(propertyId));
funcInfo->FindOrAddReferencedPropertyId(propertyId), byteCodeGenerator->forceStrictModeForClassComputedPropertyName);
}

break;
Expand Down
4 changes: 2 additions & 2 deletions lib/Runtime/ByteCode/ByteCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1923,9 +1923,9 @@ Scope * ByteCodeGenerator::FindScopeForSym(Scope *symScope, Scope *scope, Js::Pr
}

/* static */
Js::OpCode ByteCodeGenerator::GetStFldOpCode(FuncInfo* funcInfo, bool isRoot, bool isLetDecl, bool isConstDecl, bool isClassMemberInit)
Js::OpCode ByteCodeGenerator::GetStFldOpCode(FuncInfo* funcInfo, bool isRoot, bool isLetDecl, bool isConstDecl, bool isClassMemberInit, bool forceStrictModeForClassComputedPropertyName)
{
return GetStFldOpCode(funcInfo->GetIsStrictMode(), isRoot, isLetDecl, isConstDecl, isClassMemberInit);
return GetStFldOpCode(funcInfo->GetIsStrictMode() || forceStrictModeForClassComputedPropertyName, isRoot, isLetDecl, isConstDecl, isClassMemberInit);
}

/* static */
Expand Down
9 changes: 8 additions & 1 deletion lib/Runtime/ByteCode/ByteCodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ class ByteCodeGenerator
static const unsigned int MinArgumentsForCallOptimization = 16;
bool forceNoNative;

// A flag that when set will force bytecode opcodes to be emitted in strict mode when avaliable.
// This flag is set outside of emit calls under the condition that the bytecode being emitted
// corresponds to computed property names within classes. This fixes a bug where computed property
// names would not enforce strict mode when inside a class even though the spec requires that
// all code within a class must be strict.
bool forceStrictModeForClassComputedPropertyName = false;

ByteCodeGenerator(Js::ScriptContext* scriptContext, Js::ScopeInfo* parentScopeInfo);

#if DBG_DUMP
Expand Down Expand Up @@ -326,7 +333,7 @@ class ByteCodeGenerator
isStrictMode ? (isRoot ? Js::OpCode::StRootFldStrict : Js::OpCode::StFldStrict) :
isRoot ? Js::OpCode::StRootFld : Js::OpCode::StFld;
}
static Js::OpCode GetStFldOpCode(FuncInfo* funcInfo, bool isRoot, bool isLetDecl, bool isConstDecl, bool isClassMemberInit);
static Js::OpCode GetStFldOpCode(FuncInfo* funcInfo, bool isRoot, bool isLetDecl, bool isConstDecl, bool isClassMemberInit, bool forceStrictModeForClassComputedPropertyName = false);
static Js::OpCode GetScopedStFldOpCode(bool isStrictMode, bool isConsoleScope = false)
{
return isStrictMode ?
Expand Down
12 changes: 6 additions & 6 deletions lib/Runtime/ByteCode/ByteCodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1301,7 +1301,7 @@ namespace Js
return false;
}

void ByteCodeWriter::Element(OpCode op, RegSlot Value, RegSlot Instance, RegSlot Element, bool instanceAtReturnRegOK)
void ByteCodeWriter::Element(OpCode op, RegSlot Value, RegSlot Instance, RegSlot Element, bool instanceAtReturnRegOK, bool forceStrictMode)
{
CheckOpen();
CheckOp(op, OpLayoutType::ElementI);
Expand All @@ -1311,7 +1311,7 @@ namespace Js
Instance = ConsumeReg(Instance);
Element = ConsumeReg(Element);

if (this->m_functionWrite->GetIsStrictMode())
if (this->m_functionWrite->GetIsStrictMode() || forceStrictMode)
{
if (op == OpCode::DeleteElemI_A)
{
Expand Down Expand Up @@ -1401,7 +1401,7 @@ namespace Js
return false;
}

void ByteCodeWriter::ScopedProperty(OpCode op, RegSlot value, PropertyIdIndexType propertyIdIndex)
void ByteCodeWriter::ScopedProperty(OpCode op, RegSlot value, PropertyIdIndexType propertyIdIndex, bool forceStrictMode)
{
CheckOpen();
CheckOp(op, OpLayoutType::ElementScopedC);
Expand All @@ -1424,7 +1424,7 @@ namespace Js
}
#endif

if (this->m_functionWrite->GetIsStrictMode())
if (this->m_functionWrite->GetIsStrictMode() || forceStrictMode)
{
if (op == OpCode::ScopedDeleteFld)
{
Expand All @@ -1448,7 +1448,7 @@ namespace Js
return false;
}

void ByteCodeWriter::Property(OpCode op, RegSlot value, RegSlot instance, PropertyIdIndexType propertyIdIndex)
void ByteCodeWriter::Property(OpCode op, RegSlot value, RegSlot instance, PropertyIdIndexType propertyIdIndex, bool forceStrictMode)
{
CheckOpen();
CheckOp(op, OpLayoutType::ElementC);
Expand Down Expand Up @@ -1477,7 +1477,7 @@ namespace Js
}
#endif

if (this->m_functionWrite->GetIsStrictMode())
if (this->m_functionWrite->GetIsStrictMode() || forceStrictMode)
{
if (op == OpCode::DeleteFld)
{
Expand Down
6 changes: 3 additions & 3 deletions lib/Runtime/ByteCode/ByteCodeWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ namespace Js
void CallI(OpCode op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, ProfileId callSiteId, CallFlags callFlags = CallFlags_None);
void CallIExtended(OpCode op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, CallIExtendedOptions options, const void *buffer, uint byteCount, ProfileId callSiteId, CallFlags callFlags = CallFlags_None);
void RemoveEntryForRegSlotFromCacheIdMap(RegSlot functionRegister);
void Element(OpCode op, RegSlot value, RegSlot instance, RegSlot element, bool instanceAtReturnRegOK = false);
void Element(OpCode op, RegSlot value, RegSlot instance, RegSlot element, bool instanceAtReturnRegOK = false, bool forceStrictMode = false);
void ElementUnsigned1(OpCode op, RegSlot value, RegSlot instance, uint32 element);
void Property(OpCode op, RegSlot Value, RegSlot Instance, PropertyIdIndexType propertyIdIndex);
void ScopedProperty(OpCode op, RegSlot Value, PropertyIdIndexType propertyIdIndex);
void Property(OpCode op, RegSlot Value, RegSlot Instance, PropertyIdIndexType propertyIdIndex, bool forceStrictMode = false);
void ScopedProperty(OpCode op, RegSlot Value, PropertyIdIndexType propertyIdIndex, bool forceStrictMode = false);
void Slot(OpCode op, RegSlot value, RegSlot instance, uint32 slotId);
void Slot(OpCode op, RegSlot value, RegSlot instance, uint32 slotId, ProfileId profileId);
void SlotI1(OpCode op, RegSlot value, uint32 slotId1);
Expand Down
Loading

0 comments on commit 4f3f3bf

Please sign in to comment.