Skip to content

Commit 210bbe3

Browse files
committed
[MERGE #5399 @pleath] OS#16526223: Keep writes to 'prototype' from going through inline cache so that we get the necessary runtime behavior
Merge pull request #5399 from pleath:16526223 Writes to the prototype property of JavascriptFunction must go through the runtime so that the constructor cache, etc., can be invalidated. Enumeration provided a way to record a write to 'prototype' in an inline cache so that the runtime could be skipped. One solution would be to make 'prototype' an accessor property on JavascriptFunction, but this would probably grow function objects and slow down loads of the property. Instead, let PropertyRecord indicate that we should disable caching of writes to the property and disable store field cache in PropertyValueInfo.
2 parents 33c6ecb + 9fdb68b commit 210bbe3

File tree

6 files changed

+41
-0
lines changed

6 files changed

+41
-0
lines changed

lib/Runtime/Base/PropertyRecord.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ namespace Js
6868
}
6969
}
7070

71+
bool PropertyRecord::ShouldDisableWriteCache() const
72+
{
73+
// We can't cache stores to the 'prototype' property of function objects. We must go through the runtime and clear the constructor cache.
74+
// We could consider treating 'prototype' as an accessor on JavascriptFunction and friends, though this seems like it will grow the object.
75+
return pid == PropertyIds::prototype;
76+
}
77+
7178
#ifdef DEBUG
7279
// This is only used to assert that integer property names are not passed into
7380
// the GetSetter, GetProperty, SetProperty etc methods that take JavascriptString

lib/Runtime/Base/PropertyRecord.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ namespace Js
7777
bool IsBound() const { return isBound; }
7878
bool IsSymbol() const { return isSymbol; }
7979

80+
bool ShouldDisableWriteCache() const;
81+
8082
void SetHash(uint hash) const
8183
{
8284
this->hash = hash;

lib/Runtime/Library/PropertyRecordUsageCache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ namespace Js
3030
void RegisterCacheHit() { ++this->hitRate; };
3131
bool ShouldUseCache() const;
3232

33+
bool ShouldDisableWriteCache() const { return propertyRecord && propertyRecord->ShouldDisableWriteCache(); }
34+
3335
static uint32 GetOffsetOfLdElemInlineCache() { return offsetof(PropertyRecordUsageCache, ldElemInlineCache); }
3436
static uint32 GetOffsetOfStElemInlineCache() { return offsetof(PropertyRecordUsageCache, stElemInlineCache); }
3537
static uint32 GetOffsetOfHitRate() { return offsetof(PropertyRecordUsageCache, hitRate); }

lib/Runtime/Types/RecyclableObject.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ namespace Js
3939
info->prop = prop;
4040
info->propertyRecordUsageCache = propertyRecordUsageCache;
4141
SetCacheInfo(info, polymorphicInlineCache, allowResizing);
42+
if (propertyRecordUsageCache && propertyRecordUsageCache->ShouldDisableWriteCache())
43+
{
44+
info->ClearInfoFlag(CacheInfoFlag::enableStoreFieldCacheFlag);
45+
}
4246
}
4347

4448
void PropertyValueInfo::SetCacheInfo(_Out_ PropertyValueInfo* info, _In_ PolymorphicInlineCache *const polymorphicInlineCache, bool allowResizing)

test/Function/prototype_set.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
function f() {
7+
function inner() { }
8+
inner.__proto__ = {b:'b'}; // Put enumerable property into prototype chain
9+
new inner(); // Populate ctor cache
10+
for (var s in inner) { // Cache 'prototype' in TypePropertyCache while enumerating
11+
inner[s];
12+
}
13+
inner.prototype = {sox:'red'}; // Set new prototype, using inline cache on 2nd invocation
14+
return new inner(); // On 2nd invocation, try to construct using stale ctor cache
15+
}
16+
17+
f();
18+
var Boston = f();
19+
if (Boston.sox === 'red')
20+
WScript.Echo('pass');
21+

test/Function/rlexe.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@
226226
<baseline>prototype.baseline</baseline>
227227
</default>
228228
</test>
229+
<test>
230+
<default>
231+
<files>prototype_set.js</files>
232+
</default>
233+
</test>
229234
<test>
230235
<default>
231236
<files>TApply1.js</files>

0 commit comments

Comments
 (0)