object-entries.js 10.8 KB
Newer Older
1 2 3 4
// 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.

5
// Flags: --allow-natives-syntax
6 7 8 9

function TestMeta() {
  assertEquals(1, Object.entries.length);
  assertEquals(Function.prototype, Object.getPrototypeOf(Object.entries));
10 11 12 13 14 15 16 17
  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);
18 19 20 21
}
TestMeta();


22
function TestBasic(withWarmup) {
23 24 25 26 27 28 29 30 31 32 33 34 35
  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 });
36 37 38 39 40
  if (withWarmup) {
    for (const key in O) {}
  }
  O.c = 6;
  const resultEntries = [
41 42 43 44
    ["0", 123],
    ["256", "ducks"],
    ["1000", 456],
    ["d", 1],
45
    ["c", 6],
46 47 48
    ["0x100", "quack"],
    ["a", 2],
    ["b", 4]
49 50 51
  ];
  assertEquals(resultEntries, Object.entries(O));
  assertEquals(resultEntries, Object.entries(O));
52
  assertEquals(Object.entries(O), Object.keys(O).map(key => [key, O[key]]));
53 54 55

  assertTrue(Array.isArray(Object.entries({})));
  assertEquals(0, Object.entries({}).length);
56 57
}
TestBasic();
58
TestBasic(true);
59 60


61 62 63 64 65 66 67 68
function TestToObject() {
  assertThrows(function() { Object.entries(); }, TypeError);
  assertThrows(function() { Object.entries(null); }, TypeError);
  assertThrows(function() { Object.entries(void 0); }, TypeError);
}
TestToObject();


69
function TestOrder(withWarmup) {
70 71 72 73 74 75
  var O = {
    a: 1,
    [Symbol.iterator]: null
  };
  O[456] = 123;
  Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN });
76 77
  var priv = %CreatePrivateSymbol("Secret");
  O[priv] = 56;
78 79 80 81 82 83 84 85 86 87 88

  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);
    },
89 90 91 92
    getOwnPropertyDescriptor(target, name) {
      log.push(`[[GetOwnProperty]](${JSON.stringify(name)})`);
      return Reflect.getOwnPropertyDescriptor(target, name);
    },
93 94 95 96 97
    set(target, name, value) {
      assertUnreachable();
    }
  });

98 99 100 101 102
  if (withWarmup) {
    for (const key in P) {}
  }
  log = [];

103 104 105
  assertEquals([["456", 123], ["a", 1]], Object.entries(P));
  assertEquals([
    "[[OwnPropertyKeys]]",
106
    "[[GetOwnProperty]](\"456\")",
107
    "[[Get]](\"456\")",
108 109 110
    "[[GetOwnProperty]](\"a\")",
    "[[Get]](\"a\")",
    "[[GetOwnProperty]](\"HIDDEN\")"
111 112 113
  ], log);
}
TestOrder();
114
TestOrder(true);
115 116


117
function TestOrderWithDuplicates(withWarmup) {
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
  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();
    }
  });

146
  if (withWarmup) {
147 148
    for (const key in O) {};
    try { for (const key in P) {} } catch {};
149 150
  }

151
  assertThrows(() => Object.entries(P), TypeError);
152 153
}
TestOrderWithDuplicates();
154
TestOrderWithDuplicates(true);
155

156 157 158 159
function TestDescriptorProperty() {
  function f() {};
  const o = {};
  o.a = f;
160

161 162 163 164 165 166 167
  for (const key in o) {};
  const entries = Object.entries(o);
  assertEquals([['a', f]], entries);
}
TestDescriptorProperty();

function TestPropertyFilter(withWarmup) {
168 169 170 171 172 173 174 175 176 177
  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;

178 179 180 181
  if (withWarmup) {
    for (const key in object) {}
  }

182 183 184 185 186 187 188 189 190 191 192
  values = Object.entries(object);
  assertEquals(5, values.length);
  assertEquals([
    [ "2", 40 ],
    [ "prop3", 30 ],
    [ "prop4", 50 ],
    [ "prop5", 60 ],
    [ "prop7", 80 ]
  ], values);
}
TestPropertyFilter();
193
TestPropertyFilter(true);
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
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);
213

214
function TestWithProxy(withWarmup) {
215 216
  var obj1 = {prop1:10};
  var proxy1 = new Proxy(obj1, { });
217 218 219
  if (withWarmup) {
    for (const key in proxy1) {}
  }
220 221 222 223 224 225 226 227 228 229 230
  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);
    }
  });
231 232 233
  if (withWarmup) {
    for (const key in proxy2) {}
  }
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
  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") ];
    }
  });
249 250 251
  if (withWarmup) {
    for (const key in proxy3) {}
  }
252 253 254
  assertEquals([ [ "prop0", 0 ], [ "prop1", 5 ] ], Object.entries(proxy3));
}
TestWithProxy();
255
TestWithProxy(true);
256 257


258
function TestMutateDuringEnumeration(withWarmup) {
259 260 261 262 263 264 265
  var aDeletesB = {
    get a() {
      delete this.b;
      return 1;
    },
    b: 2
  };
266 267 268
  if (withWarmup) {
    for (const key in aDeletesB) {}
  }
269 270 271 272 273 274 275 276 277
  assertEquals([ [ "a", 1 ] ], Object.entries(aDeletesB));

  var aRemovesB = {
    get a() {
      Object.defineProperty(this, "b", { enumerable: false });
      return 1;
    },
    b: 2
  };
278 279 280
  if (withWarmup) {
    for (const key in aRemovesB) {}
  }
281 282 283
  assertEquals([ [ "a", 1 ] ], Object.entries(aRemovesB));

  var aAddsB = { get a() { this.b = 2; return 1; } };
284 285 286
  if (withWarmup) {
    for (const key in aAddsB) {}
  }
287 288 289 290 291 292 293 294 295 296 297 298
  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 });
299 300 301
  if (withWarmup) {
    for (const key in aMakesBEnumerable) {}
  }
302 303 304
  assertEquals([ [ "a", 1 ], [ "b", 2 ] ], Object.entries(aMakesBEnumerable));
}
TestMutateDuringEnumeration();
305
TestMutateDuringEnumeration(true);
306 307


308
function TestElementKinds(withWarmup) {
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
  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 = {
336 337 338 339 340 341
    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] ] ],
342 343

    DICTIONARY_ELEMENTS: [ Object.defineProperties({ 10000: "world" }, {
344 345
       100: { enumerable: true, value: "hello", configurable: true},
        99: { enumerable: false, value: "nope", configurable: true}
346 347 348 349 350 351 352 353 354 355 356 357
      }), [ ["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"), {
358 359
          10000: { enumerable: false, value: "X", configurable: true},
          9999: { enumerable: true, value: "Y", configurable: true}
360 361 362
        }), [["0", "s"], ["1", "t"], ["2", "r"], ["9999", "Y"]] ],
  };

363 364 365
  if (withWarmup) {
    for (const key in element_kinds) {}
  }
366
  for (let [kind, [object, expected]] of Object.entries(element_kinds)) {
367 368 369
    if (withWarmup) {
      for (const key in object) {}
    }
370
    let result1 = Object.entries(object);
371 372
    %HeapObjectVerify(object);
    %HeapObjectVerify(result1);
373 374 375
    assertEquals(expected, result1, `fast Object.entries() with ${kind}`);

    let proxy = new Proxy(object, {});
376 377 378
    if (withWarmup) {
      for (const key in proxy) {}
    }
379
    let result2 = Object.entries(proxy);
380
    %HeapObjectVerify(result2);
381 382
    assertEquals(result1, result2, `slow Object.entries() with ${kind}`);
  }
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399

  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);
400 401 402
    if (withWarmup) {
      for (const key in object) {}
    }
403 404 405 406 407
    let result1 = Object.entries(object).toString();
    %HeapObjectVerify(object);
    %HeapObjectVerify(result1);
  }

408 409 410 411
}

TestElementKinds();
TestElementKinds(true);