| // 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 |
| |
| function EnsureDictionaryMode(obj, properties=1500) { |
| for (let i = 0; i < properties; i++) { |
| obj["x" + i] = 0; |
| } |
| assertFalse(%HasFastProperties(obj)); |
| } |
| |
| function EnsureAlmostDictionaryMode(obj) { |
| for (let i = 0; i < 1020; i++) { |
| obj["x" + i] = 0; |
| } |
| } |
| |
| function TestAddingPropertyToDictionaryPrototype() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| |
| function Foo() {} |
| Foo.prototype.func = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| |
| 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 dictionary-mode prototype between o |
| // and Foo. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| // The UNINITIALIZED -> PREMONOMORPHIC transition of StoreIC should |
| // properly invalidate prototype chains. |
| Bar.prototype.func = function() { ++bar_func_called; } |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingPropertyToDictionaryPrototype); |
| TestAddingPropertyToDictionaryPrototype(); |
| |
| // Same as TestAddingPropertyToDictionaryPrototype, but using o["foo"] access |
| // instead of o.foo. |
| function TestAddingPropertyToDictionaryPrototype2() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| let name = "func"; |
| |
| function Foo() {} |
| Foo.prototype[name] = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| |
| 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 dictionary-mode prototype between o |
| // and Foo. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| // The UNINITIALIZED -> PREMONOMORPHIC transition of KeyedStoreIC should |
| // properly invalidate prototype chains. |
| Bar.prototype[name] = function() { ++bar_func_called; } |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingPropertyToDictionaryPrototype2); |
| TestAddingPropertyToDictionaryPrototype2(); |
| |
| function TestAddingPropertyToDictionaryPrototype_DefineProperty() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| |
| function Foo() {} |
| Foo.prototype.func = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| |
| 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 dictionary-mode prototype between o |
| // and Foo. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| // The runtime should properly invalidate prototype chains. |
| Object.defineProperty(Bar.prototype, "func", {value: function() { ++bar_func_called; }}); |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingPropertyToDictionaryPrototype_DefineProperty); |
| TestAddingPropertyToDictionaryPrototype_DefineProperty(); |
| |
| function TestAddingPropertyToDictionaryPrototype_DictionaryAddSlowPath() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| |
| function Foo() {} |
| Foo.prototype.func = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| // The magic number ensures that the next addition to the dictionary will |
| // trigger the slow path. |
| EnsureDictionaryMode(Bar.prototype, 2731); |
| |
| 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 dictionary-mode prototype between o |
| // and Foo. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| // -> slow path for dictionary add |
| Bar.prototype.func = function() { ++bar_func_called; } |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingPropertyToDictionaryPrototype_DictionaryAddSlowPath); |
| TestAddingPropertyToDictionaryPrototype_DictionaryAddSlowPath(); |
| |
| function TestAddingAccessorPropertyToDictionaryPrototype() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| |
| function Foo() {} |
| Foo.prototype.func = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| |
| 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 dictionary-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; }}}); |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingAccessorPropertyToDictionaryPrototype); |
| TestAddingAccessorPropertyToDictionaryPrototype(); |
| |
| function TestRemovingPropertyFromDictionaryPrototype() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| |
| function Foo() {} |
| Foo.prototype.func = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| 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 dictionary-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); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestRemovingPropertyFromDictionaryPrototype); |
| TestRemovingPropertyFromDictionaryPrototype(); |
| |
| // Same as TestRemovingPropertyFromDictionaryPrototype, but using o["foo"] access |
| // instead of o.foo. |
| function TestRemovingPropertyFromDictionaryPrototype2() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| let name = "func"; |
| |
| function Foo() {} |
| Foo.prototype[name] = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| 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 dictionary-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); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestRemovingPropertyFromDictionaryPrototype2); |
| TestRemovingPropertyFromDictionaryPrototype2(); |
| |
| function TestAddingPropertyToDictionaryPrototype_Monomorphic() { |
| function DoMonomorphicStoreToPrototype(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; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| |
| function bar_func() { |
| ++bar_func_called; |
| } |
| DoMonomorphicStoreToPrototype(Bar.prototype, bar_func); |
| DoMonomorphicStoreToPrototype(Bar.prototype, bar_func); |
| DoMonomorphicStoreToPrototype(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 dictionary-mode prototype between o |
| // and Foo. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| DoMonomorphicStoreToPrototype(Bar.prototype, bar_func, false); |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingPropertyToDictionaryPrototype_Monomorphic); |
| TestAddingPropertyToDictionaryPrototype_Monomorphic(); |
| |
| function TestAddingKeyedPropertyToDictionaryPrototype_Monomorphic() { |
| function DoMonomorphicKeyedStoreToPrototype(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; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureDictionaryMode(Bar.prototype); |
| |
| function bar_func() { |
| ++bar_func_called; |
| } |
| DoMonomorphicKeyedStoreToPrototype(Bar.prototype, name, bar_func); |
| DoMonomorphicKeyedStoreToPrototype(Bar.prototype, name, bar_func); |
| DoMonomorphicKeyedStoreToPrototype(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 dictionary-mode prototype between o |
| // and Foo. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| DoMonomorphicKeyedStoreToPrototype(Bar.prototype, name, bar_func, false); |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingKeyedPropertyToDictionaryPrototype_Monomorphic); |
| TestAddingKeyedPropertyToDictionaryPrototype_Monomorphic(); |
| |
| // Like TestAddingPropertyToDictionaryPrototype, except that the prototype isn't |
| // in dictionary mode yet, but turns to dictionary mode after the interesting |
| // property is added. |
| function TestAddingPropertyToAlmostDictionaryPrototype() { |
| let foo_func_called = 0; |
| let bar_func_called = 0; |
| |
| function Foo() {} |
| Foo.prototype.func = function() { ++foo_func_called; } |
| |
| function Bar() {} |
| Bar.prototype = Object.create(Foo.prototype); |
| EnsureAlmostDictionaryMode(Bar.prototype); |
| |
| let o = new Bar(); |
| for (let i = 0; i < 2; ++i) { |
| o.x0; |
| } |
| assertTrue(%HasFastProperties(Bar.prototype)); |
| |
| for (let i = 0; i < 11; ++i) { |
| // First, the property is looked up from Foo. |
| o.func(); |
| |
| // Add the property to Bar which will now turn permanently into dictionary |
| // mode. In the next iteration, it's looked up from Bar. |
| if (i == 9) { |
| Bar.prototype.func = function() { ++bar_func_called; } |
| assertFalse(%HasFastProperties(Bar.prototype)); |
| } |
| } |
| |
| assertEquals(10, foo_func_called); |
| assertEquals(1, bar_func_called); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestAddingPropertyToAlmostDictionaryPrototype); |
| TestAddingPropertyToAlmostDictionaryPrototype(); |
| |
| function TestReconfiguringDataToAccessor() { |
| let setter_called = 0; |
| |
| function Bar() {} |
| EnsureDictionaryMode(Bar.prototype); |
| let name = "prop"; |
| 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); |
| } |
| |
| %EnsureFeedbackVectorForFunction(TestReconfiguringDataToAccessor); |
| TestReconfiguringDataToAccessor(); |