1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 // Flags: --expose-gc --allow-natives-syntax 29 30 var symbols = [] 31 32 33 // Returns true if the string is a valid 34 // serialization of Symbols added to the 'symbols' 35 // array. Adjust if you extend 'symbols' with other 36 // values. 37 function isValidSymbolString(s) { 38 return ["Symbol(66)", "Symbol()"].indexOf(s) >= 0; 39 } 40 41 42 // Test different forms of constructor calls. 43 function TestNew() { 44 function indirectSymbol() { return Symbol() } 45 function indirect() { return indirectSymbol() } 46 for (var i = 0; i < 2; ++i) { 47 for (var j = 0; j < 5; ++j) { 48 symbols.push(Symbol()) 49 symbols.push(Symbol(undefined)) 50 symbols.push(Symbol("66")) 51 symbols.push(Symbol(66)) 52 symbols.push(Symbol().valueOf()) 53 symbols.push(indirect()) 54 } 55 %OptimizeFunctionOnNextCall(indirect) 56 indirect() // Call once before GC throws away type feedback. 57 gc() // Promote existing symbols and then allocate some more. 58 } 59 assertThrows(function () { Symbol(Symbol()) }, TypeError) 60 assertThrows(function () { new Symbol(66) }, TypeError) 61 } 62 TestNew() 63 64 65 function TestType() { 66 for (var i in symbols) { 67 assertEquals("symbol", typeof symbols[i]) 68 assertTrue(typeof symbols[i] === "symbol") 69 assertFalse(%SymbolIsPrivate(symbols[i])) 70 assertEquals(null, %_ClassOf(symbols[i])) 71 assertEquals("Symbol", %_ClassOf(Object(symbols[i]))) 72 } 73 } 74 TestType() 75 76 77 function TestPrototype() { 78 assertSame(Object.prototype, Symbol.prototype.__proto__) 79 assertSame(Symbol.prototype, Symbol().__proto__) 80 assertSame(Symbol.prototype, Object(Symbol()).__proto__) 81 for (var i in symbols) { 82 assertSame(Symbol.prototype, symbols[i].__proto__) 83 } 84 } 85 TestPrototype() 86 87 88 function TestConstructor() { 89 assertEquals(0, Symbol.length); 90 assertSame(Function.prototype, Symbol.__proto__) 91 assertFalse(Object === Symbol.prototype.constructor) 92 assertFalse(Symbol === Object.prototype.constructor) 93 assertSame(Symbol, Symbol.prototype.constructor) 94 assertSame(Symbol, Symbol().__proto__.constructor) 95 assertSame(Symbol, Object(Symbol()).__proto__.constructor) 96 for (var i in symbols) { 97 assertSame(Symbol, symbols[i].__proto__.constructor) 98 } 99 } 100 TestConstructor() 101 102 103 function TestValueOf() { 104 for (var i in symbols) { 105 assertTrue(symbols[i] === Object(symbols[i]).valueOf()) 106 assertTrue(symbols[i] === symbols[i].valueOf()) 107 assertTrue(Symbol.prototype.valueOf.call(Object(symbols[i])) === symbols[i]) 108 assertTrue(Symbol.prototype.valueOf.call(symbols[i]) === symbols[i]) 109 } 110 } 111 TestValueOf() 112 113 114 function TestToString() { 115 for (var i in symbols) { 116 assertThrows(function() { new String(symbols[i]) }, TypeError) 117 assertEquals(symbols[i].toString(), String(symbols[i])) 118 assertThrows(function() { symbols[i] + "" }, TypeError) 119 assertThrows(function() { String(Object(symbols[i])) }, TypeError) 120 assertTrue(isValidSymbolString(symbols[i].toString())) 121 assertTrue(isValidSymbolString(Object(symbols[i]).toString())) 122 assertTrue( 123 isValidSymbolString(Symbol.prototype.toString.call(symbols[i]))) 124 assertEquals( 125 "[object Symbol]", Object.prototype.toString.call(symbols[i])) 126 } 127 } 128 TestToString() 129 130 131 function TestToBoolean() { 132 for (var i in symbols) { 133 assertTrue(Boolean(Object(symbols[i]))) 134 assertFalse(!Object(symbols[i])) 135 assertTrue(Boolean(symbols[i]).valueOf()) 136 assertFalse(!symbols[i]) 137 assertTrue(!!symbols[i]) 138 assertTrue(symbols[i] && true) 139 assertFalse(!symbols[i] && false) 140 assertTrue(!symbols[i] || true) 141 assertEquals(1, symbols[i] ? 1 : 2) 142 assertEquals(2, !symbols[i] ? 1 : 2) 143 if (!symbols[i]) assertUnreachable(); 144 if (symbols[i]) {} else assertUnreachable(); 145 } 146 } 147 TestToBoolean() 148 149 150 function TestToNumber() { 151 for (var i in symbols) { 152 assertThrows(function() { Number(Object(symbols[i])) }, TypeError) 153 assertThrows(function() { +Object(symbols[i]) }, TypeError) 154 assertThrows(function() { Number(symbols[i]) }, TypeError) 155 assertThrows(function() { symbols[i] + 0 }, TypeError) 156 } 157 } 158 TestToNumber() 159 160 161 function TestEquality() { 162 // Every symbol should equal itself, and non-strictly equal its wrapper. 163 for (var i in symbols) { 164 assertSame(symbols[i], symbols[i]) 165 assertEquals(symbols[i], symbols[i]) 166 assertTrue(Object.is(symbols[i], symbols[i])) 167 assertTrue(symbols[i] === symbols[i]) 168 assertTrue(symbols[i] == symbols[i]) 169 assertFalse(symbols[i] === Object(symbols[i])) 170 assertFalse(Object(symbols[i]) === symbols[i]) 171 assertTrue(symbols[i] == Object(symbols[i])) 172 assertTrue(Object(symbols[i]) == symbols[i]) 173 assertTrue(symbols[i] === symbols[i].valueOf()) 174 assertTrue(symbols[i].valueOf() === symbols[i]) 175 assertTrue(symbols[i] == symbols[i].valueOf()) 176 assertTrue(symbols[i].valueOf() == symbols[i]) 177 assertFalse(Object(symbols[i]) === Object(symbols[i])) 178 assertEquals(Object(symbols[i]).valueOf(), Object(symbols[i]).valueOf()) 179 } 180 181 // All symbols should be distinct. 182 for (var i = 0; i < symbols.length; ++i) { 183 for (var j = i + 1; j < symbols.length; ++j) { 184 assertFalse(Object.is(symbols[i], symbols[j])) 185 assertFalse(symbols[i] === symbols[j]) 186 assertFalse(symbols[i] == symbols[j]) 187 } 188 } 189 190 // Symbols should not be equal to any other value (and the test terminates). 191 var values = [347, 1.275, NaN, "string", null, undefined, {}, function() {}] 192 for (var i in symbols) { 193 for (var j in values) { 194 assertFalse(symbols[i] === values[j]) 195 assertFalse(values[j] === symbols[i]) 196 assertFalse(symbols[i] == values[j]) 197 assertFalse(values[j] == symbols[i]) 198 } 199 } 200 } 201 TestEquality() 202 203 204 function TestGet() { 205 for (var i in symbols) { 206 assertTrue(isValidSymbolString(symbols[i].toString())) 207 assertEquals(symbols[i], symbols[i].valueOf()) 208 assertEquals(undefined, symbols[i].a) 209 assertEquals(undefined, symbols[i]["a" + "b"]) 210 assertEquals(undefined, symbols[i]["" + "1"]) 211 assertEquals(undefined, symbols[i][62]) 212 } 213 } 214 TestGet() 215 216 217 function TestSet() { 218 for (var i in symbols) { 219 symbols[i].toString = 0 220 assertTrue(isValidSymbolString(symbols[i].toString())) 221 symbols[i].valueOf = 0 222 assertEquals(symbols[i], symbols[i].valueOf()) 223 symbols[i].a = 0 224 assertEquals(undefined, symbols[i].a) 225 symbols[i]["a" + "b"] = 0 226 assertEquals(undefined, symbols[i]["a" + "b"]) 227 symbols[i][62] = 0 228 assertEquals(undefined, symbols[i][62]) 229 } 230 } 231 TestSet() 232 233 234 // Test Symbol wrapping/boxing over non-builtins. 235 Symbol.prototype.getThisProto = function () { 236 return Object.getPrototypeOf(this); 237 } 238 function TestCall() { 239 for (var i in symbols) { 240 assertTrue(symbols[i].getThisProto() === Symbol.prototype) 241 } 242 } 243 TestCall() 244 245 246 function TestCollections() { 247 var set = new Set 248 var map = new Map 249 for (var i in symbols) { 250 set.add(symbols[i]) 251 map.set(symbols[i], i) 252 } 253 assertEquals(symbols.length, set.size) 254 assertEquals(symbols.length, map.size) 255 for (var i in symbols) { 256 assertTrue(set.has(symbols[i])) 257 assertTrue(map.has(symbols[i])) 258 assertEquals(i, map.get(symbols[i])) 259 } 260 for (var i in symbols) { 261 assertTrue(set.delete(symbols[i])) 262 assertTrue(map.delete(symbols[i])) 263 } 264 assertEquals(0, set.size) 265 assertEquals(0, map.size) 266 } 267 TestCollections() 268 269 270 271 function TestKeySet(obj) { 272 assertTrue(%HasFastProperties(obj)) 273 // Set the even symbols via assignment. 274 for (var i = 0; i < symbols.length; i += 2) { 275 obj[symbols[i]] = i 276 // Object should remain in fast mode until too many properties were added. 277 assertTrue(%HasFastProperties(obj) || i >= 30) 278 } 279 } 280 281 282 function TestKeyDefine(obj) { 283 // Set the odd symbols via defineProperty (as non-enumerable). 284 for (var i = 1; i < symbols.length; i += 2) { 285 Object.defineProperty(obj, symbols[i], {value: i, configurable: true}) 286 } 287 } 288 289 290 function TestKeyGet(obj) { 291 var obj2 = Object.create(obj) 292 for (var i in symbols) { 293 assertEquals(i|0, obj[symbols[i]]) 294 assertEquals(i|0, obj2[symbols[i]]) 295 } 296 } 297 298 299 function TestKeyHas(obj) { 300 for (var i in symbols) { 301 assertTrue(symbols[i] in obj) 302 assertTrue(Object.hasOwnProperty.call(obj, symbols[i])) 303 } 304 } 305 306 307 function TestKeyEnum(obj) { 308 for (var name in obj) { 309 assertEquals("string", typeof name) 310 } 311 } 312 313 314 function TestKeyNames(obj) { 315 assertEquals(0, Object.keys(obj).length) 316 317 var names = Object.getOwnPropertyNames(obj) 318 for (var i in names) { 319 assertEquals("string", typeof names[i]) 320 } 321 } 322 323 324 function TestGetOwnPropertySymbols(obj) { 325 var syms = Object.getOwnPropertySymbols(obj) 326 assertEquals(syms.length, symbols.length) 327 for (var i in syms) { 328 assertEquals("symbol", typeof syms[i]) 329 } 330 } 331 332 333 function TestKeyDescriptor(obj) { 334 for (var i in symbols) { 335 var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]) 336 assertEquals(i|0, desc.value) 337 assertTrue(desc.configurable) 338 assertEquals(i % 2 == 0, desc.writable) 339 assertEquals(i % 2 == 0, desc.enumerable) 340 assertEquals(i % 2 == 0, 341 Object.prototype.propertyIsEnumerable.call(obj, symbols[i])) 342 } 343 } 344 345 346 function TestKeyDelete(obj) { 347 for (var i in symbols) { 348 delete obj[symbols[i]] 349 } 350 for (var i in symbols) { 351 assertEquals(undefined, Object.getOwnPropertyDescriptor(obj, symbols[i])) 352 } 353 } 354 355 356 var objs = [{}, [], Object.create(null), Object(1), new Map, function(){}] 357 358 for (var i in objs) { 359 var obj = objs[i] 360 TestKeySet(obj) 361 TestKeyDefine(obj) 362 TestKeyGet(obj) 363 TestKeyHas(obj) 364 TestKeyEnum(obj) 365 TestKeyNames(obj) 366 TestGetOwnPropertySymbols(obj) 367 TestKeyDescriptor(obj) 368 TestKeyDelete(obj) 369 } 370 371 372 function TestDefineProperties() { 373 var properties = {} 374 for (var i in symbols) { 375 Object.defineProperty( 376 properties, symbols[i], {value: {value: i}, enumerable: i % 2 === 0}) 377 } 378 var o = Object.defineProperties({}, properties) 379 for (var i in symbols) { 380 assertEquals(i % 2 === 0, symbols[i] in o) 381 } 382 } 383 TestDefineProperties() 384 385 386 function TestCreate() { 387 var properties = {} 388 for (var i in symbols) { 389 Object.defineProperty( 390 properties, symbols[i], {value: {value: i}, enumerable: i % 2 === 0}) 391 } 392 var o = Object.create(Object.prototype, properties) 393 for (var i in symbols) { 394 assertEquals(i % 2 === 0, symbols[i] in o) 395 } 396 } 397 TestCreate() 398 399 400 function TestCachedKeyAfterScavenge() { 401 gc(); 402 // Keyed property lookup are cached. Hereby we assume that the keys are 403 // tenured, so that we only have to clear the cache between mark compacts, 404 // but not between scavenges. This must also apply for symbol keys. 405 var key = Symbol("key"); 406 var a = {}; 407 a[key] = "abc"; 408 409 for (var i = 0; i < 100000; i++) { 410 a[key] += "a"; // Allocations cause a scavenge. 411 } 412 } 413 TestCachedKeyAfterScavenge(); 414 415 416 function TestGetOwnPropertySymbolsWithProto() { 417 // We need to be have fast properties to have insertion order for property 418 // keys. The current limit is currently 30 properties. 419 var syms = symbols.slice(0, 30); 420 var proto = {} 421 var object = Object.create(proto) 422 for (var i = 0; i < syms.length; i++) { 423 // Even on object, odd on proto. 424 if (i % 2) { 425 proto[syms[i]] = i 426 } else { 427 object[syms[i]] = i 428 } 429 } 430 431 assertTrue(%HasFastProperties(object)); 432 433 var objectOwnSymbols = Object.getOwnPropertySymbols(object) 434 assertEquals(objectOwnSymbols.length, syms.length / 2) 435 436 for (var i = 0; i < objectOwnSymbols.length; i++) { 437 assertEquals(objectOwnSymbols[i], syms[i * 2]) 438 } 439 } 440 TestGetOwnPropertySymbolsWithProto() 441 442 443 function TestWellKnown() { 444 var symbols = [ 445 "hasInstance", 446 // TODO(rossberg): reactivate once implemented. 447 // "isConcatSpreadable", "isRegExp", 448 "iterator", /* "toStringTag", */ "unscopables" 449 ] 450 451 for (var i in symbols) { 452 var name = symbols[i] 453 var desc = Object.getOwnPropertyDescriptor(Symbol, name) 454 assertSame("symbol", typeof desc.value) 455 assertSame("Symbol(Symbol." + name + ")", desc.value.toString()) 456 assertFalse(desc.writable) 457 assertFalse(desc.configurable) 458 assertFalse(desc.enumerable) 459 460 assertFalse(Symbol.for("Symbol." + name) === desc.value) 461 assertTrue(Symbol.keyFor(desc.value) === undefined) 462 } 463 } 464 TestWellKnown() 465 466 467 function TestRegistry() { 468 var symbol1 = Symbol.for("x1") 469 var symbol2 = Symbol.for("x2") 470 assertFalse(symbol1 === symbol2) 471 472 assertSame(symbol1, Symbol.for("x1")) 473 assertSame(symbol2, Symbol.for("x2")) 474 assertSame("x1", Symbol.keyFor(symbol1)) 475 assertSame("x2", Symbol.keyFor(symbol2)) 476 477 assertSame(Symbol.for("1"), Symbol.for(1)) 478 assertThrows(function() { Symbol.keyFor("bla") }, TypeError) 479 assertThrows(function() { Symbol.keyFor({}) }, TypeError) 480 481 var realm = Realm.create() 482 assertFalse(Symbol === Realm.eval(realm, "Symbol")) 483 assertFalse(Symbol.for === Realm.eval(realm, "Symbol.for")) 484 assertFalse(Symbol.keyFor === Realm.eval(realm, "Symbol.keyFor")) 485 assertSame(Symbol.create, Realm.eval(realm, "Symbol.create")) 486 assertSame(Symbol.iterator, Realm.eval(realm, "Symbol.iterator")) 487 488 assertSame(symbol1, Realm.eval(realm, "Symbol.for")("x1")) 489 assertSame(symbol1, Realm.eval(realm, "Symbol.for('x1')")) 490 assertSame("x1", Realm.eval(realm, "Symbol.keyFor")(symbol1)) 491 Realm.shared = symbol1 492 assertSame("x1", Realm.eval(realm, "Symbol.keyFor(Realm.shared)")) 493 494 var symbol3 = Realm.eval(realm, "Symbol.for('x3')") 495 assertFalse(symbol1 === symbol3) 496 assertFalse(symbol2 === symbol3) 497 assertSame(symbol3, Symbol.for("x3")) 498 assertSame("x3", Symbol.keyFor(symbol3)) 499 } 500 TestRegistry() 501 502 503 function TestGetOwnPropertySymbolsOnPrimitives() { 504 assertEquals(Object.getOwnPropertySymbols(true), []); 505 assertEquals(Object.getOwnPropertySymbols(5000), []); 506 assertEquals(Object.getOwnPropertySymbols("OK"), []); 507 } 508 TestGetOwnPropertySymbolsOnPrimitives(); 509 510 511 function TestComparison() { 512 function lt() { var a = Symbol(); var b = Symbol(); a < b; } 513 function gt() { var a = Symbol(); var b = Symbol(); a > b; } 514 function le() { var a = Symbol(); var b = Symbol(); a <= b; } 515 function ge() { var a = Symbol(); var b = Symbol(); a >= b; } 516 function lt_same() { var a = Symbol(); a < a; } 517 function gt_same() { var a = Symbol(); a > a; } 518 function le_same() { var a = Symbol(); a <= a; } 519 function ge_same() { var a = Symbol(); a >= a; } 520 521 var throwFuncs = [lt, gt, le, ge, lt_same, gt_same, le_same, ge_same]; 522 523 for (var f of throwFuncs) { 524 assertThrows(f, TypeError); 525 %OptimizeFunctionOnNextCall(f); 526 assertThrows(f, TypeError); 527 assertThrows(f, TypeError); 528 } 529 } 530 TestComparison(); 531 532 533 // Make sure that throws occur in the context of the Symbol function. 534 function TestContext() { 535 var r = Realm.create(); 536 var rSymbol = Realm.eval(r, "Symbol"); 537 var rError = Realm.eval(r, "TypeError"); 538 539 function verifier(symbol, error) { 540 try { 541 new symbol(); 542 } catch(e) { 543 return e.__proto__ === error.__proto__; 544 } 545 assertTrue(false); // should never get here. 546 } 547 548 assertTrue(verifier(Symbol, TypeError())); 549 assertTrue(verifier(rSymbol, rError())); 550 assertFalse(verifier(Symbol, rError())); 551 assertFalse(verifier(rSymbol, TypeError())); 552 } 553 TestContext(); 554 555 556 function TestStringify(expected, input) { 557 assertEquals(expected, JSON.stringify(input)); 558 assertEquals(expected, JSON.stringify(input, (key, value) => value)); 559 assertEquals(JSON.stringify(input, null, "="), 560 JSON.stringify(input, (key, value) => value, "=")); 561 } 562 563 TestStringify(undefined, Symbol("a")); 564 TestStringify('[{}]', [Object(Symbol())]); 565 var symbol_wrapper = Object(Symbol("a")) 566 TestStringify('{}', symbol_wrapper); 567 symbol_wrapper.a = 1; 568 TestStringify('{"a":1}', symbol_wrapper); 569