Skip to content

Commit 2b9da66

Browse files
author
Suwei Chen
committed
[1.7>master] [1.6>1.7] [MERGE chakra-core#3336 @suwc] Fix Issue#3064/chakra-core#3423: Cache conflicts in super property access
Merge pull request chakra-core#3336 from suwc:build/suwc/Issue3064 Accesses to super properties are cached on 'this' object (vs. the "super" object), causing conflict between e.g. super.x and this.x. Similar conflicts cause Issue#3423 for GetProperty cases. Fix by adding 'isSuper' flag to use appropriate object for caching. Fixes chakra-core#3064, Fixes chakra-core#3423
2 parents ecbc5a3 + 7fc3e4c commit 2b9da66

File tree

5 files changed

+63
-58
lines changed

5 files changed

+63
-58
lines changed

lib/Backend/IRBuilder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4651,7 +4651,7 @@ IRBuilder::BuildElementC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
46514651
value2Opnd = this->BuildDstOpnd(value2Slot);
46524652
regOpnd = this->BuildDstOpnd(regSlot);
46534653

4654-
instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, value2Opnd, m_func);
4654+
instr = IR::ProfiledInstr::New(newOpcode, regOpnd, fieldSymOpnd, value2Opnd, m_func);
46554655
this->AddInstr(instr, offset);
46564656
}
46574657
break;
@@ -4672,7 +4672,7 @@ IRBuilder::BuildElementC2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
46724672
regOpnd = this->BuildSrcOpnd(regSlot);
46734673
value2Opnd = this->BuildSrcOpnd(value2Slot);
46744674

4675-
instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, value2Opnd, m_func);
4675+
instr = IR::ProfiledInstr::New(newOpcode, fieldSymOpnd, regOpnd, value2Opnd, m_func);
46764676

46774677
this->AddInstr(instr, offset);
46784678
break;

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7121,15 +7121,16 @@ void EmitAssignment(
71217121
// PutValue(x, "y", rhs)
71227122
Js::PropertyId propertyId = lhs->sxBin.pnode2->sxPid.PropertyIdFromNameNode();
71237123

7124-
uint cacheId = funcInfo->FindOrAddInlineCacheId(lhs->sxBin.pnode1->location, propertyId, false, true);
71257124
EmitSuperMethodBegin(lhs, byteCodeGenerator, funcInfo);
71267125
if (lhs->sxBin.pnode1->nop == knopSuper)
71277126
{
71287127
Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, funcInfo->superRegister, funcInfo);
7128+
uint cacheId = funcInfo->FindOrAddInlineCacheId(tmpReg, propertyId, false, true);
71297129
byteCodeGenerator->Writer()->PatchablePropertyWithThisPtr(Js::OpCode::StSuperFld, rhsLocation, tmpReg, funcInfo->thisPointerRegister, cacheId);
71307130
}
71317131
else
71327132
{
7133+
uint cacheId = funcInfo->FindOrAddInlineCacheId(lhs->sxBin.pnode1->location, propertyId, false, true);
71337134
byteCodeGenerator->Writer()->PatchableProperty(
71347135
ByteCodeGenerator::GetStFldOpCode(funcInfo, false, false, false, false), rhsLocation, lhs->sxBin.pnode1->location, cacheId);
71357136
}
@@ -10911,16 +10912,17 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
1091110912
Js::RegSlot protoLocation = callObjLocation;
1091210913
EmitSuperMethodBegin(pnode, byteCodeGenerator, funcInfo);
1091310914

