| // Copyright 2016 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 TestMeta() { |
| assertEquals(1, Object.entries.length); |
| assertEquals(Function.prototype, Object.getPrototypeOf(Object.entries)); |
| assertEquals("entries", Object.entries.name); |
| |
| var descriptor = Object.getOwnPropertyDescriptor(Object, "entries"); |
| assertTrue(descriptor.writable); |
| assertFalse(descriptor.enumerable); |
| assertTrue(descriptor.configurable); |
| |
| assertThrows(() => new Object.entries({}), TypeError); |
| } |
| TestMeta(); |
| |
| |
| function TestBasic(withWarmup) { |
| var x = 16; |
| var O = { |
| d: 1, |
| c: 3, |
| [Symbol.iterator]: void 0, |
| 0: 123, |
| 1000: 456, |
| [x * x]: "ducks", |
| [`0x${(x * x).toString(16)}`]: "quack" |
| }; |
| O.a = 2; |
| O.b = 4; |
| Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); |
| if (withWarmup) { |
| for (const key in O) {} |
| } |
| O.c = 6; |
| const resultEntries = [ |
| ["0", 123], |
| ["256", "ducks"], |
| ["1000", 456], |
| ["d", 1], |
| ["c", 6], |
| ["0x100", "quack"], |
| ["a", 2], |
| ["b", 4] |
| ]; |
| assertEquals(resultEntries, Object.entries(O)); |
| assertEquals(resultEntries, Object.entries(O)); |
| assertEquals(Object.entries(O), Object.keys(O).map(key => [key, O[key]])); |
| |
| assertTrue(Array.isArray(Object.entries({}))); |
| assertEquals(0, Object.entries({}).length); |
| } |
| TestBasic(); |
| TestBasic(true); |
| |
| |
| function TestToObject() { |
| assertThrows(function() { Object.entries(); }, TypeError); |
| assertThrows(function() { Object.entries(null); }, TypeError); |
| assertThrows(function() { Object.entries(void 0); }, TypeError); |
| } |
| TestToObject(); |
| |
| |
| function TestOrder(withWarmup) { |
| var O = { |
| a: 1, |
| [Symbol.iterator]: null |
| }; |
| O[456] = 123; |
| Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); |
| var priv = %CreatePrivateSymbol("Secret"); |
| O[priv] = 56; |
| |
| var log = []; |
| var P = new Proxy(O, { |
| ownKeys(target) { |
| log.push("[[OwnPropertyKeys]]"); |
| return Reflect.ownKeys(target); |
| }, |
| get(target, name) { |
| log.push(`[[Get]](${JSON.stringify(name)})`); |
| return Reflect.get(target, name); |
| }, |
| getOwnPropertyDescriptor(target, name) { |
| log.push(`[[GetOwnProperty]](${JSON.stringify(name)})`); |
| return Reflect.getOwnPropertyDescriptor(target, name); |
| }, |
| set(target, name, value) { |
| assertUnreachable(); |
| } |
| }); |
| |
| if (withWarmup) { |
| for (const key in P) {} |
| } |
| log = []; |
| |
| assertEquals([["456", 123], ["a", 1]], Object.entries(P)); |
| assertEquals([ |
| "[[OwnPropertyKeys]]", |
| "[[GetOwnProperty]](\"456\")", |
| "[[Get]](\"456\")", |
| "[[GetOwnProperty]](\"a\")", |
| "[[Get]](\"a\")", |
| "[[GetOwnProperty]](\"HIDDEN\")" |
| ], log); |
| } |
| TestOrder(); |
| TestOrder(true); |
| |
| |
| function TestOrderWithDuplicates(withWarmup) { |
| var O = { |
| a: 1, |
| [Symbol.iterator]: null |
| }; |
| O[456] = 123; |
| Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); |
| var priv = %CreatePrivateSymbol("Secret"); |
| O[priv] = 56; |
| |
| var log = []; |
| var P = new Proxy(O, { |
| ownKeys(target) { |
| log.push("[[OwnPropertyKeys]]"); |
| return ["a", Symbol.iterator, "a", "456", "HIDDEN", "HIDDEN", "456"]; |
| }, |
| get(target, name) { |
| log.push(`[[Get]](${JSON.stringify(name)})`); |
| return Reflect.get(target, name); |
| }, |
| getOwnPropertyDescriptor(target, name) { |
| log.push(`[[GetOwnProperty]](${JSON.stringify(name)})`); |
| return Reflect.getOwnPropertyDescriptor(target, name); |
| }, |
| set(target, name, value) { |
| assertUnreachable(); |
| } |
| }); |
| |
| if (withWarmup) { |
| for (const key in O) {}; |
| try { for (const key in P) {} } catch {}; |
| } |
| |
| assertThrows(() => Object.entries(P), TypeError); |
| } |
| TestOrderWithDuplicates(); |
| TestOrderWithDuplicates(true); |
| |
| function TestDescriptorProperty() { |
| function f() {}; |
| const o = {}; |
| o.a = f; |
| |
| for (const key in o) {}; |
| const entries = Object.entries(o); |
| assertEquals([['a', f]], entries); |
| } |
| TestDescriptorProperty(); |
| |
| function TestPropertyFilter(withWarmup) { |
| var object = { prop3: 30 }; |
| object[2] = 40; |
| object["prop4"] = 50; |
| Object.defineProperty(object, "prop5", { value: 60, enumerable: true }); |
| Object.defineProperty(object, "prop6", { value: 70, enumerable: false }); |
| Object.defineProperty(object, "prop7", { |
| enumerable: true, get() { return 80; }}); |
| var sym = Symbol("prop8"); |
| object[sym] = 90; |
| |
| if (withWarmup) { |
| for (const key in object) {} |
| } |
| |
| values = Object.entries(object); |
| assertEquals(5, values.length); |
| assertEquals([ |
| [ "2", 40 ], |
| [ "prop3", 30 ], |
| [ "prop4", 50 ], |
| [ "prop5", 60 ], |
| [ "prop7", 80 ] |
| ], values); |
| } |
| TestPropertyFilter(); |
| TestPropertyFilter(true); |
| |
| function TestPropertyFilter2(withWarmup) { |
| var object = { }; |
| Object.defineProperty(object, "prop1", { value: 10 }); |
| Object.defineProperty(object, "prop2", { value: 20 }); |
| object.prop3 = 30; |
| |
| if (withWarmup) { |
| for (const key in object) {} |
| } |
| |
| values = Object.entries(object); |
| assertEquals(1, values.length); |
| assertEquals([ |
| [ "prop3", 30 ], |
| ], values); |
| } |
| TestPropertyFilter2(); |
| TestPropertyFilter2(true); |
| |
| function TestWithProxy(withWarmup) { |
| var obj1 = {prop1:10}; |
| var proxy1 = new Proxy(obj1, { }); |
| if (withWarmup) { |
| for (const key in proxy1) {} |
| } |
| assertEquals([ [ "prop1", 10 ] ], Object.entries(proxy1)); |
| |
| var obj2 = {}; |
| Object.defineProperty(obj2, "prop2", { value: 20, enumerable: true }); |
| Object.defineProperty(obj2, "prop3", { |
| get() { return 30; }, enumerable: true }); |
| var proxy2 = new Proxy(obj2, { |
| getOwnPropertyDescriptor(target, name) { |
| return Reflect.getOwnPropertyDescriptor(target, name); |
| } |
| }); |
| if (withWarmup) { |
| for (const key in proxy2) {} |
| } |
| assertEquals([ [ "prop2", 20 ], [ "prop3", 30 ] ], Object.entries(proxy2)); |
| |
| var obj3 = {}; |
| var count = 0; |
| var proxy3 = new Proxy(obj3, { |
| get(target, property, receiver) { |
| return count++ * 5; |
| }, |
| getOwnPropertyDescriptor(target, property) { |
| return { configurable: true, enumerable: true }; |
| }, |
| ownKeys(target) { |
| return [ "prop0", "prop1", Symbol("prop2"), Symbol("prop5") ]; |
| } |
| }); |
| if (withWarmup) { |
| for (const key in proxy3) {} |
| } |
| assertEquals([ [ "prop0", 0 ], [ "prop1", 5 ] ], Object.entries(proxy3)); |
| } |
| TestWithProxy(); |
| TestWithProxy(true); |
| |
| |
| function TestMutateDuringEnumeration(withWarmup) { |
| var aDeletesB = { |
| get a() { |
| delete this.b; |
| return 1; |
| }, |
| b: 2 |
| }; |
| if (withWarmup) { |
| for (const key in aDeletesB) {} |
| } |
| assertEquals([ [ "a", 1 ] ], Object.entries(aDeletesB)); |
| |
| var aRemovesB = { |
| get a() { |
| Object.defineProperty(this, "b", { enumerable: false }); |
| return 1; |
| }, |
| b: 2 |
| }; |
| if (withWarmup) { |
| for (const key in aRemovesB) {} |
| } |
| assertEquals([ [ "a", 1 ] ], Object.entries(aRemovesB)); |
| |
| var aAddsB = { get a() { this.b = 2; return 1; } }; |
| if (withWarmup) { |
| for (const key in aAddsB) {} |
| } |
| assertEquals([ [ "a", 1 ] ], Object.entries(aAddsB)); |
| |
| var aMakesBEnumerable = {}; |
| Object.defineProperty(aMakesBEnumerable, "a", { |
| get() { |
| Object.defineProperty(this, "b", { enumerable: true }); |
| return 1; |
| }, |
| enumerable: true |
| }); |
| Object.defineProperty(aMakesBEnumerable, "b", { |
| value: 2, configurable:true, enumerable: false }); |
| if (withWarmup) { |
| for (const key in aMakesBEnumerable) {} |
| } |
| assertEquals([ [ "a", 1 ], [ "b", 2 ] ], Object.entries(aMakesBEnumerable)); |
| } |
| TestMutateDuringEnumeration(); |
| TestMutateDuringEnumeration(true); |
| |
| |
| function TestElementKinds(withWarmup) { |
| var O1 = { name: "1" }, O2 = { name: "2" }, O3 = { name: "3" }; |
| var PI = 3.141592653589793; |
| var E = 2.718281828459045; |
| function fastSloppyArguments(a, b, c) { |
| delete arguments[0]; |
| arguments[0] = a; |
| return arguments; |
| } |
| |
| function slowSloppyArguments(a, b, c) { |
| delete arguments[0]; |
| arguments[0] = a; |
| Object.defineProperties(arguments, { |
| 0: { |
| enumerable: true, |
| value: a |
| }, |
| 9999: { |
| enumerable: false, |
| value: "Y" |
| } |
| }); |
| arguments[10000] = "X"; |
| return arguments; |
| } |
| |
| var element_kinds = { |
| PACKED_SMI_ELEMENTS: [ [1, 2, 3], [ ["0", 1], ["1", 2], ["2", 3] ] ], |
| HOLEY_SMI_ELEMENTS: [ [, , 3], [ ["2", 3] ] ], |
| PACKED_ELEMENTS: [ [O1, O2, O3], [ ["0", O1], ["1", O2], ["2", O3] ] ], |
| HOLEY_ELEMENTS: [ [, , O3], [ ["2", O3] ] ], |
| PACKED_DOUBLE_ELEMENTS: [ [E, NaN, PI], [ ["0", E], ["1", NaN], ["2", PI] ] ], |
| HOLEY_DOUBLE_ELEMENTS: [ [, , NaN], [ ["2", NaN] ] ], |
| |
| DICTIONARY_ELEMENTS: [ Object.defineProperties({ 10000: "world" }, { |
| 100: { enumerable: true, value: "hello", configurable: true}, |
| 99: { enumerable: false, value: "nope", configurable: true} |
| }), [ ["100", "hello"], ["10000", "world" ] ] ], |
| FAST_SLOPPY_ARGUMENTS_ELEMENTS: [ |
| fastSloppyArguments("a", "b", "c"), |
| [ ["0", "a"], ["1", "b"], ["2", "c"] ] ], |
| SLOW_SLOPPY_ARGUMENTS_ELEMENTS: [ |
| slowSloppyArguments("a", "b", "c"), |
| [ ["0", "a"], ["1", "b"], ["2", "c"], ["10000", "X"] ] ], |
| |
| FAST_STRING_WRAPPER_ELEMENTS: [ new String("str"), |
| [ ["0", "s"], ["1", "t"], ["2", "r"]] ], |
| SLOW_STRING_WRAPPER_ELEMENTS: [ |
| Object.defineProperties(new String("str"), { |
| 10000: { enumerable: false, value: "X", configurable: true}, |
| 9999: { enumerable: true, value: "Y", configurable: true} |
| }), [["0", "s"], ["1", "t"], ["2", "r"], ["9999", "Y"]] ], |
| }; |
| |
| if (withWarmup) { |
| for (const key in element_kinds) {} |
| } |
| for (let [kind, [object, expected]] of Object.entries(element_kinds)) { |
| if (withWarmup) { |
| for (const key in object) {} |
| } |
| let result1 = Object.entries(object); |
| %HeapObjectVerify(object); |
| %HeapObjectVerify(result1); |
| assertEquals(expected, result1, `fast Object.entries() with ${kind}`); |
| |
| let proxy = new Proxy(object, {}); |
| if (withWarmup) { |
| for (const key in proxy) {} |
| } |
| let result2 = Object.entries(proxy); |
| %HeapObjectVerify(result2); |
| assertEquals(result1, result2, `slow Object.entries() with ${kind}`); |
| } |
| |
| function makeFastElements(array) { |
| // Remove all possible getters. |
| for (let k of Object.getOwnPropertyNames(this)) { |
| if (k == "length") continue; |
| delete this[k]; |
| } |
| // Make the array large enough to trigger re-checking for compaction. |
| this[1000] = 1; |
| // Make the elements fast again. |
| Array.prototype.unshift.call(this, 1.1); |
| } |
| |
| // Test that changing the elements kind is supported. |
| for (let [kind, [object, expected]] of Object.entries(element_kinds)) { |
| if (kind == "FAST_STRING_WRAPPER_ELEMENTS") break; |
| object.__defineGetter__(1, makeFastElements); |
| if (withWarmup) { |
| for (const key in object) {} |
| } |
| let result1 = Object.entries(object).toString(); |
| %HeapObjectVerify(object); |
| %HeapObjectVerify(result1); |
| } |
| |
| } |
| |
| TestElementKinds(); |
| TestElementKinds(true); |