Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidate IsInstInlineCache in Object.setPrototypeOf #6858

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions lib/Runtime/Base/ThreadContext.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
Expand Down Expand Up @@ -1384,6 +1385,14 @@ class ThreadContext sealed :
#endif

public:
template<class Fn>
void MapIsInstInlineCaches(Fn fn) const
{
isInstInlineCacheByFunction.Map([fn](const Js::Var function, Js::IsInstInlineCache* inlineCacheList) {
fn(function, inlineCacheList);
});
}

void InvalidateIsInstInlineCachesForFunction(Js::Var function);
void InvalidateAllIsInstInlineCaches();
bool AreAllIsInstInlineCachesInvalidated() const;
Expand Down
35 changes: 34 additions & 1 deletion lib/Runtime/Library/JavascriptObject.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
Expand Down Expand Up @@ -247,6 +247,39 @@ BOOL JavascriptObject::ChangePrototype(RecyclableObject* object, RecyclableObjec

if (isInvalidationOfInlineCacheNeeded)
{
// Invalidate the "instanceof" cache
ThreadContext* threadContext = scriptContext->GetThreadContext();
threadContext->MapIsInstInlineCaches([threadContext, object](const Js::Var function, Js::IsInstInlineCache* inlineCacheList) {
Assert(inlineCacheList != nullptr);

Js::IsInstInlineCache* curInlineCache;
Js::IsInstInlineCache* nextInlineCache;
for (curInlineCache = inlineCacheList; curInlineCache != nullptr; curInlineCache = nextInlineCache)
{
// Stash away the next cache before we potentially zero out current one
nextInlineCache = curInlineCache->next;

bool clearCurrentCache = curInlineCache->type == object->GetType();
if (!clearCurrentCache) {
// Check if function prototype contains old prototype
JavascriptOperators::MapObjectAndPrototypes<true>(curInlineCache->type->GetPrototype(), [&](RecyclableObject* obj)
{
if (object->GetType() == obj->GetType())
clearCurrentCache = true;
});
}

if (clearCurrentCache)
{
// Fix cache list
// Deletes empty entries
threadContext->UnregisterIsInstInlineCache(curInlineCache, function);
// Actually invalidate current cache
memset(curInlineCache, 0, sizeof(Js::IsInstInlineCache));
}
}
});

bool allProtoCachesInvalidated = false;

JavascriptOperators::MapObjectAndPrototypes<true>(newPrototype, [&](RecyclableObject* obj)
Expand Down
37 changes: 37 additions & 0 deletions test/InlineCaches/isinstanceof.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

// @ts-check
/// <reference path="..\UnitTestFramework\UnitTestFramework.js" />
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

const tests = [{
name: "Clear IsInstInlineCache of 'Object.setPrototypeOf'",
body: function () {
// See https://github.com/chakra-core/ChakraCore/issues/5915

function Component() { }
function Shape() { }

function Box() { }
Box.prototype = Object.create(Shape.prototype);
Box.prototype.constructor = Box;

function checkInstanceOf(a, b) {
return a instanceof b;
}

assert.isFalse(Box.prototype instanceof Component, "Box.prototype instanceof Component");
assert.isFalse(checkInstanceOf(Box.prototype, Component), "checkInstanceOf(Box.prototype, Component)");

Object.setPrototypeOf(Shape.prototype, Component.prototype);

assert.isTrue(Box.prototype instanceof Component, "Box.prototype instanceof Component");
assert.isTrue(checkInstanceOf(Box.prototype, Component), "checkInstanceOf(Box.prototype, Component)");
}
}];

testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
6 changes: 6 additions & 0 deletions test/InlineCaches/rlexe.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,10 @@
<baseline>bug_vso_os_1206083.baseline</baseline>
</default>
</test>
<test>
<default>
<files>isinstanceof.js</files>
<compile-flags>-args summary -endargs</compile-flags>
</default>
</test>
</regress-exe>