[prototype user tracking] Don't skip JSGlobalProxies
For a prototype chain foo -> global_proxy -> global_object, we used to register a dependency from foo -> global_object. This is incorrect when the global_proxy/global_object pairing is modified, e.g. when navigating in iframes. With this patch, we properly register foo -> global_proxy and global_proxy -> global_object dependencies. Additionally, when a prototype's prototype changes from null to something else, this new usage relation must be registered if there are other users further down on the prototype chain that might expect a complete chain of registrations to exist (which was the case before, and must be preserved). BUG=chromium:571517 LOG=n R=verwaest@chromium.org Review URL: https://codereview.chromium.org/1559323002 Cr-Commit-Position: refs/heads/master@{#33119}
This commit is contained in:
parent
bdc2746d28
commit
b4583c0444
@ -2034,14 +2034,22 @@ void Factory::ReinitializeJSGlobalProxy(Handle<JSGlobalProxy> object,
|
|||||||
Handle<JSFunction> constructor) {
|
Handle<JSFunction> constructor) {
|
||||||
DCHECK(constructor->has_initial_map());
|
DCHECK(constructor->has_initial_map());
|
||||||
Handle<Map> map(constructor->initial_map(), isolate());
|
Handle<Map> map(constructor->initial_map(), isolate());
|
||||||
|
Handle<Map> old_map(object->map(), isolate());
|
||||||
|
|
||||||
// The proxy's hash should be retained across reinitialization.
|
// The proxy's hash should be retained across reinitialization.
|
||||||
Handle<Object> hash(object->hash(), isolate());
|
Handle<Object> hash(object->hash(), isolate());
|
||||||
|
|
||||||
|
JSObject::InvalidatePrototypeChains(*old_map);
|
||||||
|
if (old_map->is_prototype_map()) {
|
||||||
|
map = Map::Copy(map, "CopyAsPrototypeForJSGlobalProxy");
|
||||||
|
map->set_is_prototype_map(true);
|
||||||
|
}
|
||||||
|
JSObject::UpdatePrototypeUserRegistration(old_map, map, isolate());
|
||||||
|
|
||||||
// Check that the already allocated object has the same size and type as
|
// Check that the already allocated object has the same size and type as
|
||||||
// objects allocated using the constructor.
|
// objects allocated using the constructor.
|
||||||
DCHECK(map->instance_size() == object->map()->instance_size());
|
DCHECK(map->instance_size() == old_map->instance_size());
|
||||||
DCHECK(map->instance_type() == object->map()->instance_type());
|
DCHECK(map->instance_type() == old_map->instance_type());
|
||||||
|
|
||||||
// Allocate the backing storage for the properties.
|
// Allocate the backing storage for the properties.
|
||||||
Handle<FixedArray> properties = empty_fixed_array();
|
Handle<FixedArray> properties = empty_fixed_array();
|
||||||
|
@ -2706,9 +2706,10 @@ bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void UpdatePrototypeUserRegistration(Handle<Map> old_map,
|
// static
|
||||||
Handle<Map> new_map,
|
void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map,
|
||||||
Isolate* isolate) {
|
Handle<Map> new_map,
|
||||||
|
Isolate* isolate) {
|
||||||
if (!FLAG_track_prototype_users) return;
|
if (!FLAG_track_prototype_users) return;
|
||||||
if (!old_map->is_prototype_map()) return;
|
if (!old_map->is_prototype_map()) return;
|
||||||
DCHECK(new_map->is_prototype_map());
|
DCHECK(new_map->is_prototype_map());
|
||||||
@ -12461,7 +12462,6 @@ static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) {
|
|||||||
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
|
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
|
||||||
PrototypeOptimizationMode mode) {
|
PrototypeOptimizationMode mode) {
|
||||||
if (object->IsJSGlobalObject()) return;
|
if (object->IsJSGlobalObject()) return;
|
||||||
if (object->IsJSGlobalProxy()) return;
|
|
||||||
if (mode == FAST_PROTOTYPE && PrototypeBenefitsFromNormalization(object)) {
|
if (mode == FAST_PROTOTYPE && PrototypeBenefitsFromNormalization(object)) {
|
||||||
// First normalize to ensure all JSFunctions are DATA_CONSTANT.
|
// First normalize to ensure all JSFunctions are DATA_CONSTANT.
|
||||||
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
|
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
|
||||||
@ -12519,7 +12519,6 @@ void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
|
Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
|
||||||
if (maybe_proto->IsJSGlobalProxy()) continue;
|
|
||||||
// Proxies on the prototype chain are not supported. They make it
|
// Proxies on the prototype chain are not supported. They make it
|
||||||
// impossible to make any assumptions about the prototype chain anyway.
|
// impossible to make any assumptions about the prototype chain anyway.
|
||||||
if (maybe_proto->IsJSProxy()) return;
|
if (maybe_proto->IsJSProxy()) return;
|
||||||
@ -12554,17 +12553,18 @@ bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
|
|||||||
DCHECK(user->is_prototype_map());
|
DCHECK(user->is_prototype_map());
|
||||||
// If it doesn't have a PrototypeInfo, it was never registered.
|
// If it doesn't have a PrototypeInfo, it was never registered.
|
||||||
if (!user->prototype_info()->IsPrototypeInfo()) return false;
|
if (!user->prototype_info()->IsPrototypeInfo()) return false;
|
||||||
// If it doesn't have a prototype, it can't be registered.
|
// If it had no prototype before, see if it had users that might expect
|
||||||
if (!user->prototype()->IsJSObject()) return false;
|
// registration.
|
||||||
|
if (!user->prototype()->IsJSObject()) {
|
||||||
|
Object* users =
|
||||||
|
PrototypeInfo::cast(user->prototype_info())->prototype_users();
|
||||||
|
return users->IsWeakFixedArray();
|
||||||
|
}
|
||||||
Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate);
|
Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate);
|
||||||
Handle<PrototypeInfo> user_info =
|
Handle<PrototypeInfo> user_info =
|
||||||
Map::GetOrCreatePrototypeInfo(user, isolate);
|
Map::GetOrCreatePrototypeInfo(user, isolate);
|
||||||
int slot = user_info->registry_slot();
|
int slot = user_info->registry_slot();
|
||||||
if (slot == PrototypeInfo::UNREGISTERED) return false;
|
if (slot == PrototypeInfo::UNREGISTERED) return false;
|
||||||
if (prototype->IsJSGlobalProxy()) {
|
|
||||||
PrototypeIterator iter(isolate, prototype);
|
|
||||||
prototype = PrototypeIterator::GetCurrent<JSObject>(iter);
|
|
||||||
}
|
|
||||||
DCHECK(prototype->map()->is_prototype_map());
|
DCHECK(prototype->map()->is_prototype_map());
|
||||||
Object* maybe_proto_info = prototype->map()->prototype_info();
|
Object* maybe_proto_info = prototype->map()->prototype_info();
|
||||||
// User knows its registry slot, prototype info and user registry must exist.
|
// User knows its registry slot, prototype info and user registry must exist.
|
||||||
@ -12613,10 +12613,6 @@ static void InvalidatePrototypeChainsInternal(Map* map) {
|
|||||||
void JSObject::InvalidatePrototypeChains(Map* map) {
|
void JSObject::InvalidatePrototypeChains(Map* map) {
|
||||||
if (!FLAG_eliminate_prototype_chain_checks) return;
|
if (!FLAG_eliminate_prototype_chain_checks) return;
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
if (map->IsJSGlobalProxyMap()) {
|
|
||||||
PrototypeIterator iter(map);
|
|
||||||
map = iter.GetCurrent<JSObject>()->map();
|
|
||||||
}
|
|
||||||
InvalidatePrototypeChainsInternal(map);
|
InvalidatePrototypeChainsInternal(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -12653,10 +12649,6 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
|
|||||||
Handle<Object> maybe_prototype(map->prototype(), isolate);
|
Handle<Object> maybe_prototype(map->prototype(), isolate);
|
||||||
if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null();
|
if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null();
|
||||||
Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype);
|
Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype);
|
||||||
if (prototype->IsJSGlobalProxy()) {
|
|
||||||
PrototypeIterator iter(isolate, prototype);
|
|
||||||
prototype = PrototypeIterator::GetCurrent<JSObject>(iter);
|
|
||||||
}
|
|
||||||
// Ensure the prototype is registered with its own prototypes so its cell
|
// Ensure the prototype is registered with its own prototypes so its cell
|
||||||
// will be invalidated when necessary.
|
// will be invalidated when necessary.
|
||||||
JSObject::LazyRegisterPrototypeUser(handle(prototype->map(), isolate),
|
JSObject::LazyRegisterPrototypeUser(handle(prototype->map(), isolate),
|
||||||
|
@ -2137,6 +2137,9 @@ class JSObject: public JSReceiver {
|
|||||||
PrototypeOptimizationMode mode);
|
PrototypeOptimizationMode mode);
|
||||||
static void ReoptimizeIfPrototype(Handle<JSObject> object);
|
static void ReoptimizeIfPrototype(Handle<JSObject> object);
|
||||||
static void LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate);
|
static void LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate);
|
||||||
|
static void UpdatePrototypeUserRegistration(Handle<Map> old_map,
|
||||||
|
Handle<Map> new_map,
|
||||||
|
Isolate* isolate);
|
||||||
static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate);
|
static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate);
|
||||||
static void InvalidatePrototypeChains(Map* map);
|
static void InvalidatePrototypeChains(Map* map);
|
||||||
|
|
||||||
|
36
test/mjsunit/regress/regress-crbug-571517.js
Normal file
36
test/mjsunit/regress/regress-crbug-571517.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
function Receiver() { this.receiver = "receiver"; }
|
||||||
|
function Proto() { this.proto = "proto"; }
|
||||||
|
|
||||||
|
function f(a) {
|
||||||
|
return a.foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rec = new Receiver();
|
||||||
|
|
||||||
|
var proto = rec.__proto__.__proto__;
|
||||||
|
|
||||||
|
// Initialize prototype chain dependent IC (nonexistent load).
|
||||||
|
assertEquals(undefined, f(rec));
|
||||||
|
assertEquals(undefined, f(rec));
|
||||||
|
|
||||||
|
// Add a new prototype to the end of the chain.
|
||||||
|
var p2 = new Proto();
|
||||||
|
p2.__proto__ = null;
|
||||||
|
proto.__proto__ = p2;
|
||||||
|
|
||||||
|
// Update the IC.
|
||||||
|
assertEquals(undefined, f(rec));
|
||||||
|
|
||||||
|
// Now modify the most recently added prototype by adding a property...
|
||||||
|
p2.foo = "bar";
|
||||||
|
assertEquals("bar", f(rec));
|
||||||
|
|
||||||
|
// ...and removing it again. Due to missing prototype user registrations,
|
||||||
|
// this fails to invalidate the IC.
|
||||||
|
delete p2.foo;
|
||||||
|
p2.secret = "GAME OVER";
|
||||||
|
assertEquals(undefined, f(rec));
|
Loading…
Reference in New Issue
Block a user