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)"].indexOf(s) >= 0; 39 } 40 41 42 // Test different forms of constructor calls, all equivalent. 43 function TestNew() { 44 for (var i = 0; i < 2; ++i) { 45 for (var j = 0; j < 5; ++j) { 46 symbols.push(%CreatePrivateSymbol("66")) 47 symbols.push(Object(%CreatePrivateSymbol("66")).valueOf()) 48 } 49 gc() // Promote existing symbols and then allocate some more. 50 } 51 } 52 TestNew() 53 54 55 function TestType() { 56 for (var i in symbols) { 57 assertEquals("symbol", typeof symbols[i]) 58 assertTrue(typeof symbols[i] === "symbol") 59 assertTrue(%SymbolIsPrivate(symbols[i])) 60 assertEquals(null, %_ClassOf(symbols[i])) 61 assertEquals("Symbol", %_ClassOf(Object(symbols[i]))) 62 } 63 } 64 TestType() 65 66 67 function TestPrototype() { 68 for (var i in symbols) { 69 assertSame(Symbol.prototype, symbols[i].__proto__) 70 } 71 } 72 TestPrototype() 73 74 75 function TestConstructor() { 76 for (var i in symbols) { 77 assertSame(Symbol, symbols[i].__proto__.constructor) 78 assertSame(Symbol, Object(symbols[i]).__proto__.constructor) 79 } 80 } 81 TestConstructor() 82 83 84 function TestToString() { 85 for (var i in symbols) { 86 assertThrows(function() {new String(symbols[i]) }, TypeError) 87 assertEquals(symbols[i].toString(), String(symbols[i])) 88 assertThrows(function() { symbols[i] + "" }, TypeError) 89 assertTrue(isValidSymbolString(symbols[i].toString())) 90 assertTrue(isValidSymbolString(Object(symbols[i]).toString())) 91 assertTrue(isValidSymbolString(Symbol.prototype.toString.call(symbols[i]))) 92 assertEquals( 93 "[object Symbol]", Object.prototype.toString.call(symbols[i])) 94 } 95 } 96 TestToString() 97 98 99 function TestToBoolean() { 100 for (var i in symbols) { 101 assertTrue(Boolean(symbols[i]).valueOf()) 102 assertFalse(!symbols[i]) 103 assertTrue(!!symbols[i]) 104 assertTrue(symbols[i] && true) 105 assertFalse(!symbols[i] && false) 106 assertTrue(!symbols[i] || true) 107 assertEquals(1, symbols[i] ? 1 : 2) 108 assertEquals(2, !symbols[i] ? 1 : 2) 109 if (!symbols[i]) assertUnreachable(); 110 if (symbols[i]) {} else assertUnreachable(); 111 } 112 } 113 TestToBoolean() 114 115 116 function TestToNumber() { 117 for (var i in symbols) { 118 assertThrows(function() { Number(symbols[i]); }, TypeError); 119 assertThrows(function() { symbols[i] + 0; }, TypeError); 120 } 121 } 122 TestToNumber() 123 124 125 function TestEquality() { 126 // Every symbol should equal itself, and non-strictly equal its wrapper. 127 for (var i in symbols) { 128 assertSame(symbols[i], symbols[i]) 129 assertEquals(symbols[i], symbols[i]) 130 assertTrue(Object.is(symbols[i], symbols[i])) 131 assertTrue(symbols[i] === symbols[i]) 132 assertTrue(symbols[i] == symbols[i]) 133 assertFalse(symbols[i] === Object(symbols[i])) 134 assertFalse(Object(symbols[i]) === symbols[i]) 135 assertTrue(symbols[i] == Object(symbols[i])) 136 assertTrue(Object(symbols[i]) == symbols[i]) 137 assertTrue(symbols[i] === symbols[i].valueOf()) 138 assertTrue(symbols[i].valueOf() === symbols[i]) 139 assertTrue(symbols[i] == symbols[i].valueOf()) 140 assertTrue(symbols[i].valueOf() == symbols[i]) 141 } 142 143 // All symbols should be distinct. 144 for (var i = 0; i < symbols.length; ++i) { 145 for (var j = i + 1; j < symbols.length; ++j) { 146 assertFalse(Object.is(symbols[i], symbols[j])) 147 assertFalse(symbols[i] === symbols[j]) 148 assertFalse(symbols[i] == symbols[j]) 149 } 150 } 151 152 // Symbols should not be equal to any other value (and the test terminates). 153 var values = [347, 1.275, NaN, "string", null, undefined, {}, function() {}] 154 for (var i in symbols) { 155 for (var j in values) { 156 assertFalse(symbols[i] === values[j]) 157 assertFalse(values[j] === symbols[i]) 158 assertFalse(symbols[i] == values[j]) 159 assertFalse(values[j] == symbols[i]) 160 } 161 } 162 } 163 TestEquality() 164 165 166 function TestGet() { 167 for (var i in symbols) { 168 assertTrue(isValidSymbolString(symbols[i].toString())) 169 assertEquals(symbols[i], symbols[i].valueOf()) 170 assertEquals(undefined, symbols[i].a) 171 assertEquals(undefined, symbols[i]["a" + "b"]) 172 assertEquals(undefined, symbols[i]["" + "1"]) 173 assertEquals(undefined, symbols[i][62]) 174 } 175 } 176 TestGet() 177 178 179 function TestSet() { 180 for (var i in symbols) { 181 symbols[i].toString = 0 182 assertTrue(isValidSymbolString(symbols[i].toString())) 183 symbols[i].valueOf = 0 184 assertEquals(symbols[i], symbols[i].valueOf()) 185 symbols[i].a = 0 186 assertEquals(undefined, symbols[i].a) 187 symbols[i]["a" + "b"] = 0 188 assertEquals(undefined, symbols[i]["a" + "b"]) 189 symbols[i][62] = 0 190 assertEquals(undefined, symbols[i][62]) 191 } 192 } 193 TestSet() 194 195 196 function TestCollections() { 197 var set = new Set 198 var map = new Map 199 for (var i in symbols) { 200 set.add(symbols[i]) 201 map.set(symbols[i], i) 202 } 203 assertEquals(symbols.length, set.size) 204 assertEquals(symbols.length, map.size) 205 for (var i in symbols) { 206 assertTrue(set.has(symbols[i])) 207 assertTrue(map.has(symbols[i])) 208 assertEquals(i, map.get(symbols[i])) 209 } 210 for (var i in symbols) { 211 assertTrue(set.delete(symbols[i])) 212 assertTrue(map.delete(symbols[i])) 213 } 214 assertEquals(0, set.size) 215 assertEquals(0, map.size) 216 } 217 TestCollections() 218 219 220 221 function TestKeySet(obj) { 222 assertTrue(%HasFastProperties(obj)) 223 // Set the even symbols via assignment. 224 for (var i = 0; i < symbols.length; i += 2) { 225 obj[symbols[i]] = i 226 // Object should remain in fast mode until too many properties were added. 227 assertTrue(%HasFastProperties(obj) || i >= 30) 228 } 229 } 230 231 232 function TestKeyDefine(obj) { 233 // Set the odd symbols via defineProperty (as non-enumerable). 234 for (var i = 1; i < symbols.length; i += 2) { 235 Object.defineProperty(obj, symbols[i], {value: i, configurable: true}) 236 } 237 } 238 239 240 function TestKeyGet(obj) { 241 var obj2 = Object.create(obj) 242 for (var i in symbols) { 243 assertEquals(i|0, obj[symbols[i]]) 244 // Private symbols key own-properties. 245 assertEquals(undefined, obj2[symbols[i]]) 246 } 247 } 248 249 250 function TestKeyHas() { 251 for (var i in symbols) { 252 assertTrue(symbols[i] in obj) 253 assertTrue(Object.hasOwnProperty.call(obj, symbols[i])) 254 } 255 } 256 257 258 function TestKeyEnum(obj) { 259 for (var name in obj) { 260 assertEquals("string", typeof name) 261 } 262 } 263 264 265 function TestKeyNames(obj) { 266 assertEquals(0, Object.keys(obj).length) 267 268 var names = Object.getOwnPropertyNames(obj) 269 for (var i in names) { 270 assertEquals("string", typeof names[i]) 271 } 272 } 273 274 275 function TestKeyDescriptor(obj) { 276 for (var i in symbols) { 277 var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]); 278 assertEquals(i|0, desc.value) 279 assertTrue(desc.configurable) 280 assertEquals(i % 2 == 0, desc.writable) 281 assertEquals(i % 2 == 0, desc.enumerable) 282 assertEquals(i % 2 == 0, 283 Object.prototype.propertyIsEnumerable.call(obj, symbols[i])) 284 } 285 } 286 287 288 function TestKeyDelete(obj) { 289 for (var i in symbols) { 290 delete obj[symbols[i]] 291 } 292 for (var i in symbols) { 293 assertEquals(undefined, Object.getOwnPropertyDescriptor(obj, symbols[i])) 294 } 295 } 296 297 298 var objs = [{}, [], Object.create(null), Object(1), new Map, function(){}] 299 300 for (var i in objs) { 301 var obj = objs[i] 302 TestKeySet(obj) 303 TestKeyDefine(obj) 304 TestKeyGet(obj) 305 TestKeyHas(obj) 306 TestKeyEnum(obj) 307 TestKeyNames(obj) 308 TestKeyDescriptor(obj) 309 TestKeyDelete(obj) 310 } 311 312 313 function TestCachedKeyAfterScavenge() { 314 gc(); 315 // Keyed property lookup are cached. Hereby we assume that the keys are 316 // tenured, so that we only have to clear the cache between mark compacts, 317 // but not between scavenges. This must also apply for symbol keys. 318 var key = Symbol("key"); 319 var a = {}; 320 a[key] = "abc"; 321 322 for (var i = 0; i < 100000; i++) { 323 a[key] += "a"; // Allocations cause a scavenge. 324 } 325 } 326 TestCachedKeyAfterScavenge(); 327 328 329 function TestGetOwnPropertySymbols() { 330 var privateSymbol = %CreatePrivateSymbol("private") 331 var publicSymbol = Symbol() 332 var publicSymbol2 = Symbol() 333 var obj = {} 334 obj[publicSymbol] = 1 335 obj[privateSymbol] = 2 336 obj[publicSymbol2] = 3 337 var syms = Object.getOwnPropertySymbols(obj) 338 assertEquals(syms, [publicSymbol, publicSymbol2]) 339 } 340 TestGetOwnPropertySymbols() 341 342 343 function TestSealAndFreeze(freeze) { 344 var sym = %CreatePrivateSymbol("private") 345 var obj = {} 346 obj[sym] = 1 347 freeze(obj) 348 obj[sym] = 2 349 assertEquals(2, obj[sym]) 350 assertTrue(delete obj[sym]) 351 assertEquals(undefined, obj[sym]) 352 } 353 TestSealAndFreeze(Object.seal) 354 TestSealAndFreeze(Object.freeze) 355 TestSealAndFreeze(Object.preventExtensions) 356 357 358 var s = %CreatePrivateSymbol("s"); 359 var s1 = %CreatePrivateSymbol("s1"); 360 361 function TestSimple() { 362 var p = {} 363 p[s] = "moo"; 364 365 var o = Object.create(p); 366 367 assertEquals(undefined, o[s]); 368 assertEquals("moo", p[s]); 369 370 o[s] = "bow-wow"; 371 assertEquals("bow-wow", o[s]); 372 assertEquals("moo", p[s]); 373 } 374 TestSimple(); 375 376 377 function TestICs() { 378 var p = {} 379 p[s] = "moo"; 380 381 382 var o = Object.create(p); 383 o[s1] = "bow-wow"; 384 function checkNonOwn(o) { 385 assertEquals(undefined, o[s]); 386 assertEquals("bow-wow", o[s1]); 387 } 388 389 checkNonOwn(o); 390 391 // Test monomorphic/optimized. 392 for (var i = 0; i < 1000; i++) { 393 checkNonOwn(o); 394 } 395 396 // Test non-monomorphic. 397 for (var i = 0; i < 1000; i++) { 398 var oNew = Object.create(p); 399 oNew["s" + i] = i; 400 oNew[s1] = "bow-wow"; 401 checkNonOwn(oNew); 402 } 403 } 404 TestICs(); 405