10914-
uint cacheId = funcInfo->FindOrAddInlineCacheId(callObjLocation, propertyId, false, false);
1091510915
if (pnode->IsCallApplyTargetLoad())
1091610916
{
1091710917
if (pnode->sxBin.pnode1->nop == knopSuper)
1091810918
{
1091910919
Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, funcInfo->superRegister, funcInfo);
10920+
uint cacheId = funcInfo->FindOrAddInlineCacheId(tmpReg, propertyId, false, false);
1092010921
byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFldForCallApplyTarget, pnode->location, tmpReg, cacheId);
1092110922
}
1092210923
else
1092310924
{
10925+
uint cacheId = funcInfo->FindOrAddInlineCacheId(callObjLocation, propertyId, false, false);
1092410926
byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFldForCallApplyTarget, pnode->location, protoLocation, cacheId);
1092510927
}
1092610928
}
@@ -10929,10 +10931,12 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
1092910931
if (pnode->sxBin.pnode1->nop == knopSuper)
1093010932
{
1093110933
Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, funcInfo->superRegister, funcInfo);
10934+
uint cacheId = funcInfo->FindOrAddInlineCacheId(tmpReg, propertyId, false, false);
1093210935
byteCodeGenerator->Writer()->PatchablePropertyWithThisPtr(Js::OpCode::LdSuperFld, pnode->location, tmpReg, funcInfo->thisPointerRegister, cacheId, isConstructorCall);
1093310936
}
1093410937
else
1093510938
{
10939+
uint cacheId = funcInfo->FindOrAddInlineCacheId(callObjLocation, propertyId, false, false);
1093610940
byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, pnode->location, callObjLocation, cacheId, isConstructorCall);
1093710941
}
1093810942
}

lib/Runtime/Language/JavascriptOperators.cpp

Lines changed: 4 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,7 +1739,7 @@ namespace Js
17391739
// Also we might want to throw here instead of checking it again in the caller
17401740
if (value && !requestContext->IsUndeclBlockVar(*value) && !WithScopeObject::Is(object))
17411741
{
1742-
CacheOperators::CachePropertyRead(instance, object, isRoot, propertyId, false, info, requestContext);
1742+
CacheOperators::CachePropertyRead(propertyObject, object, isRoot, propertyId, false, info, requestContext);
17431743
}
17441744
#ifdef TELEMETRY_JSO
17451745
if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
@@ -1779,7 +1779,7 @@ namespace Js
17791779
}
17801780

17811781
PropertyValueInfo::Set(info, requestContext->GetLibrary()->GetMissingPropertyHolder(), 0);
1782-
CacheOperators::CachePropertyRead(instance, requestContext->GetLibrary()->GetMissingPropertyHolder(), isRoot, propertyId, true, info, requestContext);
1782+
CacheOperators::CachePropertyRead(propertyObject, requestContext->GetLibrary()->GetMissingPropertyHolder(), isRoot, propertyId, true, info, requestContext);
17831783
}
17841784
#if defined(TELEMETRY_JSO) || defined(TELEMETRY_AddToCache) // enabled for `TELEMETRY_AddToCache`, because this is the property-not-found codepath where the normal TELEMETRY_AddToCache code wouldn't be executed.
17851785
if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
@@ -7282,7 +7282,7 @@ namespace Js
72827282
PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
72837283
Var value;
72847284
if (CacheOperators::TryGetProperty<true, true, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
7285-
thisInstance, false, object, propertyId, &value, scriptContext, nullptr, &info))
7285+
instance, false, object, propertyId, &value, scriptContext, nullptr, &info))
72867286
{
72877287
return value;
72887288
}
@@ -7938,55 +7938,7 @@ namespace Js
79387938
template <bool IsFromFullJit, class TInlineCache>
79397939
inline void JavascriptOperators::PatchPutValueNoLocalFastPath(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
79407940
{
7941-
ScriptContext *const scriptContext = functionBody->GetScriptContext();
7942-
7943-
if (TaggedNumber::Is(instance))
7944-
{
7945-
JavascriptOperators::SetPropertyOnTaggedNumber(instance,
7946-
nullptr,
7947-
propertyId,
7948-
newValue,
7949-
scriptContext,
7950-
flags);
7951-
return;
7952-
}
7953-
#if ENABLE_COPYONACCESS_ARRAY
7954-
JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
7955-
#endif
7956-
RecyclableObject *object = RecyclableObject::FromVar(instance);
7957-
7958-
PropertyValueInfo info;
7959-
PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
7960-
if (CacheOperators::TrySetProperty<!TInlineCache::IsPolymorphic, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
7961-
object, false, propertyId, newValue, scriptContext, flags, nullptr, &info))
7962-
{
7963-
return;
7964-
}
7965-
7966-
#if DBG_DUMP
7967-
if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
7968-
{
7969-
CacheOperators::TraceCache(inlineCache, _u("PatchPutValueNoLocalFastPath"), propertyId, scriptContext, object);
7970-
}
7971-
#endif
7972-
7973-
ImplicitCallFlags prevImplicitCallFlags = ImplicitCall_None;
7974-
ImplicitCallFlags currImplicitCallFlags = ImplicitCall_None;
7975-
bool hasThisOnlyStatements = functionBody->GetHasOnlyThisStmts();
7976-
if (hasThisOnlyStatements)
7977-
{
7978-
prevImplicitCallFlags = CacheAndClearImplicitBit(scriptContext);
7979-
}
7980-
if (!JavascriptOperators::OP_SetProperty(instance, propertyId, newValue, scriptContext, &info, flags))
7981-
{
7982-
// Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
7983-
scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
7984-
}
7985-
if (hasThisOnlyStatements)
7986-
{
7987-
currImplicitCallFlags = CheckAndUpdateFunctionBodyWithImplicitFlag(functionBody);
7988-
RestoreImplicitFlag(scriptContext, prevImplicitCallFlags, currImplicitCallFlags);
7989-
}
7941+
PatchPutValueWithThisPtrNoLocalFastPath<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
79907942
}
79917943
template void JavascriptOperators::PatchPutValueNoLocalFastPath<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
79927944
template void JavascriptOperators::PatchPutValueNoLocalFastPath<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);

