Commit 2b08723c authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[ic] Prototype-only dictionaries, step 1.

- When a global object changes, invalidate its validity cell.
- The global object prototypes don't need to be gathered into an array in InitPrototypeChecks.

Bug: v8:7159
Change-Id: I3621c914d08b83e49e8a391800a92eb53ba19feb
Reviewed-on: https://chromium-review.googlesource.com/808588Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49901}
parent 75002114
......@@ -48,31 +48,6 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
}
checks_count++;
}
// Create/count entries for each global or dictionary prototype appeared in
// the prototype chain contains from receiver till holder.
PrototypeIterator::WhereToEnd end = name->IsPrivate()
? PrototypeIterator::END_AT_NON_HIDDEN
: PrototypeIterator::END_AT_NULL;
for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
iter.Advance()) {
Handle<JSReceiver> current =
PrototypeIterator::GetCurrent<JSReceiver>(iter);
if (holder.is_identical_to(current)) break;
Handle<Map> current_map(current->map(), isolate);
if (current_map->IsJSGlobalObjectMap()) {
if (fill_array) {
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current);
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
global, name, PropertyCellType::kInvalidated);
DCHECK(cell->value()->IsTheHole(isolate));
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
array->set(first_index + checks_count, *weak_cell);
}
checks_count++;
}
}
return checks_count;
}
......
......@@ -479,6 +479,7 @@ void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
DCHECK(receiver.is_identical_to(GetStoreTarget()));
holder_ = receiver;
if (receiver->IsJSGlobalObject()) {
JSObject::InvalidatePrototypeChains(receiver->map());
state_ = DATA;
return;
}
......
......@@ -2163,11 +2163,13 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object,
uint32_t hash = name->Hash();
if (object->IsJSGlobalObject()) {
Handle<JSGlobalObject> global_obj(JSGlobalObject::cast(*object));
Handle<JSGlobalObject> global_obj = Handle<JSGlobalObject>::cast(object);
Handle<GlobalDictionary> dictionary(global_obj->global_dictionary());
int entry = dictionary->FindEntry(isolate, name, hash);
if (entry == GlobalDictionary::kNotFound) {
DCHECK_IMPLIES(global_obj->map()->is_prototype_map(),
Map::IsPrototypeChainInvalidated(global_obj->map()));
auto cell = isolate->factory()->NewPropertyCell(name);
cell->set_value(*value);
auto cell_type = value->IsUndefined(isolate)
......
// Copyright 2017 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.
// Flags: --allow-natives-syntax
assertEquals(this.__proto__, Object.prototype);
function TestAddingPropertyToGlobalPrototype() {
let foo_func_called = 0;
let bar_func_called = 0;
function Foo() {}
Foo.prototype.func = function() { ++foo_func_called; }
delete this.func;
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Foo.
o.func();
// Add the property to Bar which is a Global-mode prototype between o
// and Foo. In the next iteration, it's looked up from Bar.
if (i == 9) {
Bar.prototype.func = function() { ++bar_func_called; }
}
}
assertEquals(10, foo_func_called);
assertEquals(1, bar_func_called);
}
TestAddingPropertyToGlobalPrototype();
// Same as TestAddingPropertyToGlobalPrototype, but using o["foo"] access
// instead of o.foo.
function TestAddingPropertyToGlobalPrototype2() {
let foo_func_called = 0;
let bar_func_called = 0;
let name = "func";
function Foo() {}
Foo.prototype[name] = function() { ++foo_func_called; }
delete this[name];
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Foo.
o[name]();
// Add the property to Bar which is a Global-mode prototype between o
// and Foo. In the next iteration, it's looked up from Bar.
if (i == 9) {
Bar.prototype[name] = function() { ++bar_func_called; }
}
}
assertEquals(10, foo_func_called);
assertEquals(1, bar_func_called);
}
TestAddingPropertyToGlobalPrototype2();
function TestAddingPropertyToGlobalPrototype_DefineProperty() {
let foo_func_called = 0;
let bar_func_called = 0;
function Foo() {}
Foo.prototype.func = function() { ++foo_func_called; }
delete this.func;
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Foo.
o.func();
// Add the property to Bar which is a Global-mode prototype between o
// and Foo. In the next iteration, it's looked up from Bar.
if (i == 9) {
Object.defineProperty(Bar.prototype, "func",
{
value: function() { ++bar_func_called; },
configurable:true
});
}
}
assertEquals(10, foo_func_called);
assertEquals(1, bar_func_called);
}
TestAddingPropertyToGlobalPrototype_DefineProperty();
function TestAddingAccessorPropertyToGlobalPrototype() {
let foo_func_called = 0;
let bar_func_called = 0;
function Foo() {}
Foo.prototype.func = function() { ++foo_func_called; }
delete this.func;
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Foo.
o.func();
// Add the property to Bar which is a Global-mode prototype between o
// and Foo. In the next iteration, it's looked up from Bar.
if (i == 9) {
Object.defineProperty(Bar.prototype, "func",
{
get: function() { return function() { ++bar_func_called; }},
configurable: true
});
}
}
assertEquals(10, foo_func_called);
assertEquals(1, bar_func_called);
}
TestAddingAccessorPropertyToGlobalPrototype();
function TestRemovingPropertyFromGlobalPrototype() {
let foo_func_called = 0;
let bar_func_called = 0;
function Foo() {}
Foo.prototype.func = function() { ++foo_func_called; }
delete this.func;
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
Bar.prototype.func = function() { ++bar_func_called; }
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Bar.
o.func();
// Remove the property from Bar which is a Global-mode prototype between
// o and Foo. In the next iteration, it's looked up from Foo.
if (i == 9) {
delete Bar.prototype.func;
}
}
assertEquals(1, foo_func_called);
assertEquals(10, bar_func_called);
}
TestRemovingPropertyFromGlobalPrototype();
// Same as TestRemovingPropertyFromGlobalPrototype, but using o["foo"] access
// instead of o.foo.
function TestRemovingPropertyFromGlobalPrototype2() {
let foo_func_called = 0;
let bar_func_called = 0;
let name = "func";
function Foo() {}
Foo.prototype[name] = function() { ++foo_func_called; }
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
Bar.prototype[name] = function() { ++bar_func_called; }
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Bar.
o[name]();
// Remove the property from Bar which is a Global-mode prototype between
// o and Foo. In the next iteration, it's looked up from Foo.
if (i == 9) {
delete Bar.prototype[name];
}
}
assertEquals(1, foo_func_called);
assertEquals(10, bar_func_called);
}
TestRemovingPropertyFromGlobalPrototype2();
function TestAddingPropertyToGlobalPrototype_MonomorphicDot() {
function DoMonomorphicStoreToPrototypeDot(p, f, do_delete=true) {
p.func = f;
if (do_delete) {
delete p.func;
}
}
let foo_func_called = 0;
let bar_func_called = 0;
function Foo() {}
Foo.prototype.func = function() { ++foo_func_called; }
delete this.func;
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
function bar_func() {
++bar_func_called;
}
DoMonomorphicStoreToPrototypeDot(Bar.prototype, bar_func);
DoMonomorphicStoreToPrototypeDot(Bar.prototype, bar_func);
DoMonomorphicStoreToPrototypeDot(Bar.prototype, bar_func);
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Foo.
o.func();
// Add the property to Bar which is a Global-mode prototype between o
// and Foo. In the next iteration, it's looked up from Bar.
if (i == 9) {
DoMonomorphicStoreToPrototypeDot(Bar.prototype, bar_func, false);
}
}
assertEquals(10, foo_func_called);
assertEquals(1, bar_func_called);
}
TestAddingPropertyToGlobalPrototype_MonomorphicDot();
function TestAddingPropertyToGlobalPrototype_MonomorphicBrackets() {
function DoMonomorphicStoreToPrototypeBrackets(p, name, f, do_delete=true) {
p[name] = f;
if (do_delete) {
delete p[name];
}
}
let foo_func_called = 0;
let bar_func_called = 0;
let name = "func";
function Foo() {}
Foo.prototype[name] = function() { ++foo_func_called; }
delete this[name];
this.__proto__ = Foo.prototype;
function Bar() {}
Bar.prototype = this;
function bar_func() {
++bar_func_called;
}
DoMonomorphicStoreToPrototypeBrackets(Bar.prototype, name, bar_func);
DoMonomorphicStoreToPrototypeBrackets(Bar.prototype, name, bar_func);
DoMonomorphicStoreToPrototypeBrackets(Bar.prototype, name, bar_func);
let o = new Bar();
for (let i = 0; i < 11; ++i) {
// First, the property is looked up from Foo.
o.func();
// Add the property to Bar which is a Global-mode prototype between o
// and Foo. In the next iteration, it's looked up from Bar.
if (i == 9) {
DoMonomorphicStoreToPrototypeBrackets(Bar.prototype, name, bar_func, false);
}
}
assertEquals(10, foo_func_called);
assertEquals(1, bar_func_called);
}
TestAddingPropertyToGlobalPrototype_MonomorphicBrackets();
function TestReconfiguringDataToAccessor() {
let setter_called = 0;
let name = "prop";
delete this[name];
this.__proto__ = Object.prototype;
function Bar() {}
Bar.prototype = this;
Object.defineProperty(Bar.prototype, name, {value: 1000, writable: true, configurable: true});
for (let i = 0; i < 11; ++i) {
let obj1 = new Bar();
if (i < 10) {
assertEquals(1000, obj1.prop);
} else {
assertEquals(3000, obj1.prop);
}
// Add the property into the object.
obj1.prop = 2000;
if (i < 10) {
assertEquals(2000, obj1.prop);
} else {
assertEquals(3000, obj1.prop);
}
// Make "prop" an accessor property in the prototype.
if (i == 9) {
Object.defineProperty(Bar.prototype, name,
{get: () => 3000,
set: function(val) { ++setter_called; }});
}
}
assertEquals(1, setter_called);
}
TestReconfiguringDataToAccessor();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment