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