[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) {
|
||||
DCHECK(constructor->has_initial_map());
|
||||
Handle<Map> map(constructor->initial_map(), isolate());
|
||||
Handle<Map> old_map(object->map(), isolate());
|
||||
|
||||
// The proxy's hash should be retained across reinitialization.
|
||||
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
|
||||
// objects allocated using the constructor.
|
||||
DCHECK(map->instance_size() == object->map()->instance_size());
|
||||
DCHECK(map->instance_type() == object->map()->instance_type());
|
||||
DCHECK(map->instance_size() == old_map->instance_size());
|
||||
DCHECK(map->instance_type() == old_map->instance_type());
|
||||
|
||||
// Allocate the backing storage for the properties.
|
||||
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,
|
||||
Handle<Map> new_map,
|
||||
Isolate* isolate) {
|
||||
// static
|
||||
void JSObject::UpdatePrototypeUserRegistration(Handle<Map> old_map,
|
||||
Handle<Map> new_map,
|
||||
Isolate* isolate) {
|
||||
if (!FLAG_track_prototype_users) return;
|
||||
if (!old_map->is_prototype_map()) return;
|
||||
DCHECK(new_map->is_prototype_map());
|
||||
@ -12461,7 +12462,6 @@ static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) {
|
||||
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
|
||||
PrototypeOptimizationMode mode) {
|
||||
if (object->IsJSGlobalObject()) return;
|
||||
if (object->IsJSGlobalProxy()) return;
|
||||
if (mode == FAST_PROTOTYPE && PrototypeBenefitsFromNormalization(object)) {
|
||||
// First normalize to ensure all JSFunctions are DATA_CONSTANT.
|
||||
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
|
||||
@ -12519,7 +12519,6 @@ void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
|
||||
break;
|
||||
}
|
||||
Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
|
||||
if (maybe_proto->IsJSGlobalProxy()) continue;
|
||||
// Proxies on the prototype chain are not supported. They make it
|
||||
// impossible to make any assumptions about the prototype chain anyway.
|
||||
if (maybe_proto->IsJSProxy()) return;
|
||||
@ -12554,17 +12553,18 @@ bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
|
||||
DCHECK(user->is_prototype_map());
|
||||
// If it doesn't have a PrototypeInfo, it was never registered.
|
||||
if (!user->prototype_info()->IsPrototypeInfo()) return false;
|
||||
// If it doesn't have a prototype, it can't be registered.
|
||||
if (!user->prototype()->IsJSObject()) return false;
|
||||
// If it had no prototype before, see if it had users that might expect
|
||||
// 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<PrototypeInfo> user_info =
|
||||
Map::GetOrCreatePrototypeInfo(user, isolate);
|
||||
int slot = user_info->registry_slot();
|
||||
if (slot == PrototypeInfo::UNREGISTERED) return false;
|
||||
if (prototype->IsJSGlobalProxy()) {
|
||||
PrototypeIterator iter(isolate, prototype);
|
||||
prototype = PrototypeIterator::GetCurrent<JSObject>(iter);
|
||||
}
|
||||
DCHECK(prototype->map()->is_prototype_map());
|
||||
Object* maybe_proto_info = prototype->map()->prototype_info();
|
||||
// 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) {
|
||||
if (!FLAG_eliminate_prototype_chain_checks) return;
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (map->IsJSGlobalProxyMap()) {
|
||||
PrototypeIterator iter(map);
|
||||
map = iter.GetCurrent<JSObject>()->map();
|
||||
}
|
||||
InvalidatePrototypeChainsInternal(map);
|
||||
}
|
||||
|
||||
@ -12653,10 +12649,6 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
|
||||
Handle<Object> maybe_prototype(map->prototype(), isolate);
|
||||
if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null();
|
||||
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
|
||||
// will be invalidated when necessary.
|
||||
JSObject::LazyRegisterPrototypeUser(handle(prototype->map(), isolate),
|
||||
|
@ -2137,6 +2137,9 @@ class JSObject: public JSReceiver {
|
||||
PrototypeOptimizationMode mode);
|
||||
static void ReoptimizeIfPrototype(Handle<JSObject> object);
|
||||
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 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