From a1839d3c72332ac6e84ea17155e60ba81ff204d5 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Fri, 16 Feb 2018 19:19:53 +0700 Subject: [PATCH] add own toString method to instances with @@toStringTag in non-global version, fixes #199 --- .../override/internals/collection.js | 2 +- .../override/internals/set-to-string-tag.js | 16 ++++++++++++++++ .../internals/create-iterator-constructor.js | 2 +- packages/core-js/internals/define-iterator.js | 2 +- packages/core-js/internals/object-to-string.js | 12 ++++++++++++ packages/core-js/internals/set-to-string-tag.js | 6 +++--- packages/core-js/modules/es.object.to-string.js | 12 +++--------- packages/core-js/modules/es.promise.js | 2 +- tests/pure/es.array.iterator.js | 1 + tests/pure/es.map.js | 2 ++ tests/pure/es.promise.js | 1 + tests/pure/es.set.js | 2 ++ tests/pure/es.string.iterator.js | 1 + tests/pure/es.weak-map.js | 1 + tests/pure/es.weak-set.js | 1 + tests/pure/esnext.string.code-points.js | 1 + tests/pure/esnext.string.match-all.js | 3 +++ tests/tests/es.array.iterator.js | 1 + tests/tests/es.map.js | 2 ++ tests/tests/es.promise.js | 1 + tests/tests/es.set.js | 2 ++ tests/tests/es.string.iterator.js | 1 + tests/tests/es.weak-map.js | 1 + tests/tests/es.weak-set.js | 1 + tests/tests/esnext.string.code-points.js | 1 + tests/tests/esnext.string.match-all.js | 2 ++ 26 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 packages/core-js-pure/override/internals/set-to-string-tag.js create mode 100644 packages/core-js/internals/object-to-string.js diff --git a/packages/core-js-pure/override/internals/collection.js b/packages/core-js-pure/override/internals/collection.js index 3acdd1577bda..da7958da7e37 100644 --- a/packages/core-js-pure/override/internals/collection.js +++ b/packages/core-js-pure/override/internals/collection.js @@ -46,7 +46,7 @@ module.exports = function (NAME, wrapper, common, IS_MAP, IS_WEAK) { }); } - setToStringTag(C, NAME); + setToStringTag(C, NAME, false, true); exported[NAME] = C; $export({ global: true, wrap: true, forced: true }, exported); diff --git a/packages/core-js-pure/override/internals/set-to-string-tag.js b/packages/core-js-pure/override/internals/set-to-string-tag.js new file mode 100644 index 000000000000..f73db525890d --- /dev/null +++ b/packages/core-js-pure/override/internals/set-to-string-tag.js @@ -0,0 +1,16 @@ +var defineProperty = require('../internals/object-define-property').f; +var hide = require('../internals/hide'); +var has = require('../internals/has'); +var TO_STRING_TAG = require('../internals/well-known-symbol')('toStringTag'); +var toString = require('../internals/object-to-string'); +var METHOD_REQUIRED = toString !== ({}).toString; + +module.exports = function (it, TAG, STATIC, SET_METHOD) { + if (it) { + var target = STATIC ? it : it.prototype; + if (!has(target, TO_STRING_TAG)) { + defineProperty(target, TO_STRING_TAG, { configurable: true, value: TAG }); + } + if (SET_METHOD && METHOD_REQUIRED) hide(target, 'toString', toString); + } +}; diff --git a/packages/core-js/internals/create-iterator-constructor.js b/packages/core-js/internals/create-iterator-constructor.js index 1715c8eda751..8fc0fe9ab937 100644 --- a/packages/core-js/internals/create-iterator-constructor.js +++ b/packages/core-js/internals/create-iterator-constructor.js @@ -10,6 +10,6 @@ require('../internals/hide')(IteratorPrototype, ITERATOR, function () { return t module.exports = function (Constructor, NAME, next) { Constructor.prototype = create(IteratorPrototype, { next: createPropertyDescriptor(1, next) }); - setToStringTag(Constructor, NAME + ' Iterator'); + setToStringTag(Constructor, NAME + ' Iterator', false, true); return Constructor; }; diff --git a/packages/core-js/internals/define-iterator.js b/packages/core-js/internals/define-iterator.js index 74a5a3014399..e17859f8867f 100644 --- a/packages/core-js/internals/define-iterator.js +++ b/packages/core-js/internals/define-iterator.js @@ -39,7 +39,7 @@ module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCE IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { // Set @@toStringTag to native iterators - setToStringTag(IteratorPrototype, TAG, true); + setToStringTag(IteratorPrototype, TAG, true, true); // fix for some old engines if (!IS_PURE && !has(IteratorPrototype, ITERATOR)) hide(IteratorPrototype, ITERATOR, returnThis); } diff --git a/packages/core-js/internals/object-to-string.js b/packages/core-js/internals/object-to-string.js new file mode 100644 index 000000000000..3dda0145125f --- /dev/null +++ b/packages/core-js/internals/object-to-string.js @@ -0,0 +1,12 @@ +'use strict'; +var classof = require('../internals/classof'); +var TO_STRING_TAG = require('../internals/well-known-symbol')('toStringTag'); +var test = {}; + +test[TO_STRING_TAG] = 'z'; + +// `Object.prototype.toString` method implementation +// https://tc39.github.io/ecma262/#sec-object.prototype.tostring +module.exports = String(test) !== '[object z]' ? function toString() { + return '[object ' + classof(this) + ']'; +} : test.toString; diff --git a/packages/core-js/internals/set-to-string-tag.js b/packages/core-js/internals/set-to-string-tag.js index 1d7cec5d417c..0999ec837d4b 100644 --- a/packages/core-js/internals/set-to-string-tag.js +++ b/packages/core-js/internals/set-to-string-tag.js @@ -2,8 +2,8 @@ var defineProperty = require('../internals/object-define-property').f; var has = require('../internals/has'); var TO_STRING_TAG = require('../internals/well-known-symbol')('toStringTag'); -module.exports = function (it, tag, stat) { - if (it && !has(it = stat ? it : it.prototype, TO_STRING_TAG)) { - defineProperty(it, TO_STRING_TAG, { configurable: true, value: tag }); +module.exports = function (it, TAG, STATIC) { + if (it && !has(it = STATIC ? it : it.prototype, TO_STRING_TAG)) { + defineProperty(it, TO_STRING_TAG, { configurable: true, value: TAG }); } }; diff --git a/packages/core-js/modules/es.object.to-string.js b/packages/core-js/modules/es.object.to-string.js index 4afacc611784..fed6588c36c7 100644 --- a/packages/core-js/modules/es.object.to-string.js +++ b/packages/core-js/modules/es.object.to-string.js @@ -1,14 +1,8 @@ 'use strict'; -var classof = require('../internals/classof'); -var TO_STRING_TAG = require('../internals/well-known-symbol')('toStringTag'); -var test = {}; - -test[TO_STRING_TAG] = 'z'; +var toString = require('../internals/object-to-string'); // `Object.prototype.toString` method // https://tc39.github.io/ecma262/#sec-object.prototype.tostring -if (String(test) !== '[object z]') { - require('../internals/redefine')(Object.prototype, 'toString', function toString() { - return '[object ' + classof(this) + ']'; - }, true); +if (toString !== ({}).toString) { + require('../internals/redefine')(Object.prototype, 'toString', toString, true); } diff --git a/packages/core-js/modules/es.promise.js b/packages/core-js/modules/es.promise.js index 4aeb30003dd4..54fa89ba7b76 100644 --- a/packages/core-js/modules/es.promise.js +++ b/packages/core-js/modules/es.promise.js @@ -239,7 +239,7 @@ if (!USE_NATIVE) { } $export({ global: true, wrap: true, forced: !USE_NATIVE }, { Promise: $Promise }); -require('../internals/set-to-string-tag')($Promise, PROMISE); +require('../internals/set-to-string-tag')($Promise, PROMISE, false, true); require('../internals/set-species')(PROMISE); Wrapper = require('../internals/path')[PROMISE]; diff --git a/tests/pure/es.array.iterator.js b/tests/pure/es.array.iterator.js index 786c49f24849..c10b2843805b 100644 --- a/tests/pure/es.array.iterator.js +++ b/tests/pure/es.array.iterator.js @@ -7,6 +7,7 @@ QUnit.test('Array#@@iterator', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'Array Iterator'); + assert.strictEqual(String(iterator), '[object Array Iterator]'); assert.deepEqual(iterator.next(), { value: 'q', done: false, diff --git a/tests/pure/es.map.js b/tests/pure/es.map.js index f09a939c9579..cc533b022c8f 100644 --- a/tests/pure/es.map.js +++ b/tests/pure/es.map.js @@ -266,6 +266,7 @@ QUnit.test('Map & -0', assert => { QUnit.test('Map#@@toStringTag', assert => { assert.strictEqual(Map.prototype[Symbol.toStringTag], 'Map', 'Map::@@toStringTag is `Map`'); + assert.strictEqual(String(new Map()), '[object Map]', 'correct stringification'); }); QUnit.test('Map Iterator', assert => { @@ -382,6 +383,7 @@ QUnit.test('Map#@@iterator', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'Map Iterator'); + assert.strictEqual(String(iterator), '[object Map Iterator]'); assert.deepEqual(iterator.next(), { value: ['a', 'q'], done: false, diff --git a/tests/pure/es.promise.js b/tests/pure/es.promise.js index 0f9fff336132..977631c91bb8 100644 --- a/tests/pure/es.promise.js +++ b/tests/pure/es.promise.js @@ -159,6 +159,7 @@ QUnit.test('Promise#catch', assert => { QUnit.test('Promise#@@toStringTag', assert => { assert.ok(Promise.prototype[Symbol.toStringTag] === 'Promise', 'Promise::@@toStringTag is `Promise`'); + assert.strictEqual(String(new Promise(() => { /* empty */ })), '[object Promise]', 'correct stringification'); }); QUnit.test('Promise.all', assert => { diff --git a/tests/pure/es.set.js b/tests/pure/es.set.js index bdc66146cad9..3ba6da50ec99 100644 --- a/tests/pure/es.set.js +++ b/tests/pure/es.set.js @@ -260,6 +260,7 @@ QUnit.test('Set & -0', assert => { QUnit.test('Set#@@toStringTag', assert => { assert.strictEqual(Set.prototype[Symbol.toStringTag], 'Set', 'Set::@@toStringTag is `Set`'); + assert.strictEqual(String(new Set()), '[object Set]', 'correct stringification'); }); QUnit.test('Set Iterator', assert => { @@ -376,6 +377,7 @@ QUnit.test('Set#@@iterator', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'Set Iterator'); + assert.strictEqual(String(iterator), '[object Set Iterator]'); assert.deepEqual(iterator.next(), { value: 'q', done: false, diff --git a/tests/pure/es.string.iterator.js b/tests/pure/es.string.iterator.js index 37168e04ea16..44a357686198 100644 --- a/tests/pure/es.string.iterator.js +++ b/tests/pure/es.string.iterator.js @@ -5,6 +5,7 @@ QUnit.test('String#@@iterator', assert => { let iterator = getIterator('qwe'); assert.isIterator(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'String Iterator'); + assert.strictEqual(String(iterator), '[object String Iterator]'); assert.deepEqual(iterator.next(), { value: 'q', done: false, diff --git a/tests/pure/es.weak-map.js b/tests/pure/es.weak-map.js index be8936aacf11..cc033710a958 100644 --- a/tests/pure/es.weak-map.js +++ b/tests/pure/es.weak-map.js @@ -113,4 +113,5 @@ QUnit.test('WeakMap#set', assert => { QUnit.test('WeakMap#@@toStringTag', assert => { assert.strictEqual(WeakMap.prototype[Symbol.toStringTag], 'WeakMap', 'WeakMap::@@toStringTag is `WeakMap`'); + assert.strictEqual(String(new WeakMap()), '[object WeakMap]', 'correct stringification'); }); diff --git a/tests/pure/es.weak-set.js b/tests/pure/es.weak-set.js index bbf06d48a47f..72190819346e 100644 --- a/tests/pure/es.weak-set.js +++ b/tests/pure/es.weak-set.js @@ -90,4 +90,5 @@ QUnit.test('WeakSet#has', assert => { QUnit.test('WeakSet::@@toStringTag', assert => { assert.strictEqual(WeakSet.prototype[Symbol.toStringTag], 'WeakSet', 'WeakSet::@@toStringTag is `WeakSet`'); + assert.strictEqual(String(new WeakSet()), '[object WeakSet]', 'correct stringification'); }); diff --git a/tests/pure/esnext.string.code-points.js b/tests/pure/esnext.string.code-points.js index 1e1051acbdf1..7e33d7748cd5 100644 --- a/tests/pure/esnext.string.code-points.js +++ b/tests/pure/esnext.string.code-points.js @@ -7,6 +7,7 @@ QUnit.test('String#codePoints', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'String Iterator'); + assert.strictEqual(String(iterator), '[object String Iterator]'); assert.deepEqual(iterator.next(), { value: 113, done: false, diff --git a/tests/pure/esnext.string.match-all.js b/tests/pure/esnext.string.match-all.js index b094a5aadeec..443014b76c26 100644 --- a/tests/pure/esnext.string.match-all.js +++ b/tests/pure/esnext.string.match-all.js @@ -1,6 +1,7 @@ import { STRICT } from '../helpers/constants'; import matchAll from 'core-js-pure/features/string/match-all'; +import Symbol from 'core-js-pure/features/symbol'; import assign from 'core-js-pure/features/object/assign'; QUnit.test('String#matchAll', assert => { @@ -41,6 +42,8 @@ QUnit.test('String#matchAll', assert => { let iterator = matchAll('1111a2b3cccc', /(\d)(\D)/g); assert.isIterator(iterator); assert.isIterable(iterator); + assert.strictEqual(iterator[Symbol.toStringTag], 'RegExp String Iterator'); + assert.strictEqual(String(iterator), '[object RegExp String Iterator]'); assert.deepEqual(iterator.next(), { value: assign(['1a', '1', 'a'], { input: '1111a2b3cccc', diff --git a/tests/tests/es.array.iterator.js b/tests/tests/es.array.iterator.js index c0daaa0f4c0a..3ab0c35cacd7 100644 --- a/tests/tests/es.array.iterator.js +++ b/tests/tests/es.array.iterator.js @@ -13,6 +13,7 @@ QUnit.test('Array#keys', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'Array Iterator'); + assert.strictEqual(String(iterator), '[object Array Iterator]'); assert.deepEqual(iterator.next(), { value: 0, done: false, diff --git a/tests/tests/es.map.js b/tests/tests/es.map.js index 58e637ac4361..52d4224ceb70 100644 --- a/tests/tests/es.map.js +++ b/tests/tests/es.map.js @@ -301,6 +301,7 @@ QUnit.test('Map & -0', assert => { QUnit.test('Map#@@toStringTag', assert => { assert.strictEqual(Map.prototype[Symbol.toStringTag], 'Map', 'Map::@@toStringTag is `Map`'); + assert.strictEqual(String(new Map()), '[object Map]', 'correct stringification'); }); QUnit.test('Map Iterator', assert => { @@ -438,6 +439,7 @@ QUnit.test('Map#@@iterator', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'Map Iterator'); + assert.strictEqual(String(iterator), '[object Map Iterator]'); assert.deepEqual(iterator.next(), { value: ['a', 'q'], done: false, diff --git a/tests/tests/es.promise.js b/tests/tests/es.promise.js index bb3c2e2481f4..fe40ee643534 100644 --- a/tests/tests/es.promise.js +++ b/tests/tests/es.promise.js @@ -167,6 +167,7 @@ QUnit.test('Promise#catch', assert => { QUnit.test('Promise#@@toStringTag', assert => { assert.ok(Promise.prototype[Symbol.toStringTag] === 'Promise', 'Promise::@@toStringTag is `Promise`'); + assert.strictEqual(String(new Promise(() => { /* empty */ })), '[object Promise]', 'correct stringification'); }); QUnit.test('Promise.all', assert => { diff --git a/tests/tests/es.set.js b/tests/tests/es.set.js index 2e0277ed4e28..9cac2d2862c4 100644 --- a/tests/tests/es.set.js +++ b/tests/tests/es.set.js @@ -284,6 +284,7 @@ QUnit.test('Set & -0', assert => { QUnit.test('Set#@@toStringTag', assert => { assert.strictEqual(Set.prototype[Symbol.toStringTag], 'Set', 'Set::@@toStringTag is `Set`'); + assert.strictEqual(String(new Set()), '[object Set]', 'correct stringification'); }); QUnit.test('Set Iterator', assert => { @@ -419,6 +420,7 @@ QUnit.test('Set#@@iterator', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'Set Iterator'); + assert.strictEqual(String(iterator), '[object Set Iterator]'); assert.deepEqual(iterator.next(), { value: 'q', done: false, diff --git a/tests/tests/es.string.iterator.js b/tests/tests/es.string.iterator.js index 5ecf31fd5ffc..c265b9e3fc02 100644 --- a/tests/tests/es.string.iterator.js +++ b/tests/tests/es.string.iterator.js @@ -8,6 +8,7 @@ QUnit.test('String#@@iterator', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'String Iterator'); + assert.strictEqual(String(iterator), '[object String Iterator]'); assert.deepEqual(iterator.next(), { value: 'q', done: false, diff --git a/tests/tests/es.weak-map.js b/tests/tests/es.weak-map.js index 2f8af1b0426c..5f1b48f03bea 100644 --- a/tests/tests/es.weak-map.js +++ b/tests/tests/es.weak-map.js @@ -132,4 +132,5 @@ QUnit.test('WeakMap#set', assert => { QUnit.test('WeakMap#@@toStringTag', assert => { assert.strictEqual(WeakMap.prototype[Symbol.toStringTag], 'WeakMap', 'WeakMap::@@toStringTag is `WeakMap`'); + assert.strictEqual(String(new WeakMap()), '[object WeakMap]', 'correct stringification'); }); diff --git a/tests/tests/es.weak-set.js b/tests/tests/es.weak-set.js index 8ce9936a11bc..ed85f6ff679d 100644 --- a/tests/tests/es.weak-set.js +++ b/tests/tests/es.weak-set.js @@ -104,4 +104,5 @@ QUnit.test('WeakSet#has', assert => { QUnit.test('WeakSet::@@toStringTag', assert => { assert.strictEqual(WeakSet.prototype[Symbol.toStringTag], 'WeakSet', 'WeakSet::@@toStringTag is `WeakSet`'); + assert.strictEqual(String(new WeakSet()), '[object WeakSet]', 'correct stringification'); }); diff --git a/tests/tests/esnext.string.code-points.js b/tests/tests/esnext.string.code-points.js index 9d17199cd229..b8bbc72795c1 100644 --- a/tests/tests/esnext.string.code-points.js +++ b/tests/tests/esnext.string.code-points.js @@ -13,6 +13,7 @@ QUnit.test('String#codePoints', assert => { assert.isIterator(iterator); assert.isIterable(iterator); assert.strictEqual(iterator[Symbol.toStringTag], 'String Iterator'); + assert.strictEqual(String(iterator), '[object String Iterator]'); assert.deepEqual(iterator.next(), { value: 113, done: false, diff --git a/tests/tests/esnext.string.match-all.js b/tests/tests/esnext.string.match-all.js index 1e43bfeec65b..71794659c75b 100644 --- a/tests/tests/esnext.string.match-all.js +++ b/tests/tests/esnext.string.match-all.js @@ -42,6 +42,8 @@ QUnit.test('String#matchAll', assert => { let iterator = '1111a2b3cccc'.matchAll(/(\d)(\D)/g); assert.isIterator(iterator); assert.isIterable(iterator); + assert.strictEqual(iterator[Symbol.toStringTag], 'RegExp String Iterator'); + assert.strictEqual(String(iterator), '[object RegExp String Iterator]'); assert.deepEqual(iterator.next(), { value: assign(['1a', '1', 'a'], { input: '1111a2b3cccc',