| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| var global = this; |
| |
| var funs = { |
| Object: [ Object ], |
| Function: [ Function ], |
| Array: [ Array ], |
| String: [ String ], |
| Boolean: [ Boolean ], |
| Number: [ Number ], |
| Date: [ Date ], |
| RegExp: [ RegExp ], |
| Error: [ Error, TypeError, RangeError, SyntaxError, ReferenceError, |
| EvalError, URIError ] |
| }; |
| for (var f in funs) { |
| for (var i in funs[f]) { |
| |
| assertEquals("[object " + f + "]", |
| Object.prototype.toString.call(new funs[f][i]), |
| funs[f][i]); |
| assertEquals("[object Function]", |
| Object.prototype.toString.call(funs[f][i]), |
| funs[f][i]); |
| } |
| } |
| |
| function testToStringTag(className) { |
| // Using builtin toStringTags |
| var obj = {}; |
| obj[Symbol.toStringTag] = className; |
| assertEquals("[object " + className + "]", |
| Object.prototype.toString.call(obj)); |
| |
| // Getter throws |
| obj = {}; |
| Object.defineProperty(obj, Symbol.toStringTag, { |
| get: function() { throw className; } |
| }); |
| assertThrowsEquals(function() { |
| Object.prototype.toString.call(obj); |
| }, className); |
| |
| // Getter does not throw |
| obj = {}; |
| Object.defineProperty(obj, Symbol.toStringTag, { |
| get: function() { return className; } |
| }); |
| assertEquals("[object " + className + "]", |
| Object.prototype.toString.call(obj)); |
| |
| // Custom, non-builtin toStringTags |
| obj = {}; |
| obj[Symbol.toStringTag] = "X" + className; |
| assertEquals("[object X" + className + "]", |
| Object.prototype.toString.call(obj)); |
| |
| // With getter |
| obj = {}; |
| Object.defineProperty(obj, Symbol.toStringTag, { |
| get: function() { return "X" + className; } |
| }); |
| assertEquals("[object X" + className + "]", |
| Object.prototype.toString.call(obj)); |
| |
| // Undefined toStringTag should return [object className] |
| var obj = className === "Arguments" ? |
| (function() { return arguments; })() : new global[className]; |
| obj[Symbol.toStringTag] = undefined; |
| assertEquals("[object " + className + "]", |
| Object.prototype.toString.call(obj)); |
| |
| // With getter |
| var obj = className === "Arguments" ? |
| (function() { return arguments; })() : new global[className]; |
| Object.defineProperty(obj, Symbol.toStringTag, { |
| get: function() { return undefined; } |
| }); |
| assertEquals("[object " + className + "]", |
| Object.prototype.toString.call(obj)); |
| } |
| |
| [ |
| "Arguments", |
| "Array", |
| "Boolean", |
| "Date", |
| "Error", |
| "Function", |
| "Number", |
| "RegExp", |
| "String" |
| ].forEach(testToStringTag); |
| |
| function testToStringTagNonString(value) { |
| var obj = {}; |
| obj[Symbol.toStringTag] = value; |
| assertEquals("[object Object]", Object.prototype.toString.call(obj)); |
| |
| // With getter |
| obj = {}; |
| Object.defineProperty(obj, Symbol.toStringTag, { |
| get: function() { return value; } |
| }); |
| assertEquals("[object Object]", Object.prototype.toString.call(obj)); |
| } |
| |
| [ |
| null, |
| function() {}, |
| [], |
| {}, |
| /regexp/, |
| 42, |
| Symbol("sym"), |
| new Date(), |
| (function() { return arguments; })(), |
| true, |
| new Error("oops"), |
| new String("str") |
| ].forEach(testToStringTagNonString); |
| |
| function testObjectToStringPropertyDesc() { |
| var desc = Object.getOwnPropertyDescriptor(Object.prototype, "toString"); |
| assertTrue(desc.writable); |
| assertFalse(desc.enumerable); |
| assertTrue(desc.configurable); |
| } |
| testObjectToStringPropertyDesc(); |
| |
| function testObjectToStringOnNonStringValue(obj) { |
| Object.defineProperty(obj, Symbol.toStringTag, { value: 1 }); |
| assertEquals("[object Object]", ({}).toString.call(obj)); |
| } |
| testObjectToStringOnNonStringValue({}); |
| |
| |
| // Proxies |
| |
| function assertTag(tag, obj) { |
| assertEquals("[object " + tag + "]", Object.prototype.toString.call(obj)); |
| } |
| |
| assertTag("Object", new Proxy({}, {})); |
| assertTag("Array", new Proxy([], {})); |
| assertTag("Function", new Proxy(() => 42, {})); |
| assertTag("Foo", new Proxy(() => 42, {get() {return "Foo"}})); |
| assertTag("Function", new Proxy(() => 42, {get() {return 666}})); |
| |
| var revocable = Proxy.revocable([], {}); |
| revocable.revoke(); |
| assertThrows(() => Object.prototype.toString.call(revocable.proxy), TypeError); |
| |
| var handler = {}; |
| revocable = Proxy.revocable([], handler); |
| // The first get() call, i.e., toString() revokes the proxy |
| handler.get = () => revocable.revoke(); |
| assertEquals("[object Array]", Object.prototype.toString.call(revocable.proxy)); |
| assertThrows(() => Object.prototype.toString.call(revocable.proxy), TypeError); |
| |
| revocable = Proxy.revocable([], handler); |
| handler.get = () => {revocable.revoke(); return "value";}; |
| assertEquals("[object value]", Object.prototype.toString.call(revocable.proxy)); |
| assertThrows(() => Object.prototype.toString.call(revocable.proxy), TypeError); |
| |
| |
| revocable = Proxy.revocable(function() {}, handler); |
| handler.get = () => revocable.revoke(); |
| assertEquals("[object Function]", Object.prototype.toString.call(revocable.proxy)); |
| assertThrows(() => Object.prototype.toString.call(revocable.proxy), TypeError); |
| |
| function* gen() { yield 1; } |
| |
| assertTag("GeneratorFunction", gen); |
| Object.defineProperty(gen, Symbol.toStringTag, {writable: true}); |
| gen[Symbol.toStringTag] = "different string"; |
| assertTag("different string", gen); |
| gen[Symbol.toStringTag] = 1; |
| assertTag("Function", gen); |
| |
| function overwriteToStringTagWithNonStringValue(tag, obj) { |
| assertTag(tag, obj); |
| |
| Object.defineProperty(obj, Symbol.toStringTag, { |
| configurable: true, |
| value: "different string" |
| }); |
| assertTag("different string", obj); |
| |
| testObjectToStringOnNonStringValue(obj); |
| } |
| |
| overwriteToStringTagWithNonStringValue("global", global); |
| overwriteToStringTagWithNonStringValue("Generator", gen()); |
| |
| var arrayBuffer = new ArrayBuffer(); |
| overwriteToStringTagWithNonStringValue("ArrayBuffer", arrayBuffer); |
| overwriteToStringTagWithNonStringValue("DataView", new DataView(arrayBuffer)); |
| |
| overwriteToStringTagWithNonStringValue("Int8Array", new Int8Array()); |
| overwriteToStringTagWithNonStringValue("Uint8Array", new Uint8Array()); |
| overwriteToStringTagWithNonStringValue("Uint8ClampedArray", |
| new Uint8ClampedArray()); |
| overwriteToStringTagWithNonStringValue("Int16Array", new Int16Array()); |
| overwriteToStringTagWithNonStringValue("Uint16Array", new Uint16Array()); |
| overwriteToStringTagWithNonStringValue("Int32Array", new Int32Array()); |
| overwriteToStringTagWithNonStringValue("Uint32Array", new Uint32Array()); |
| overwriteToStringTagWithNonStringValue("Float32Array", new Float32Array()); |
| overwriteToStringTagWithNonStringValue("Float64Array", new Float64Array()); |
| |
| var set = new Set(); |
| var map = new Map(); |
| |
| overwriteToStringTagWithNonStringValue("Set", set); |
| overwriteToStringTagWithNonStringValue("Map", map); |
| |
| overwriteToStringTagWithNonStringValue("Set Iterator", set[Symbol.iterator]()); |
| overwriteToStringTagWithNonStringValue("Map Iterator", map[Symbol.iterator]()); |
| |
| overwriteToStringTagWithNonStringValue("WeakSet", new WeakSet()); |
| overwriteToStringTagWithNonStringValue("WeakMap", new WeakMap()); |
| |
| overwriteToStringTagWithNonStringValue("Promise", new Promise(function() {})); |