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 --harmony-tostring 29 30var 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. 37function isValidSymbolString(s) { 38 return ["Symbol(66)", "Symbol()"].indexOf(s) >= 0; 39} 40 41 42// Test different forms of constructor calls. 43function 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} 62TestNew() 63 64 65function 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} 74TestType() 75 76 77function 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} 85TestPrototype() 86 87 88function TestConstructor() { 89 assertSame(Function.prototype, Symbol.__proto__) 90 assertFalse(Object === Symbol.prototype.constructor) 91 assertFalse(Symbol === Object.prototype.constructor) 92 assertSame(Symbol, Symbol.prototype.constructor) 93 assertSame(Symbol, Symbol().__proto__.constructor) 94 assertSame(Symbol, Object(Symbol()).__proto__.constructor) 95 for (var i in symbols) { 96 assertSame(Symbol, symbols[i].__proto__.constructor) 97 } 98} 99TestConstructor() 100 101 102function TestValueOf() { 103 for (var i in symbols) { 104 assertTrue(symbols[i] === Object(symbols[i]).valueOf()) 105 assertTrue(symbols[i] === symbols[i].valueOf()) 106 assertTrue(Symbol.prototype.valueOf.call(Object(symbols[i])) === symbols[i]) 107 assertTrue(Symbol.prototype.valueOf.call(symbols[i]) === symbols[i]) 108 } 109} 110TestValueOf() 111 112 113function TestToString() { 114 for (var i in symbols) { 115 assertThrows(function() { new String(symbols[i]) }, TypeError) 116 assertEquals(symbols[i].toString(), String(symbols[i])) 117 assertThrows(function() { symbols[i] + "" }, TypeError) 118 assertThrows(function() { String(Object(symbols[i])) }, TypeError) 119 assertTrue(isValidSymbolString(symbols[i].toString())) 120 assertTrue(isValidSymbolString(Object(symbols[i]).toString())) 121 assertTrue( 122 isValidSymbolString(Symbol.prototype.toString.call(symbols[i]))) 123 assertEquals( 124 "[object Symbol]", Object.prototype.toString.call(symbols[i])) 125 } 126} 127TestToString() 128 129 130function TestToBoolean() { 131 for (var i in symbols) { 132 assertTrue(Boolean(Object(symbols[i]))) 133 assertFalse(!Object(symbols[i])) 134 assertTrue(Boolean(symbols[i]).valueOf()) 135 assertFalse(!symbols[i]) 136 assertTrue(!!symbols[i]) 137 assertTrue(symbols[i] && true) 138 assertFalse(!symbols[i] && false) 139 assertTrue(!symbols[i] || true) 140 assertEquals(1, symbols[i] ? 1 : 2) 141 assertEquals(2, !symbols[i] ? 1 : 2) 142 if (!symbols[i]) assertUnreachable(); 143 if (symbols[i]) {} else assertUnreachable(); 144 } 145} 146TestToBoolean() 147 148 149function TestToNumber() { 150 for (var i in symbols) { 151 assertThrows(function() { Number(Object(symbols[i])) }, TypeError) 152 assertThrows(function() { +Object(symbols[i]) }, TypeError) 153 assertThrows(function() { Number(symbols[i]) }, TypeError) 154 assertThrows(function() { symbols[i] + 0 }, TypeError) 155 } 156} 157TestToNumber() 158 159 160function TestEquality() { 161 // Every symbol should equal itself, and non-strictly equal its wrapper. 162 for (var i in symbols) { 163 assertSame(symbols[i], symbols[i]) 164 assertEquals(symbols[i], symbols[i]) 165 assertTrue(Object.is(symbols[i], symbols[i])) 166 assertTrue(symbols[i] === symbols[i]) 167 assertTrue(symbols[i] == symbols[i]) 168 assertFalse(symbols[i] === Object(symbols[i])) 169 assertFalse(Object(symbols[i]) === symbols[i]) 170 assertTrue(symbols[i] == Object(symbols[i])) 171 assertTrue(Object(symbols[i]) == symbols[i]) 172 assertTrue(symbols[i] === symbols[i].valueOf()) 173 assertTrue(symbols[i].valueOf() === symbols[i]) 174 assertTrue(symbols[i] == symbols[i].valueOf()) 175 assertTrue(symbols[i].valueOf() == symbols[i]) 176 assertFalse(Object(symbols[i]) === Object(symbols[i])) 177 assertEquals(Object(symbols[i]).valueOf(), Object(symbols[i]).valueOf()) 178 } 179 180 // All symbols should be distinct. 181 for (var i = 0; i < symbols.length; ++i) { 182 for (var j = i + 1; j < symbols.length; ++j) { 183 assertFalse(Object.is(symbols[i], symbols[j])) 184 assertFalse(symbols[i] === symbols[j]) 185 assertFalse(symbols[i] == symbols[j]) 186 } 187 } 188 189 // Symbols should not be equal to any other value (and the test terminates). 190 var values = [347, 1.275, NaN, "string", null, undefined, {}, function() {}] 191 for (var i in symbols) { 192 for (var j in values) { 193 assertFalse(symbols[i] === values[j]) 194 assertFalse(values[j] === symbols[i]) 195 assertFalse(symbols[i] == values[j]) 196 assertFalse(values[j] == symbols[i]) 197 } 198 } 199} 200TestEquality() 201 202 203function TestGet() { 204 for (var i in symbols) { 205 assertTrue(isValidSymbolString(symbols[i].toString())) 206 assertEquals(symbols[i], symbols[i].valueOf()) 207 assertEquals(undefined, symbols[i].a) 208 assertEquals(undefined, symbols[i]["a" + "b"]) 209 assertEquals(undefined, symbols[i]["" + "1"]) 210 assertEquals(undefined, symbols[i][62]) 211 } 212} 213TestGet() 214 215 216function TestSet() { 217 for (var i in symbols) { 218 symbols[i].toString = 0 219 assertTrue(isValidSymbolString(symbols[i].toString())) 220 symbols[i].valueOf = 0 221 assertEquals(symbols[i], symbols[i].valueOf()) 222 symbols[i].a = 0 223 assertEquals(undefined, symbols[i].a) 224 symbols[i]["a" + "b"] = 0 225 assertEquals(undefined, symbols[i]["a" + "b"]) 226 symbols[i][62] = 0 227 assertEquals(undefined, symbols[i][62]) 228 } 229} 230TestSet() 231 232 233// Test Symbol wrapping/boxing over non-builtins. 234Symbol.prototype.getThisProto = function () { 235 return Object.getPrototypeOf(this); 236} 237function TestCall() { 238 for (var i in symbols) { 239 assertTrue(symbols[i].getThisProto() === Symbol.prototype) 240 } 241} 242TestCall() 243 244 245function TestCollections() { 246 var set = new Set 247 var map = new Map 248 for (var i in symbols) { 249 set.add(symbols[i]) 250 map.set(symbols[i], i) 251 } 252 assertEquals(symbols.length, set.size) 253 assertEquals(symbols.length, map.size) 254 for (var i in symbols) { 255 assertTrue(set.has(symbols[i])) 256 assertTrue(map.has(symbols[i])) 257 assertEquals(i, map.get(symbols[i])) 258 } 259 for (var i in symbols) { 260 assertTrue(set.delete(symbols[i])) 261 assertTrue(map.delete(symbols[i])) 262 } 263 assertEquals(0, set.size) 264 assertEquals(0, map.size) 265} 266TestCollections() 267 268 269 270function TestKeySet(obj) { 271 assertTrue(%HasFastProperties(obj)) 272 // Set the even symbols via assignment. 273 for (var i = 0; i < symbols.length; i += 2) { 274 obj[symbols[i]] = i 275 // Object should remain in fast mode until too many properties were added. 276 assertTrue(%HasFastProperties(obj) || i >= 30) 277 } 278} 279 280 281function TestKeyDefine(obj) { 282 // Set the odd symbols via defineProperty (as non-enumerable). 283 for (var i = 1; i < symbols.length; i += 2) { 284 Object.defineProperty(obj, symbols[i], {value: i, configurable: true}) 285 } 286} 287 288 289function TestKeyGet(obj) { 290 var obj2 = Object.create(obj) 291 for (var i in symbols) { 292 assertEquals(i|0, obj[symbols[i]]) 293 assertEquals(i|0, obj2[symbols[i]]) 294 } 295} 296 297 298function TestKeyHas(obj) { 299 for (var i in symbols) { 300 assertTrue(symbols[i] in obj) 301 assertTrue(Object.hasOwnProperty.call(obj, symbols[i])) 302 } 303} 304 305 306function TestKeyEnum(obj) { 307 for (var name in obj) { 308 assertEquals("string", typeof name) 309 } 310} 311 312 313function TestKeyNames(obj) { 314 assertEquals(0, Object.keys(obj).length) 315 316 var names = Object.getOwnPropertyNames(obj) 317 for (var i in names) { 318 assertEquals("string", typeof names[i]) 319 } 320} 321 322 323function TestGetOwnPropertySymbols(obj) { 324 var syms = Object.getOwnPropertySymbols(obj) 325 assertEquals(syms.length, symbols.length) 326 for (var i in syms) { 327 assertEquals("symbol", typeof syms[i]) 328 } 329} 330 331 332function TestKeyDescriptor(obj) { 333 for (var i in symbols) { 334 var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]) 335 assertEquals(i|0, desc.value) 336 assertTrue(desc.configurable) 337 assertEquals(i % 2 == 0, desc.writable) 338 assertEquals(i % 2 == 0, desc.enumerable) 339 assertEquals(i % 2 == 0, 340 Object.prototype.propertyIsEnumerable.call(obj, symbols[i])) 341 } 342} 343 344 345function TestKeyDelete(obj) { 346 for (var i in symbols) { 347 delete obj[symbols[i]] 348 } 349 for (var i in symbols) { 350 assertEquals(undefined, Object.getOwnPropertyDescriptor(obj, symbols[i])) 351 } 352} 353 354 355var objs = [{}, [], Object.create(null), Object(1), new Map, function(){}] 356 357for (var i in objs) { 358 var obj = objs[i] 359 TestKeySet(obj) 360 TestKeyDefine(obj) 361 TestKeyGet(obj) 362 TestKeyHas(obj) 363 TestKeyEnum(obj) 364 TestKeyNames(obj) 365 TestGetOwnPropertySymbols(obj) 366 TestKeyDescriptor(obj) 367 TestKeyDelete(obj) 368} 369 370 371function TestDefineProperties() { 372 var properties = {} 373 for (var i in symbols) { 374 Object.defineProperty( 375 properties, symbols[i], {value: {value: i}, enumerable: i % 2 === 0}) 376 } 377 var o = Object.defineProperties({}, properties) 378 for (var i in symbols) { 379 assertEquals(i % 2 === 0, symbols[i] in o) 380 } 381} 382TestDefineProperties() 383 384 385function TestCreate() { 386 var properties = {} 387 for (var i in symbols) { 388 Object.defineProperty( 389 properties, symbols[i], {value: {value: i}, enumerable: i % 2 === 0}) 390 } 391 var o = Object.create(Object.prototype, properties) 392 for (var i in symbols) { 393 assertEquals(i % 2 === 0, symbols[i] in o) 394 } 395} 396TestCreate() 397 398 399function TestCachedKeyAfterScavenge() { 400 gc(); 401 // Keyed property lookup are cached. Hereby we assume that the keys are 402 // tenured, so that we only have to clear the cache between mark compacts, 403 // but not between scavenges. This must also apply for symbol keys. 404 var key = Symbol("key"); 405 var a = {}; 406 a[key] = "abc"; 407 408 for (var i = 0; i < 100000; i++) { 409 a[key] += "a"; // Allocations cause a scavenge. 410 } 411} 412TestCachedKeyAfterScavenge(); 413 414 415function TestGetOwnPropertySymbolsWithProto() { 416 // We need to be have fast properties to have insertion order for property 417 // keys. The current limit is currently 30 properties. 418 var syms = symbols.slice(0, 30); 419 var proto = {} 420 var object = Object.create(proto) 421 for (var i = 0; i < syms.length; i++) { 422 // Even on object, odd on proto. 423 if (i % 2) { 424 proto[syms[i]] = i 425 } else { 426 object[syms[i]] = i 427 } 428 } 429 430 assertTrue(%HasFastProperties(object)); 431 432 var objectOwnSymbols = Object.getOwnPropertySymbols(object) 433 assertEquals(objectOwnSymbols.length, syms.length / 2) 434 435 for (var i = 0; i < objectOwnSymbols.length; i++) { 436 assertEquals(objectOwnSymbols[i], syms[i * 2]) 437 } 438} 439TestGetOwnPropertySymbolsWithProto() 440 441 442function TestWellKnown() { 443 var symbols = [ 444 // TODO(rossberg): reactivate once implemented. 445 // "hasInstance", "isConcatSpreadable", "isRegExp", 446 "iterator", /* "toStringTag", */ "unscopables" 447 ] 448 449 for (var i in symbols) { 450 var name = symbols[i] 451 var desc = Object.getOwnPropertyDescriptor(Symbol, name) 452 assertSame("symbol", typeof desc.value) 453 assertSame("Symbol(Symbol." + name + ")", desc.value.toString()) 454 assertFalse(desc.writable) 455 assertFalse(desc.configurable) 456 assertFalse(desc.enumerable) 457 458 assertFalse(Symbol.for("Symbol." + name) === desc.value) 459 assertTrue(Symbol.keyFor(desc.value) === undefined) 460 } 461} 462TestWellKnown() 463 464 465function TestRegistry() { 466 var symbol1 = Symbol.for("x1") 467 var symbol2 = Symbol.for("x2") 468 assertFalse(symbol1 === symbol2) 469 470 assertSame(symbol1, Symbol.for("x1")) 471 assertSame(symbol2, Symbol.for("x2")) 472 assertSame("x1", Symbol.keyFor(symbol1)) 473 assertSame("x2", Symbol.keyFor(symbol2)) 474 475 assertSame(Symbol.for("1"), Symbol.for(1)) 476 assertThrows(function() { Symbol.keyFor("bla") }, TypeError) 477 assertThrows(function() { Symbol.keyFor({}) }, TypeError) 478 479 var realm = Realm.create() 480 assertFalse(Symbol === Realm.eval(realm, "Symbol")) 481 assertFalse(Symbol.for === Realm.eval(realm, "Symbol.for")) 482 assertFalse(Symbol.keyFor === Realm.eval(realm, "Symbol.keyFor")) 483 assertSame(Symbol.create, Realm.eval(realm, "Symbol.create")) 484 assertSame(Symbol.iterator, Realm.eval(realm, "Symbol.iterator")) 485 486 assertSame(symbol1, Realm.eval(realm, "Symbol.for")("x1")) 487 assertSame(symbol1, Realm.eval(realm, "Symbol.for('x1')")) 488 assertSame("x1", Realm.eval(realm, "Symbol.keyFor")(symbol1)) 489 Realm.shared = symbol1 490 assertSame("x1", Realm.eval(realm, "Symbol.keyFor(Realm.shared)")) 491 492 var symbol3 = Realm.eval(realm, "Symbol.for('x3')") 493 assertFalse(symbol1 === symbol3) 494 assertFalse(symbol2 === symbol3) 495 assertSame(symbol3, Symbol.for("x3")) 496 assertSame("x3", Symbol.keyFor(symbol3)) 497} 498TestRegistry() 499 500 501function TestGetOwnPropertySymbolsOnPrimitives() { 502 assertEquals(Object.getOwnPropertySymbols(true), []); 503 assertEquals(Object.getOwnPropertySymbols(5000), []); 504 assertEquals(Object.getOwnPropertySymbols("OK"), []); 505} 506TestGetOwnPropertySymbolsOnPrimitives(); 507 508 509function TestComparison() { 510 function lt() { var a = Symbol(); var b = Symbol(); a < b; } 511 function gt() { var a = Symbol(); var b = Symbol(); a > b; } 512 function le() { var a = Symbol(); var b = Symbol(); a <= b; } 513 function ge() { var a = Symbol(); var b = Symbol(); a >= b; } 514 function lt_same() { var a = Symbol(); a < a; } 515 function gt_same() { var a = Symbol(); a > a; } 516 function le_same() { var a = Symbol(); a <= a; } 517 function ge_same() { var a = Symbol(); a >= a; } 518 519 var throwFuncs = [lt, gt, le, ge, lt_same, gt_same, le_same, ge_same]; 520 521 for (var f of throwFuncs) { 522 assertThrows(f, TypeError); 523 %OptimizeFunctionOnNextCall(f); 524 assertThrows(f, TypeError); 525 assertThrows(f, TypeError); 526 } 527} 528TestComparison(); 529 530 531// Make sure that throws occur in the context of the Symbol function. 532function TestContext() { 533 var r = Realm.create(); 534 var rSymbol = Realm.eval(r, "Symbol"); 535 var rError = Realm.eval(r, "TypeError"); 536 537 function verifier(symbol, error) { 538 try { 539 new symbol(); 540 } catch(e) { 541 return e.__proto__ === error.__proto__; 542 } 543 assertTrue(false); // should never get here. 544 } 545 546 assertTrue(verifier(Symbol, TypeError())); 547 assertTrue(verifier(rSymbol, rError())); 548 assertFalse(verifier(Symbol, rError())); 549 assertFalse(verifier(rSymbol, TypeError())); 550} 551TestContext(); 552 553 554function TestStringify(expected, input) { 555 assertEquals(expected, JSON.stringify(input)); 556 assertEquals(expected, JSON.stringify(input, null, 0)); 557} 558 559TestStringify(undefined, Symbol("a")); 560TestStringify('[{}]', [Object(Symbol())]); 561var symbol_wrapper = Object(Symbol("a")) 562TestStringify('{}', symbol_wrapper); 563symbol_wrapper.a = 1; 564TestStringify('{"a":1}', symbol_wrapper); 565