[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:
jkummerow 2016-01-05 08:15:00 -08:00 committed by Commit bot
parent bdc2746d28
commit b4583c0444
4 changed files with 60 additions and 21 deletions

View File

@ -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();

View File

@ -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),

View File

@ -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);

View 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));