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