| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // Flags: --expose-gc --allow-natives-syntax |
| |
| var symbols = [] |
| |
| |
| // Returns true if the string is a valid |
| // serialization of Symbols added to the 'symbols' |
| // array. Adjust if you extend 'symbols' with other |
| // values. |
| function isValidSymbolString(s) { |
| return ["Symbol(66)", "Symbol()"].indexOf(s) >= 0; |
| } |
| |
| |
| // Test different forms of constructor calls. |
| function TestNew() { |
| function indirectSymbol() { return Symbol() } |
| function indirect() { return indirectSymbol() } |
| for (var i = 0; i < 2; ++i) { |
| %PrepareFunctionForOptimization(indirect); |
| for (var j = 0; j < 5; ++j) { |
| symbols.push(Symbol()) |
| symbols.push(Symbol(undefined)) |
| symbols.push(Symbol("66")) |
| symbols.push(Symbol(66)) |
| symbols.push(Symbol().valueOf()) |
| symbols.push(indirect()) |
| } |
| %OptimizeFunctionOnNextCall(indirect) |
| indirect() // Call once before GC throws away type feedback. |
| gc() // Promote existing symbols and then allocate some more. |
| } |
| assertThrows(function () { Symbol(Symbol()) }, TypeError) |
| assertThrows(function () { new Symbol(66) }, TypeError) |
| } |
| TestNew() |
| |
| |
| function TestType() { |
| for (var i in symbols) { |
| assertEquals("symbol", typeof symbols[i]) |
| assertTrue(typeof symbols[i] === "symbol") |
| assertFalse(%SymbolIsPrivate(symbols[i])) |
| } |
| } |
| TestType() |
| |
| |
| function TestPrototype() { |
| assertSame(Object.prototype, Symbol.prototype.__proto__) |
| assertSame(Symbol.prototype, Symbol().__proto__) |
| assertSame(Symbol.prototype, Object(Symbol()).__proto__) |
| for (var i in symbols) { |
| assertSame(Symbol.prototype, symbols[i].__proto__) |
| } |
| } |
| TestPrototype() |
| |
| |
| function TestConstructor() { |
| assertEquals(0, Symbol.length); |
| assertSame(Function.prototype, Symbol.__proto__) |
| assertFalse(Object === Symbol.prototype.constructor) |
| assertFalse(Symbol === Object.prototype.constructor) |
| assertSame(Symbol, Symbol.prototype.constructor) |
| assertSame(Symbol, Symbol().__proto__.constructor) |
| assertSame(Symbol, Object(Symbol()).__proto__.constructor) |
| for (var i in symbols) { |
| assertSame(Symbol, symbols[i].__proto__.constructor) |
| } |
| } |
| TestConstructor() |
| |
| |
| function TestValueOf() { |
| for (var i in symbols) { |
| assertTrue(symbols[i] === Object(symbols[i]).valueOf()) |
| assertTrue(symbols[i] === symbols[i].valueOf()) |
| assertTrue(Symbol.prototype.valueOf.call(Object(symbols[i])) === symbols[i]) |
| assertTrue(Symbol.prototype.valueOf.call(symbols[i]) === symbols[i]) |
| } |
| } |
| TestValueOf() |
| |
| |
| function TestToString() { |
| for (var i in symbols) { |
| assertThrows(function() { new String(symbols[i]) }, TypeError) |
| assertEquals(symbols[i].toString(), String(symbols[i])) |
| assertThrows(function() { symbols[i] + "" }, TypeError) |
| assertThrows(function() { String(Object(symbols[i])) }, TypeError) |
| assertTrue(isValidSymbolString(symbols[i].toString())) |
| assertTrue(isValidSymbolString(Object(symbols[i]).toString())) |
| assertTrue( |
| isValidSymbolString(Symbol.prototype.toString.call(symbols[i]))) |
| assertEquals( |
| "[object Symbol]", Object.prototype.toString.call(symbols[i])) |
| } |
| } |
| TestToString() |
| |
| |
| function TestToBoolean() { |
| for (var i in symbols) { |
| assertTrue(Boolean(Object(symbols[i]))) |
| assertFalse(!Object(symbols[i])) |
| assertTrue(Boolean(symbols[i]).valueOf()) |
| assertFalse(!symbols[i]) |
| assertTrue(!!symbols[i]) |
| assertTrue(symbols[i] && true) |
| assertFalse(!symbols[i] && false) |
| assertTrue(!symbols[i] || true) |
| assertEquals(1, symbols[i] ? 1 : 2) |
| assertEquals(2, !symbols[i] ? 1 : 2) |
| if (!symbols[i]) assertUnreachable(); |
| if (symbols[i]) {} else assertUnreachable(); |
| } |
| } |
| TestToBoolean() |
| |
| |
| function TestToNumber() { |
| for (var i in symbols) { |
| assertThrows(function() { Number(Object(symbols[i])) }, TypeError) |
| assertThrows(function() { +Object(symbols[i]) }, TypeError) |
| assertThrows(function() { Number(symbols[i]) }, TypeError) |
| assertThrows(function() { symbols[i] + 0 }, TypeError) |
| } |
| } |
| TestToNumber() |
| |
| |
| function TestEquality() { |
| // Every symbol should equal itself, and non-strictly equal its wrapper. |
| for (var i in symbols) { |
| assertSame(symbols[i], symbols[i]) |
| assertEquals(symbols[i], symbols[i]) |
| assertTrue(Object.is(symbols[i], symbols[i])) |
| assertTrue(symbols[i] === symbols[i]) |
| assertTrue(symbols[i] == symbols[i]) |
| assertFalse(symbols[i] === Object(symbols[i])) |
| assertFalse(Object(symbols[i]) === symbols[i]) |
| assertTrue(symbols[i] == Object(symbols[i])) |
| assertTrue(Object(symbols[i]) == symbols[i]) |
| assertTrue(symbols[i] === symbols[i].valueOf()) |
| assertTrue(symbols[i].valueOf() === symbols[i]) |
| assertTrue(symbols[i] == symbols[i].valueOf()) |
| assertTrue(symbols[i].valueOf() == symbols[i]) |
| assertFalse(Object(symbols[i]) === Object(symbols[i])) |
| assertEquals(Object(symbols[i]).valueOf(), Object(symbols[i]).valueOf()) |
| } |
| |
| // All symbols should be distinct. |
| for (var i = 0; i < symbols.length; ++i) { |
| for (var j = i + 1; j < symbols.length; ++j) { |
| assertFalse(Object.is(symbols[i], symbols[j])) |
| assertFalse(symbols[i] === symbols[j]) |
| assertFalse(symbols[i] == symbols[j]) |
| } |
| } |
| |
| // Symbols should not be equal to any other value (and the test terminates). |
| var values = [347, 1.275, NaN, "string", null, undefined, {}, function() {}] |
| for (var i in symbols) { |
| for (var j in values) { |
| assertFalse(symbols[i] === values[j]) |
| assertFalse(values[j] === symbols[i]) |
| assertFalse(symbols[i] == values[j]) |
| assertFalse(values[j] == symbols[i]) |
| } |
| } |
| } |
| TestEquality() |
| |
| |
| function TestGet() { |
| for (var i in symbols) { |
| assertTrue(isValidSymbolString(symbols[i].toString())) |
| assertEquals(symbols[i], symbols[i].valueOf()) |
| assertEquals(undefined, symbols[i].a) |
| assertEquals(undefined, symbols[i]["a" + "b"]) |
| assertEquals(undefined, symbols[i]["" + "1"]) |
| assertEquals(undefined, symbols[i][62]) |
| } |
| } |
| TestGet() |
| |
| |
| function TestSet() { |
| for (var i in symbols) { |
| symbols[i].toString = 0 |
| assertTrue(isValidSymbolString(symbols[i].toString())) |
| symbols[i].valueOf = 0 |
| assertEquals(symbols[i], symbols[i].valueOf()) |
| symbols[i].a = 0 |
| assertEquals(undefined, symbols[i].a) |
| symbols[i]["a" + "b"] = 0 |
| assertEquals(undefined, symbols[i]["a" + "b"]) |
| symbols[i][62] = 0 |
| assertEquals(undefined, symbols[i][62]) |
| } |
| } |
| TestSet() |
| |
| |
| // Test Symbol wrapping/boxing over non-builtins. |
| Symbol.prototype.getThisProto = function () { |
| return Object.getPrototypeOf(this); |
| } |
| function TestCall() { |
| for (var i in symbols) { |
| assertTrue(symbols[i].getThisProto() === Symbol.prototype) |
| } |
| } |
| TestCall() |
| |
| |
| function TestCollections() { |
| var set = new Set |
| var map = new Map |
| for (var i in symbols) { |
| set.add(symbols[i]) |
| map.set(symbols[i], i) |
| } |
| assertEquals(symbols.length, set.size) |
| assertEquals(symbols.length, map.size) |
| for (var i in symbols) { |
| assertTrue(set.has(symbols[i])) |
| assertTrue(map.has(symbols[i])) |
| assertEquals(i, map.get(symbols[i])) |
| } |
| for (var i in symbols) { |
| assertTrue(set.delete(symbols[i])) |
| assertTrue(map.delete(symbols[i])) |
| } |
| assertEquals(0, set.size) |
| assertEquals(0, map.size) |
| } |
| TestCollections() |
| |
| |
| |
| function TestKeySet(obj) { |
| assertTrue(%HasFastProperties(obj)) |
| // Set the even symbols via assignment. |
| for (var i = 0; i < symbols.length; i += 2) { |
| obj[symbols[i]] = i |
| // Object should remain in fast mode until too many properties were added. |
| assertTrue(%HasFastProperties(obj) || i >= 30) |
| } |
| } |
| |
| |
| function TestKeyDefine(obj) { |
| // Set the odd symbols via defineProperty (as non-enumerable). |
| for (var i = 1; i < symbols.length; i += 2) { |
| Object.defineProperty(obj, symbols[i], {value: i, configurable: true}) |
| } |
| } |
| |
| |
| function TestKeyGet(obj) { |
| var obj2 = Object.create(obj) |
| for (var i in symbols) { |
| assertEquals(i|0, obj[symbols[i]]) |
| assertEquals(i|0, obj2[symbols[i]]) |
| } |
| } |
| |
| |
| function TestKeyHas(obj) { |
| for (var i in symbols) { |
| assertTrue(symbols[i] in obj) |
| assertTrue(Object.hasOwnProperty.call(obj, symbols[i])) |
| } |
| } |
| |
| |
| function TestKeyEnum(obj) { |
| for (var name in obj) { |
| assertEquals("string", typeof name) |
| } |
| } |
| |
| |
| function TestKeyNames(obj) { |
| assertEquals(0, Object.keys(obj).length) |
| |
| var names = Object.getOwnPropertyNames(obj) |
| for (var i in names) { |
| assertEquals("string", typeof names[i]) |
| } |
| } |
| |
| |
| function TestGetOwnPropertySymbols(obj) { |
| var syms = Object.getOwnPropertySymbols(obj) |
| assertEquals(syms.length, symbols.length) |
| for (var i in syms) { |
| assertEquals("symbol", typeof syms[i]) |
| } |
| } |
| |
| |
| function TestKeyDescriptor(obj) { |
| for (var i in symbols) { |
| var desc = Object.getOwnPropertyDescriptor(obj, symbols[i]) |
| assertEquals(i|0, desc.value) |
| assertTrue(desc.configurable) |
| assertEquals(i % 2 == 0, desc.writable) |
| assertEquals(i % 2 == 0, desc.enumerable) |
| assertEquals(i % 2 == 0, |
| Object.prototype.propertyIsEnumerable.call(obj, symbols[i])) |
| } |
| } |
| |
| |
| function TestKeyDelete(obj) { |
| for (var i in symbols) { |
| delete obj[symbols[i]] |
| } |
| for (var i in symbols) { |
| assertEquals(undefined, Object.getOwnPropertyDescriptor(obj, symbols[i])) |
| } |
| } |
| |
| |
| var objs = [{}, [], Object.create({}), Object(1), new Map, function(){}] |
| |
| for (var i in objs) { |
| var obj = objs[i] |
| TestKeySet(obj) |
| TestKeyDefine(obj) |
| TestKeyGet(obj) |
| TestKeyHas(obj) |
| TestKeyEnum(obj) |
| TestKeyNames(obj) |
| TestGetOwnPropertySymbols(obj) |
| TestKeyDescriptor(obj) |
| TestKeyDelete(obj) |
| } |
| |
| |
| function TestDefineProperties() { |
| var properties = {} |
| for (var i in symbols) { |
| Object.defineProperty( |
| properties, symbols[i], {value: {value: i}, enumerable: i % 2 === 0}) |
| } |
| var o = Object.defineProperties({}, properties) |
| for (var i in symbols) { |
| assertEquals(i % 2 === 0, symbols[i] in o) |
| } |
| } |
| TestDefineProperties() |
| |
| |
| function TestCreate() { |
| var properties = {} |
| for (var i in symbols) { |
| Object.defineProperty( |
| properties, symbols[i], {value: {value: i}, enumerable: i % 2 === 0}) |
| } |
| var o = Object.create(Object.prototype, properties) |
| for (var i in symbols) { |
| assertEquals(i % 2 === 0, symbols[i] in o) |
| } |
| } |
| TestCreate() |
| |
| |
| function TestCachedKeyAfterScavenge() { |
| gc(); |
| // Keyed property lookup are cached. Hereby we assume that the keys are |
| // tenured, so that we only have to clear the cache between mark compacts, |
| // but not between scavenges. This must also apply for symbol keys. |
| var key = Symbol("key"); |
| var a = {}; |
| a[key] = "abc"; |
| |
| for (var i = 0; i < 100000; i++) { |
| a[key] += "a"; // Allocations cause a scavenge. |
| } |
| } |
| TestCachedKeyAfterScavenge(); |
| |
| |
| function TestGetOwnPropertySymbolsWithProto() { |
| // We need to be have fast properties to have insertion order for property |
| // keys. The current limit is currently 30 properties. |
| var syms = symbols.slice(0, 30); |
| var proto = {} |
| var object = Object.create(proto) |
| for (var i = 0; i < syms.length; i++) { |
| // Even on object, odd on proto. |
| if (i % 2) { |
| proto[syms[i]] = i |
| } else { |
| object[syms[i]] = i |
| } |
| } |
| |
| assertTrue(%HasFastProperties(object)); |
| |
| var objectOwnSymbols = Object.getOwnPropertySymbols(object) |
| assertEquals(objectOwnSymbols.length, syms.length / 2) |
| |
| for (var i = 0; i < objectOwnSymbols.length; i++) { |
| assertEquals(objectOwnSymbols[i], syms[i * 2]) |
| } |
| } |
| TestGetOwnPropertySymbolsWithProto() |
| |
| |
| function TestWellKnown() { |
| var symbols = [ |
| "hasInstance", |
| // TODO(rossberg): reactivate once implemented. |
| // "isConcatSpreadable", "isRegExp", |
| "iterator", /* "toStringTag", */ "unscopables" |
| ] |
| |
| for (var i in symbols) { |
| var name = symbols[i] |
| var desc = Object.getOwnPropertyDescriptor(Symbol, name) |
| assertSame("symbol", typeof desc.value) |
| assertSame("Symbol(Symbol." + name + ")", desc.value.toString()) |
| assertFalse(desc.writable) |
| assertFalse(desc.configurable) |
| assertFalse(desc.enumerable) |
| |
| assertFalse(Symbol.for("Symbol." + name) === desc.value) |
| assertTrue(Symbol.keyFor(desc.value) === undefined) |
| } |
| } |
| TestWellKnown() |
| |
| |
| function TestRegistry() { |
| var symbol1 = Symbol.for("x1") |
| var symbol2 = Symbol.for("x2") |
| assertFalse(symbol1 === symbol2) |
| |
| assertSame(symbol1, Symbol.for("x1")) |
| assertSame(symbol2, Symbol.for("x2")) |
| assertSame("x1", Symbol.keyFor(symbol1)) |
| assertSame("x2", Symbol.keyFor(symbol2)) |
| |
| assertSame(Symbol.for("1"), Symbol.for(1)) |
| assertThrows(function() { Symbol.keyFor("bla") }, TypeError) |
| assertThrows(function() { Symbol.keyFor({}) }, TypeError) |
| |
| var realm = Realm.create() |
| assertFalse(Symbol === Realm.eval(realm, "Symbol")) |
| assertFalse(Symbol.for === Realm.eval(realm, "Symbol.for")) |
| assertFalse(Symbol.keyFor === Realm.eval(realm, "Symbol.keyFor")) |
| assertSame(Symbol.create, Realm.eval(realm, "Symbol.create")) |
| assertSame(Symbol.iterator, Realm.eval(realm, "Symbol.iterator")) |
| |
| assertSame(symbol1, Realm.eval(realm, "Symbol.for")("x1")) |
| assertSame(symbol1, Realm.eval(realm, "Symbol.for('x1')")) |
| assertSame("x1", Realm.eval(realm, "Symbol.keyFor")(symbol1)) |
| Realm.shared = symbol1 |
| assertSame("x1", Realm.eval(realm, "Symbol.keyFor(Realm.shared)")) |
| |
| var symbol3 = Realm.eval(realm, "Symbol.for('x3')") |
| assertFalse(symbol1 === symbol3) |
| assertFalse(symbol2 === symbol3) |
| assertSame(symbol3, Symbol.for("x3")) |
| assertSame("x3", Symbol.keyFor(symbol3)) |
| } |
| TestRegistry() |
| |
| |
| function TestGetOwnPropertySymbolsOnPrimitives() { |
| assertEquals(Object.getOwnPropertySymbols(true), []); |
| assertEquals(Object.getOwnPropertySymbols(5000), []); |
| assertEquals(Object.getOwnPropertySymbols("OK"), []); |
| } |
| TestGetOwnPropertySymbolsOnPrimitives(); |
| |
| |
| function TestComparison() { |
| function lt() { var a = Symbol(); var b = Symbol(); a < b; } |
| function gt() { var a = Symbol(); var b = Symbol(); a > b; } |
| function le() { var a = Symbol(); var b = Symbol(); a <= b; } |
| function ge() { var a = Symbol(); var b = Symbol(); a >= b; } |
| function lt_same() { var a = Symbol(); a < a; } |
| function gt_same() { var a = Symbol(); a > a; } |
| function le_same() { var a = Symbol(); a <= a; } |
| function ge_same() { var a = Symbol(); a >= a; } |
| |
| var throwFuncs = [lt, gt, le, ge, lt_same, gt_same, le_same, ge_same]; |
| |
| for (var f of throwFuncs) { |
| %PrepareFunctionForOptimization(f); |
| assertThrows(f, TypeError); |
| %OptimizeFunctionOnNextCall(f); |
| assertThrows(f, TypeError); |
| assertThrows(f, TypeError); |
| } |
| } |
| TestComparison(); |
| |
| |
| // Make sure that throws occur in the context of the Symbol function. |
| function TestContext() { |
| var r = Realm.create(); |
| var rSymbol = Realm.eval(r, "Symbol"); |
| var rError = Realm.eval(r, "TypeError"); |
| |
| function verifier(symbol, error) { |
| try { |
| new symbol(); |
| } catch(e) { |
| return e.__proto__ === error.__proto__; |
| } |
| assertTrue(false); // should never get here. |
| } |
| |
| assertTrue(verifier(Symbol, TypeError())); |
| assertTrue(verifier(rSymbol, rError())); |
| assertFalse(verifier(Symbol, rError())); |
| assertFalse(verifier(rSymbol, TypeError())); |
| } |
| TestContext(); |
| |
| |
| function TestStringify(expected, input) { |
| assertEquals(expected, JSON.stringify(input)); |
| assertEquals(expected, JSON.stringify(input, (key, value) => value)); |
| assertEquals(JSON.stringify(input, null, "="), |
| JSON.stringify(input, (key, value) => value, "=")); |
| } |
| |
| TestStringify(undefined, Symbol("a")); |
| TestStringify('[{}]', [Object(Symbol())]); |
| var symbol_wrapper = Object(Symbol("a")) |
| TestStringify('{}', symbol_wrapper); |
| symbol_wrapper.a = 1; |
| TestStringify('{"a":1}', symbol_wrapper); |