Home | History | Annotate | Download | only in harmony
      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