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