Procházet zdrojové kódy

OS#16526223: Keep writes to 'prototype' from going through inline cache so that we get the necessary runtime behavior

Paul Leathers před 7 roky
rodič
revize
9fdb68befa

+ 7 - 0
lib/Runtime/Base/PropertyRecord.cpp

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

+ 2 - 0
lib/Runtime/Base/PropertyRecord.h

@@ -77,6 +77,8 @@ namespace Js
         bool IsBound() const { return isBound; }
         bool IsSymbol() const { return isSymbol; }
 
+        bool ShouldDisableWriteCache() const;
+
         void SetHash(uint hash) const
         {
             this->hash = hash;

+ 2 - 0
lib/Runtime/Library/PropertyRecordUsageCache.h

@@ -30,6 +30,8 @@ namespace Js
         void RegisterCacheHit() { ++this->hitRate; };
         bool ShouldUseCache() const;
 
+        bool ShouldDisableWriteCache() const { return propertyRecord && propertyRecord->ShouldDisableWriteCache(); }
+
         static uint32 GetOffsetOfLdElemInlineCache() { return offsetof(PropertyRecordUsageCache, ldElemInlineCache); }
         static uint32 GetOffsetOfStElemInlineCache() { return offsetof(PropertyRecordUsageCache, stElemInlineCache); }
         static uint32 GetOffsetOfHitRate() { return offsetof(PropertyRecordUsageCache, hitRate); }

+ 4 - 0
lib/Runtime/Types/RecyclableObject.cpp

@@ -39,6 +39,10 @@ namespace Js
         info->prop = prop;
         info->propertyRecordUsageCache = propertyRecordUsageCache;
         SetCacheInfo(info, polymorphicInlineCache, allowResizing);
+        if (propertyRecordUsageCache && propertyRecordUsageCache->ShouldDisableWriteCache())
+        {
+            info->ClearInfoFlag(CacheInfoFlag::enableStoreFieldCacheFlag);
+        }
     }
 
     void PropertyValueInfo::SetCacheInfo(_Out_ PropertyValueInfo* info, _In_ PolymorphicInlineCache *const polymorphicInlineCache, bool allowResizing)

+ 21 - 0
test/Function/prototype_set.js

@@ -0,0 +1,21 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function f() {
+    function inner() { }
+    inner.__proto__ = {b:'b'};  // Put enumerable property into prototype chain
+    new inner();                // Populate ctor cache
+    for (var s in inner) {      // Cache 'prototype' in TypePropertyCache while enumerating
+        inner[s];
+    }
+    inner.prototype = {sox:'red'}; // Set new prototype, using inline cache on 2nd invocation
+    return new inner();            // On 2nd invocation, try to construct using stale ctor cache
+}
+
+f();
+var Boston = f();
+if (Boston.sox === 'red')
+    WScript.Echo('pass');
+

+ 5 - 0
test/Function/rlexe.xml

@@ -226,6 +226,11 @@
       <baseline>prototype.baseline</baseline>
     </default>
   </test>
+  <test>
+    <default>
+      <files>prototype_set.js</files>
+    </default>
+  </test>
   <test>
     <default>
       <files>TApply1.js</files>