| // 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 |
| |
| // Ensure empty keys are handled properly |
| (function() { |
| const a = {}; |
| let k = Object.keys(a); |
| %HeapObjectVerify(k); |
| assertEquals(0, k.length); |
| })(); |
| |
| // Ensure non-enumerable keys are handled properly |
| (function() { |
| const a = {}; |
| Object.defineProperty(a, 'x', { |
| value: 1, |
| enumerable: false |
| }); |
| let k = Object.keys(a); |
| %HeapObjectVerify(k); |
| assertEquals(0, k.length); |
| |
| a.y = 2; |
| k = Object.keys(a); |
| %HeapObjectVerify(k); |
| assertEquals(1, k.length); |
| })(); |
| |
| // Ensure that mutation of the Object.keys result doesn't affect the |
| // enumeration cache for fast-mode objects. |
| (function() { |
| const a = {x:1, y:2}; |
| let k = Object.keys(a); |
| %HeapObjectVerify(k); |
| assertEquals(2, k.length); |
| assertEquals("x", k[0]); |
| assertEquals("y", k[1]); |
| k[0] = "y"; |
| k[1] = "x"; |
| k = Object.keys(a); |
| assertEquals(2, k.length); |
| assertEquals("x", k[0]); |
| assertEquals("y", k[1]); |
| })(); |
| |
| // Ensure that the copy-on-write keys are handled properly, even in |
| // the presence of Symbols. |
| (function() { |
| const s = Symbol(); |
| const a = {[s]: 1}; |
| let k = Object.keys(a); |
| %HeapObjectVerify(k); |
| assertEquals(0, k.length); |
| k.shift(); |
| assertEquals(0, k.length); |
| })(); |
| |
| // Ensure we invoke all steps on proxies. |
| (function ObjectKeysProxy() { |
| let log = []; |
| let result = Object.keys(new Proxy({}, { |
| ownKeys(target) { |
| log.push('ownKeys'); |
| return ['a', 'b', 'c']; |
| }, |
| getOwnPropertyDescriptor(target, key) { |
| log.push('getOwnPropertyDescriptor-' + key); |
| if (key === 'b') return {enumerable: false, configurable: true}; |
| return {enumerable: true, configurable: true}; |
| } |
| })); |
| assertEquals(['a', 'c'], result); |
| assertEquals( |
| [ |
| 'ownKeys', 'getOwnPropertyDescriptor-a', 'getOwnPropertyDescriptor-b', |
| 'getOwnPropertyDescriptor-c' |
| ], |
| log); |
| |
| // Test normal target. |
| log = []; |
| let target = {a: 1, b: 1, c: 1}; |
| let handler = { |
| getOwnPropertyDescriptor(target, key) { |
| log.push('getOwnPropertyDescriptor-' + key); |
| if (key === 'b') return {enumerable: false, configurable: true}; |
| return {enumerable: true, configurable: true}; |
| } |
| }; |
| result = Object.keys(new Proxy(target, handler)); |
| assertEquals(['a', 'c'], result); |
| assertEquals( |
| [ |
| 'getOwnPropertyDescriptor-a', 'getOwnPropertyDescriptor-b', |
| 'getOwnPropertyDescriptor-c' |
| ], |
| log); |
| |
| // Test trap invocation with non-enumerable target properties. |
| log = []; |
| target = Object.create(Object.prototype, { |
| a: {enumerable: true, configurable: true}, |
| b: {enumerable: false, configurable: true}, |
| c: {enumerable: true, configurable: true} |
| }); |
| result = Object.keys(new Proxy(target, handler)); |
| assertEquals(['a', 'c'], result); |
| assertEquals( |
| [ |
| 'getOwnPropertyDescriptor-a', 'getOwnPropertyDescriptor-b', |
| 'getOwnPropertyDescriptor-c' |
| ], |
| log); |
| })(); |