lib/Runtime/Language/ProfilingHelpers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ namespace Js
857857
PropertyValueInfo propertyValueInfo;
858858
PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
859859
if (!CacheOperators::TryGetProperty<true, true, true, !Root && !Method, true, !Root, true, false, true>(
860-
thisObject,
860+
object,
861861
Root,
862862
object,
863863
propertyId,
@@ -1102,7 +1102,7 @@ namespace Js
11021102
PropertyValueInfo propertyValueInfo;
11031103
PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
11041104
if(!CacheOperators::TrySetProperty<true, true, true, true, !Root, true, false, true>(
1105-
thisObject,
1105+
object,
11061106
Root,
11071107
propertyId,
11081108
value,

test/es6/classes_bugfixes.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,55 @@ var tests = [
376376
assert.areEqual('abc', result, "result == 'abc'");
377377
}
378378
},
379+
{
380+
name: "Issue3064: Caching conflict of super property access",
381+
body: function () {
382+
function Base() { }
383+
Base.prototype = {
384+
x: 15,
385+
f() { return this.x; },
386+
};
387+
388+
function Derived() {}
389+
Derived.prototype = {
390+
__proto__: Base.prototype,
391+
x: 27,
392+
f() {
393+
var a = super.x;
394+
var b = eval("super.x");
395+
return this.x;
396+
}
397+
};
398+
399+
assert.areEqual(15, new Base().f());
400+
assert.areEqual(27, new Derived().f());
401+
}
402+
},
403+
{
404+
name: "Issue3423: Repeated calls to class setter triggers assertion",
405+
body: function () {
406+
var result = "";
407+
class B {
408+
set x(v) { result += "Bset;"; this._x = v; }
409+
get x() { result += "Bget;"; return this._x; }
410+
}
411+
412+
class A extends B {
413+
set x(v) { result += "Aset;"; super.x = v + 100; }
414+
get x() { result += "Aget;"; return super.x; }
415+
}
416+
417+
var a = new A();
418+
a.x = 100;
419+
assert.areEqual(200, a.x);
420+
assert.areEqual("Aset;Bset;Aget;Bget;", result);
421+
422+
var a1 = new A();
423+
a1.x = -100;
424+
assert.areEqual(0, a1.x);
425+
assert.areEqual("Aset;Bset;Aget;Bget;Aset;Bset;Aget;Bget;", result);
426+
}
427+
},
379428
{
380429
name: "Issue3217: Reflect.construct permanently corrupts the invoked constructor",
381430
body: function () {

0 commit comments

Comments
 (0)