From 4707a2686c85aa6f11223f7e669b8ac4a98a410e Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Wed, 18 Sep 2019 20:25:17 +0700 Subject: [PATCH 1/4] update `Map#updateOrInsert` -> `Map#upsert` per https://github.com/thumbsupep/proposal-upsert --- CHANGELOG.md | 1 + README.md | 14 ++++--- packages/core-js-compat/src/data.js | 2 + .../core-js-compat/src/modules-by-versions.js | 1 + packages/core-js/features/map/index.js | 2 + .../core-js/features/map/update-or-insert.js | 1 + packages/core-js/features/map/upsert.js | 5 +++ packages/core-js/internals/map-upsert.js | 14 +++++++ .../modules/esnext.map.update-or-insert.js | 17 +++------ packages/core-js/modules/esnext.map.upsert.js | 10 +++++ .../core-js/proposals/map-update-or-insert.js | 3 +- packages/core-js/proposals/map-upsert.js | 4 ++ packages/core-js/stage/1.js | 2 +- tests/commonjs.js | 1 + tests/compat/tests.js | 6 +++ tests/pure/esnext.map.upsert.js | 37 +++++++++++++++++++ tests/pure/index.js | 1 + tests/tests/esnext.map.update-or-insert.js | 4 +- tests/tests/esnext.map.upsert.js | 36 ++++++++++++++++++ tests/tests/index.js | 1 + 20 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 packages/core-js/features/map/upsert.js create mode 100644 packages/core-js/internals/map-upsert.js create mode 100644 packages/core-js/modules/esnext.map.upsert.js create mode 100644 packages/core-js/proposals/map-upsert.js create mode 100644 tests/pure/esnext.map.upsert.js create mode 100644 tests/tests/esnext.map.upsert.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4c9b154073..f2fa1fdae673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - `AsyncIterator#take` - `AsyncIterator#toArray` - `AsyncIterator#@@toStringTag` +- `Map#updateOrInsert` renamed to `Map#upsert`, [stage 1 proposal](https://github.com/thumbsupep/proposal-upsert) - Added a workaround for iOS Safari MessageChannel + bfcache bug, [#624](https://github.com/zloirock/core-js/issues/624) - Added a workaround for Chrome 33 / Android 4.4.4 `Promise` bug, [#640](https://github.com/zloirock/core-js/issues/640) - Added compat data for Node 12.9, FF 69 and Phantom 1.9 diff --git a/README.md b/README.md index 3ff4115fb0f1..e2b4310d148e 100644 --- a/README.md +++ b/README.md @@ -1905,7 +1905,7 @@ Promise.try(() => { throw 42; }).catch(it => console.log(`Promise, rejected as $ ``` * New collections methods proposals: - New `Set` and `Map` methods [proposal](https://github.com/tc39/proposal-collection-methods) - modules [`esnext.set.add-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.add-all.js), [`esnext.set.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.delete-all.js), [`esnext.set.every`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.every.js), [`esnext.set.filter`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.filter.js), [`esnext.set.find`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.find.js), [`esnext.set.join`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.join.js), [`esnext.set.map`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.map.js), [`esnext.set.reduce`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.reduce.js), [`esnext.set.some`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.some.js), [`esnext.map.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.delete-all.js), [`esnext.map.every`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.every.js), [`esnext.map.filter`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.filter.js), [`esnext.map.find`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.find.js), [`esnext.map.find-key`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.find-key.js), [`esnext.map.group-by`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.group-by.js), [`esnext.map.includes`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.includes.js), [`esnext.map.key-by`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.key-by.js), [`esnext.map.key-of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.key-of.js), [`esnext.map.map-keys`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.map-keys.js), [`esnext.map.map-values`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.map-values.js), [`esnext.map.merge`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.merge.js), [`esnext.map.reduce`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.reduce.js), [`esnext.map.some`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.some.js), [`esnext.map.update`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.update.js), [`esnext.weak-set.add-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.add-all.js), [`esnext.weak-set.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.delete-all.js), [`esnext.weak-map.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.delete-all.js) -- `Map#updateOrInsert` [proposal](https://docs.google.com/presentation/d/1_xtrGSoN1-l2Q74eCXPHBbbrBHsVyqArWN0ebnW-pVQ/) - module [`esnext.map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.update-or-insert.js) +- `Map#upsert` [proposal](https://github.com/thumbsupep/proposal-upsert) - modules [`esnext.map.upsert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.upsert.js) and [`esnext.map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.update-or-insert.js) - `.of` and `.from` methods on collection constructors [proposal](https://github.com/tc39/proposal-setmap-offrom) - modules [`esnext.set.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.of.js), [`esnext.set.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.from.js), [`esnext.map.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.of.js), [`esnext.map.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.from.js), [`esnext.weak-set.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.of.js), [`esnext.weak-set.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.from.js), [`esnext.weak-map.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.of.js), [`esnext.weak-map.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.from.js) ```js class Set { @@ -1940,7 +1940,8 @@ class Map { reduce(callbackfn: (memo: any, value: any, key: any, target: any) => any, initialValue?: any): any; some(callbackfn: (value: any, key: any, target: any) => boolean, thisArg?: any): boolean; update(key: any, callbackfn: (value: any, key: any, target: any) => any, thunk?: (key: any, target: any) => any): this; - updateOrInsert(key: any, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value; + updateOrInsert(key: any, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value; (obsolete in favor `.upsert`) + upsert(key: any, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value; } class WeakSet { @@ -1960,7 +1961,7 @@ class WeakMap { ```js core-js/proposals/collection-methods core-js/proposals/collection-of-from -core-js/proposals/map-update-or-insert +core-js/proposals/map-upsert core-js(-pure)/features/set/add-all core-js(-pure)/features/set/delete-all core-js(-pure)/features/set/every @@ -1990,6 +1991,7 @@ core-js(-pure)/features/map/reduce core-js(-pure)/features/map/some core-js(-pure)/features/map/update core-js(-pure)/features/map/update-or-insert +core-js(-pure)/features/map/upsert core-js(-pure)/features/weak-set/add-all core-js(-pure)/features/weak-set/delete-all core-js(-pure)/features/weak-set/of @@ -1998,13 +2000,13 @@ core-js(-pure)/features/weak-map/delete-all core-js(-pure)/features/weak-map/of core-js(-pure)/features/weak-map/from ``` -`Map#updateOrInsert` [*examples*](http://es6.zloirock.ru/#const%20map%20%3D%20new%20Map(%5B%5B'a'%2C%202%5D%5D)%3B%0A%0Amap.updateOrInsert('a'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%204%0A%0Amap.updateOrInsert('b'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%203%0A%0Alog(map)%3B%20%2F%2F%20%3D%3E%20%7B%20'a'%3A%204%2C%20'b'%3A%203%20%7D): +`Map#upsert` [*examples*](http://es6.zloirock.ru/#const%20map%20%3D%20new%20Map(%5B%5B'a'%2C%202%5D%5D)%3B%0A%0Amap.upsert('a'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%204%0A%0Amap.upsert('b'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%203%0A%0Alog(map)%3B%20%2F%2F%20%3D%3E%20%7B%20'a'%3A%204%2C%20'b'%3A%203%20%7D): ```js const map = new Map([['a', 2]]); -map.updateOrInsert('a', it => it ** 2, () => 3); // => 4 +map.upsert('a', it => it ** 2, () => 3); // => 4 -map.updateOrInsert('b', it => it ** 2, () => 3); // => 3 +map.upsert('b', it => it ** 2, () => 3); // => 3 console.log(map); // => Map { 'a': 4, 'b': 3 } ``` diff --git a/packages/core-js-compat/src/data.js b/packages/core-js-compat/src/data.js index 19e6c16114a3..90497d7035a0 100644 --- a/packages/core-js-compat/src/data.js +++ b/packages/core-js-compat/src/data.js @@ -1301,6 +1301,8 @@ const data = { }, 'esnext.map.update-or-insert': { }, + 'esnext.map.upsert': { + }, 'esnext.math.clamp': { }, 'esnext.math.deg-per-rad': { diff --git a/packages/core-js-compat/src/modules-by-versions.js b/packages/core-js-compat/src/modules-by-versions.js index 5bfc8e705cac..6cf155b54740 100644 --- a/packages/core-js-compat/src/modules-by-versions.js +++ b/packages/core-js-compat/src/modules-by-versions.js @@ -40,5 +40,6 @@ module.exports = { 'esnext.iterator.some', 'esnext.iterator.take', 'esnext.iterator.to-array', + 'esnext.map.upsert', ], }; diff --git a/packages/core-js/features/map/index.js b/packages/core-js/features/map/index.js index bc7a5360c072..4a9b28ce1a97 100644 --- a/packages/core-js/features/map/index.js +++ b/packages/core-js/features/map/index.js @@ -17,4 +17,6 @@ require('../../modules/esnext.map.merge'); require('../../modules/esnext.map.reduce'); require('../../modules/esnext.map.some'); require('../../modules/esnext.map.update'); +// TODO: remove from `core-js@4` require('../../modules/esnext.map.update-or-insert'); +require('../../modules/esnext.map.upsert'); diff --git a/packages/core-js/features/map/update-or-insert.js b/packages/core-js/features/map/update-or-insert.js index 09c43531c4f0..f195f576c508 100644 --- a/packages/core-js/features/map/update-or-insert.js +++ b/packages/core-js/features/map/update-or-insert.js @@ -1,3 +1,4 @@ +// TODO: remove from `core-js@4` require('../../modules/es.map'); require('../../modules/esnext.map.update-or-insert'); var entryUnbind = require('../../internals/entry-unbind'); diff --git a/packages/core-js/features/map/upsert.js b/packages/core-js/features/map/upsert.js new file mode 100644 index 000000000000..e658a40dead6 --- /dev/null +++ b/packages/core-js/features/map/upsert.js @@ -0,0 +1,5 @@ +require('../../modules/es.map'); +require('../../modules/esnext.map.upsert'); +var entryUnbind = require('../../internals/entry-unbind'); + +module.exports = entryUnbind('Map', 'upsert'); diff --git a/packages/core-js/internals/map-upsert.js b/packages/core-js/internals/map-upsert.js new file mode 100644 index 000000000000..d7125ccdc8ce --- /dev/null +++ b/packages/core-js/internals/map-upsert.js @@ -0,0 +1,14 @@ +'use strict'; +var anObject = require('../internals/an-object'); +var aFunction = require('../internals/a-function'); + +// `Map.prototype.upsert` method +// https://github.com/thumbsupep/proposal-upsert +module.exports = function upsert(key, onUpdate, onInsert) { + var map = anObject(this); + aFunction(onUpdate); + aFunction(onInsert); + var value = map.has(key) ? onUpdate(map.get(key)) : onInsert(); + map.set(key, value); + return value; +}; diff --git a/packages/core-js/modules/esnext.map.update-or-insert.js b/packages/core-js/modules/esnext.map.update-or-insert.js index 6a0dac88fed1..f429b213445f 100644 --- a/packages/core-js/modules/esnext.map.update-or-insert.js +++ b/packages/core-js/modules/esnext.map.update-or-insert.js @@ -1,18 +1,11 @@ 'use strict'; +// TODO: remove from `core-js@4` var $ = require('../internals/export'); var IS_PURE = require('../internals/is-pure'); -var anObject = require('../internals/an-object'); -var aFunction = require('../internals/a-function'); +var $upsert = require('../internals/map-upsert'); -// `Set.prototype.updateOrInsert` method -// https://docs.google.com/presentation/d/1_xtrGSoN1-l2Q74eCXPHBbbrBHsVyqArWN0ebnW-pVQ/ +// `Map.prototype.updateOrInsert` method (replaced by `Map.prototype.upsert`) +// https://github.com/thumbsupep/proposal-upsert $({ target: 'Map', proto: true, real: true, forced: IS_PURE }, { - updateOrInsert: function updateOrInsert(key, onUpdate, onInsert) { - var map = anObject(this); - aFunction(onUpdate); - aFunction(onInsert); - var value = map.has(key) ? onUpdate(map.get(key)) : onInsert(); - map.set(key, value); - return value; - } + updateOrInsert: $upsert }); diff --git a/packages/core-js/modules/esnext.map.upsert.js b/packages/core-js/modules/esnext.map.upsert.js new file mode 100644 index 000000000000..a30e8351c526 --- /dev/null +++ b/packages/core-js/modules/esnext.map.upsert.js @@ -0,0 +1,10 @@ +'use strict'; +var $ = require('../internals/export'); +var IS_PURE = require('../internals/is-pure'); +var $upsert = require('../internals/map-upsert'); + +// `Map.prototype.upsert` method +// https://github.com/thumbsupep/proposal-upsert +$({ target: 'Map', proto: true, real: true, forced: IS_PURE }, { + upsert: $upsert +}); diff --git a/packages/core-js/proposals/map-update-or-insert.js b/packages/core-js/proposals/map-update-or-insert.js index 0438870dbd2b..28603018c81f 100644 --- a/packages/core-js/proposals/map-update-or-insert.js +++ b/packages/core-js/proposals/map-update-or-insert.js @@ -1 +1,2 @@ -require('../modules/esnext.map.update-or-insert'); +// TODO: remove from `core-js@4` +require('./map-upsert'); diff --git a/packages/core-js/proposals/map-upsert.js b/packages/core-js/proposals/map-upsert.js new file mode 100644 index 000000000000..e8ccfdfa2948 --- /dev/null +++ b/packages/core-js/proposals/map-upsert.js @@ -0,0 +1,4 @@ +// https://github.com/thumbsupep/proposal-upsert +// TODO: remove from `core-js@4` +require('../modules/esnext.map.update-or-insert'); +require('../modules/esnext.map.upsert'); diff --git a/packages/core-js/stage/1.js b/packages/core-js/stage/1.js index 98b215ce5245..c9cf83c94a11 100644 --- a/packages/core-js/stage/1.js +++ b/packages/core-js/stage/1.js @@ -2,7 +2,7 @@ require('../proposals/array-last'); require('../proposals/collection-methods'); require('../proposals/collection-of-from'); require('../proposals/keys-composition'); -require('../proposals/map-update-or-insert'); +require('../proposals/map-upsert'); require('../proposals/math-extensions'); require('../proposals/math-signbit'); require('../proposals/number-from-string'); diff --git a/tests/commonjs.js b/tests/commonjs.js index e5c0316583dc..2eefb0e4e439 100644 --- a/tests/commonjs.js +++ b/tests/commonjs.js @@ -326,6 +326,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(load('features/map/some')(new Map([[1, 2], [2, 3], [3, 4]]), it => it % 2) === true); ok(load('features/map/update')(new Map([[1, 2]]), 1, it => it * 2).get(1) === 4); ok(load('features/map/update-or-insert')(new Map([[1, 2]]), 1, it => it ** 2, () => 42) === 4); + ok(load('features/map/upsert')(new Map([[1, 2]]), 1, it => it ** 2, () => 42) === 4); ok(load('features/set/add-all')(new Set([1, 2, 3]), 4, 5).size === 5); ok(load('features/set/delete-all')(new Set([1, 2, 3]), 4, 5) === false); ok(load('features/set/difference')(new Set([1, 2, 3]), [3, 4, 5]).size === 2); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index a91f70b195b3..6b30bd76ae90 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1165,6 +1165,12 @@ GLOBAL.tests = { 'esnext.map.update': function () { return Map.prototype.update; }, + 'esnext.map.update-or-insert': function () { + return Map.prototype.updateOrInsert; + }, + 'esnext.map.upsert': function () { + return Map.prototype.upsert; + }, 'esnext.math.clamp': function () { return Math.clamp; }, diff --git a/tests/pure/esnext.map.upsert.js b/tests/pure/esnext.map.upsert.js new file mode 100644 index 000000000000..86a3c731917e --- /dev/null +++ b/tests/pure/esnext.map.upsert.js @@ -0,0 +1,37 @@ +import Map from 'core-js-pure/features/map'; + +QUnit.test('Map#upsert', assert => { + const { upsert } = Map.prototype; + assert.isFunction(upsert); + assert.arity(upsert, 3); + assert.name(upsert, 'upsert'); + assert.nonEnumerable(Map.prototype, 'upsert'); + + const map = new Map([['a', 2]]); + assert.same(map.upsert('a', function (value) { + assert.same(arguments.length, 1, 'correct number of callback arguments'); + assert.same(value, 2, 'correct value in callback'); + return value ** 2; + }, () => { + assert.ok(false, 'should not be called'); + return 3; + }), 4, 'returns a correct value'); + assert.same(map.upsert('b', value => { + assert.ok(false, 'should not be called'); + return value ** 2; + }, function () { + assert.same(arguments.length, 0, 'correct number of callback arguments'); + return 3; + }), 3, 'returns a correct value'); + assert.same(map.size, 2, 'correct size'); + assert.same(map.get('a'), 4, 'correct result #1'); + assert.same(map.get('b'), 3, 'correct result #2'); + + assert.throws(() => new Map([['a', 2]]).upsert('b', null, () => 3), TypeError); + assert.throws(() => new Map([['a', 2]]).upsert('a', value => value ** 2), TypeError); + + assert.throws(() => upsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(null, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); +}); diff --git a/tests/pure/index.js b/tests/pure/index.js index 54ca8c998356..dd03a466e22f 100644 --- a/tests/pure/index.js +++ b/tests/pure/index.js @@ -216,6 +216,7 @@ import './esnext.map.reduce'; import './esnext.map.some'; import './esnext.map.update'; import './esnext.map.update-or-insert'; +import './esnext.map.upsert'; import './esnext.set.add-all'; import './esnext.set.delete-all'; import './esnext.set.difference'; diff --git a/tests/tests/esnext.map.update-or-insert.js b/tests/tests/esnext.map.update-or-insert.js index dfe70d82ae35..18a44926c769 100644 --- a/tests/tests/esnext.map.update-or-insert.js +++ b/tests/tests/esnext.map.update-or-insert.js @@ -2,9 +2,9 @@ QUnit.test('Map#updateOrInsert', assert => { const { updateOrInsert } = Map.prototype; assert.isFunction(updateOrInsert); assert.arity(updateOrInsert, 3); - assert.name(updateOrInsert, 'updateOrInsert'); + assert.name(updateOrInsert, 'upsert'); assert.looksNative(updateOrInsert); - assert.nonEnumerable(Map.prototype, 'updateOrInsert'); + assert.nonEnumerable(Map.prototype, 'upsert'); const map = new Map([['a', 2]]); assert.same(map.updateOrInsert('a', function (value) { diff --git a/tests/tests/esnext.map.upsert.js b/tests/tests/esnext.map.upsert.js new file mode 100644 index 000000000000..3d6baf647118 --- /dev/null +++ b/tests/tests/esnext.map.upsert.js @@ -0,0 +1,36 @@ +QUnit.test('Map#upsert', assert => { + const { upsert } = Map.prototype; + assert.isFunction(upsert); + assert.arity(upsert, 3); + assert.name(upsert, 'upsert'); + assert.looksNative(upsert); + assert.nonEnumerable(Map.prototype, 'upsert'); + + const map = new Map([['a', 2]]); + assert.same(map.upsert('a', function (value) { + assert.same(arguments.length, 1, 'correct number of callback arguments'); + assert.same(value, 2, 'correct value in callback'); + return value ** 2; + }, () => { + assert.ok(false, 'should not be called'); + return 3; + }), 4, 'returns a correct value'); + assert.same(map.upsert('b', value => { + assert.ok(false, 'should not be called'); + return value ** 2; + }, function () { + assert.same(arguments.length, 0, 'correct number of callback arguments'); + return 3; + }), 3, 'returns a correct value'); + assert.same(map.size, 2, 'correct size'); + assert.same(map.get('a'), 4, 'correct result #1'); + assert.same(map.get('b'), 3, 'correct result #2'); + + assert.throws(() => new Map([['a', 2]]).upsert('b', null, () => 3), TypeError); + assert.throws(() => new Map([['a', 2]]).upsert('a', value => value ** 2), TypeError); + + assert.throws(() => upsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(null, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); +}); diff --git a/tests/tests/index.js b/tests/tests/index.js index 338a0ac2a4c6..d3e27d30e14a 100644 --- a/tests/tests/index.js +++ b/tests/tests/index.js @@ -267,6 +267,7 @@ import './esnext.map.reduce'; import './esnext.map.some'; import './esnext.map.update'; import './esnext.map.update-or-insert'; +import './esnext.map.upsert'; import './esnext.set.add-all'; import './esnext.set.delete-all'; import './esnext.set.difference'; From a786ae80161441da6055ff7582a8752352395241 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Wed, 25 Sep 2019 23:40:27 +0700 Subject: [PATCH 2/4] update `Map#upsert` semantic per https://github.com/thumbsupep/proposal-upsert/pull/2 --- packages/core-js/internals/map-upsert.js | 22 +++++++++++++++------- tests/pure/esnext.map.update-or-insert.js | 5 +++-- tests/pure/esnext.map.upsert.js | 5 +++-- tests/tests/esnext.map.update-or-insert.js | 5 +++-- tests/tests/esnext.map.upsert.js | 5 +++-- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/core-js/internals/map-upsert.js b/packages/core-js/internals/map-upsert.js index d7125ccdc8ce..410a9d34dc70 100644 --- a/packages/core-js/internals/map-upsert.js +++ b/packages/core-js/internals/map-upsert.js @@ -1,14 +1,22 @@ 'use strict'; var anObject = require('../internals/an-object'); -var aFunction = require('../internals/a-function'); // `Map.prototype.upsert` method // https://github.com/thumbsupep/proposal-upsert -module.exports = function upsert(key, onUpdate, onInsert) { +module.exports = function upsert(key, updateFn, insertFn) { var map = anObject(this); - aFunction(onUpdate); - aFunction(onInsert); - var value = map.has(key) ? onUpdate(map.get(key)) : onInsert(); - map.set(key, value); - return value; + if (typeof updateFn != 'function' && typeof insertFn != 'function') { + throw TypeError('At least one callback required'); + } + var value; + if (map.has(key)) { + value = map.get(key); + if (typeof updateFn == 'function') { + value = updateFn(value); + map.set(key, value); + } + } else if (typeof insertFn == 'function') { + value = insertFn(); + map.set(key, value); + } return value; }; diff --git a/tests/pure/esnext.map.update-or-insert.js b/tests/pure/esnext.map.update-or-insert.js index 3f9e59d02846..19093ee9cbac 100644 --- a/tests/pure/esnext.map.update-or-insert.js +++ b/tests/pure/esnext.map.update-or-insert.js @@ -27,9 +27,10 @@ QUnit.test('Map#updateOrInsert', assert => { assert.same(map.get('a'), 4, 'correct result #1'); assert.same(map.get('b'), 3, 'correct result #2'); - assert.throws(() => new Map([['a', 2]]).updateOrInsert('b', null, () => 3), TypeError); - assert.throws(() => new Map([['a', 2]]).updateOrInsert('a', value => value ** 2), TypeError); + assert.same(new Map([['a', 2]]).updateOrInsert('b', null, () => 3), 3); + assert.same(new Map([['a', 2]]).updateOrInsert('a', value => value ** 2), 4); + assert.throws(() => new Map().updateOrInsert('a'), TypeError); assert.throws(() => updateOrInsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => updateOrInsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => updateOrInsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); diff --git a/tests/pure/esnext.map.upsert.js b/tests/pure/esnext.map.upsert.js index 86a3c731917e..08f078e0a8b3 100644 --- a/tests/pure/esnext.map.upsert.js +++ b/tests/pure/esnext.map.upsert.js @@ -27,9 +27,10 @@ QUnit.test('Map#upsert', assert => { assert.same(map.get('a'), 4, 'correct result #1'); assert.same(map.get('b'), 3, 'correct result #2'); - assert.throws(() => new Map([['a', 2]]).upsert('b', null, () => 3), TypeError); - assert.throws(() => new Map([['a', 2]]).upsert('a', value => value ** 2), TypeError); + assert.same(new Map([['a', 2]]).upsert('b', null, () => 3), 3); + assert.same(new Map([['a', 2]]).upsert('a', value => value ** 2), 4); + assert.throws(() => new Map().upsert('a'), TypeError); assert.throws(() => upsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => upsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => upsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); diff --git a/tests/tests/esnext.map.update-or-insert.js b/tests/tests/esnext.map.update-or-insert.js index 18a44926c769..fabae0fdfb63 100644 --- a/tests/tests/esnext.map.update-or-insert.js +++ b/tests/tests/esnext.map.update-or-insert.js @@ -26,9 +26,10 @@ QUnit.test('Map#updateOrInsert', assert => { assert.same(map.get('a'), 4, 'correct result #1'); assert.same(map.get('b'), 3, 'correct result #2'); - assert.throws(() => new Map([['a', 2]]).updateOrInsert('b', null, () => 3), TypeError); - assert.throws(() => new Map([['a', 2]]).updateOrInsert('a', value => value ** 2), TypeError); + assert.same(new Map([['a', 2]]).updateOrInsert('b', null, () => 3), 3); + assert.same(new Map([['a', 2]]).updateOrInsert('a', value => value ** 2), 4); + assert.throws(() => new Map().updateOrInsert('a'), TypeError); assert.throws(() => updateOrInsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => updateOrInsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => updateOrInsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); diff --git a/tests/tests/esnext.map.upsert.js b/tests/tests/esnext.map.upsert.js index 3d6baf647118..3f72192fbb7b 100644 --- a/tests/tests/esnext.map.upsert.js +++ b/tests/tests/esnext.map.upsert.js @@ -26,9 +26,10 @@ QUnit.test('Map#upsert', assert => { assert.same(map.get('a'), 4, 'correct result #1'); assert.same(map.get('b'), 3, 'correct result #2'); - assert.throws(() => new Map([['a', 2]]).upsert('b', null, () => 3), TypeError); - assert.throws(() => new Map([['a', 2]]).upsert('a', value => value ** 2), TypeError); + assert.same(new Map([['a', 2]]).upsert('b', null, () => 3), 3); + assert.same(new Map([['a', 2]]).upsert('a', value => value ** 2), 4); + assert.throws(() => new Map().upsert('a'), TypeError); assert.throws(() => upsert.call({}, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => upsert.call([], 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); assert.throws(() => upsert.call(undefined, 'a', () => { /* empty */ }, () => { /* empty */ }), TypeError); From 42c502c161078c85aa2f17675ff0d790d5b102de Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Tue, 1 Oct 2019 13:35:04 +0700 Subject: [PATCH 3/4] add `WeakMap#upsert` --- CHANGELOG.md | 4 +- README.md | 4 +- packages/core-js-compat/src/data.js | 2 + .../core-js-compat/src/modules-by-versions.js | 1 + packages/core-js/features/weak-map/index.js | 1 + packages/core-js/features/weak-map/upsert.js | 5 +++ .../core-js/modules/esnext.weak-map.upsert.js | 10 +++++ packages/core-js/proposals/map-upsert.js | 1 + tests/commonjs.js | 1 + tests/compat/tests.js | 3 ++ tests/pure/esnext.weak-map.upsert.js | 39 +++++++++++++++++++ tests/pure/index.js | 1 + tests/tests/esnext.weak-map.upsert.js | 39 +++++++++++++++++++ tests/tests/index.js | 1 + 14 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 packages/core-js/features/weak-map/upsert.js create mode 100644 packages/core-js/modules/esnext.weak-map.upsert.js create mode 100644 tests/pure/esnext.weak-map.upsert.js create mode 100644 tests/tests/esnext.weak-map.upsert.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f2fa1fdae673..75139eba89c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,9 @@ - `AsyncIterator#take` - `AsyncIterator#toArray` - `AsyncIterator#@@toStringTag` -- `Map#updateOrInsert` renamed to `Map#upsert`, [stage 1 proposal](https://github.com/thumbsupep/proposal-upsert) +- Updated `Map#upsert` [stage 1 proposal](https://github.com/thumbsupep/proposal-upsert) + - `Map#updateOrInsert` renamed to `Map#upsert` + - Added `WeakMap#upsert` - Added a workaround for iOS Safari MessageChannel + bfcache bug, [#624](https://github.com/zloirock/core-js/issues/624) - Added a workaround for Chrome 33 / Android 4.4.4 `Promise` bug, [#640](https://github.com/zloirock/core-js/issues/640) - Added compat data for Node 12.9, FF 69 and Phantom 1.9 diff --git a/README.md b/README.md index e2b4310d148e..1e13a648763f 100644 --- a/README.md +++ b/README.md @@ -1905,7 +1905,7 @@ Promise.try(() => { throw 42; }).catch(it => console.log(`Promise, rejected as $ ``` * New collections methods proposals: - New `Set` and `Map` methods [proposal](https://github.com/tc39/proposal-collection-methods) - modules [`esnext.set.add-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.add-all.js), [`esnext.set.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.delete-all.js), [`esnext.set.every`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.every.js), [`esnext.set.filter`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.filter.js), [`esnext.set.find`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.find.js), [`esnext.set.join`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.join.js), [`esnext.set.map`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.map.js), [`esnext.set.reduce`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.reduce.js), [`esnext.set.some`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.some.js), [`esnext.map.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.delete-all.js), [`esnext.map.every`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.every.js), [`esnext.map.filter`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.filter.js), [`esnext.map.find`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.find.js), [`esnext.map.find-key`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.find-key.js), [`esnext.map.group-by`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.group-by.js), [`esnext.map.includes`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.includes.js), [`esnext.map.key-by`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.key-by.js), [`esnext.map.key-of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.key-of.js), [`esnext.map.map-keys`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.map-keys.js), [`esnext.map.map-values`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.map-values.js), [`esnext.map.merge`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.merge.js), [`esnext.map.reduce`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.reduce.js), [`esnext.map.some`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.some.js), [`esnext.map.update`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.update.js), [`esnext.weak-set.add-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.add-all.js), [`esnext.weak-set.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.delete-all.js), [`esnext.weak-map.delete-all`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.delete-all.js) -- `Map#upsert` [proposal](https://github.com/thumbsupep/proposal-upsert) - modules [`esnext.map.upsert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.upsert.js) and [`esnext.map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.update-or-insert.js) +- `Map#upsert` [proposal](https://github.com/thumbsupep/proposal-upsert) - modules [`esnext.map.upsert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.upsert.js), [`esnext.map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.map.update-or-insert.js) and [`esnext.weak-map.update-or-insert`](https://github.com/zloirock/core-js/blob/v3.3.0/packages/core-js/modules/esnext.weak-map.update-or-insert.js) - `.of` and `.from` methods on collection constructors [proposal](https://github.com/tc39/proposal-setmap-offrom) - modules [`esnext.set.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.of.js), [`esnext.set.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.set.from.js), [`esnext.map.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.of.js), [`esnext.map.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.map.from.js), [`esnext.weak-set.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.of.js), [`esnext.weak-set.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-set.from.js), [`esnext.weak-map.of`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.of.js), [`esnext.weak-map.from`](https://github.com/zloirock/core-js/blob/v3.2.1/packages/core-js/modules/esnext.weak-map.from.js) ```js class Set { @@ -1955,6 +1955,7 @@ class WeakMap { static of(...args: Array<[key, value]>): WeakMap; static from(iterable: Iterable, mapFn?: (value: any, index: number) => [key: Object, value: any], thisArg?: any): WeakMap; deleteAll(...args: Array): boolean; + upsert(key: Object, onUpdate: (value: any) => updated: any, onInsert: () => value: any): updated | value; } ``` [*CommonJS entry points:*](#commonjs-api) @@ -1999,6 +2000,7 @@ core-js(-pure)/features/weak-set/from core-js(-pure)/features/weak-map/delete-all core-js(-pure)/features/weak-map/of core-js(-pure)/features/weak-map/from +core-js(-pure)/features/weak-map/upsert ``` `Map#upsert` [*examples*](http://es6.zloirock.ru/#const%20map%20%3D%20new%20Map(%5B%5B'a'%2C%202%5D%5D)%3B%0A%0Amap.upsert('a'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%204%0A%0Amap.upsert('b'%2C%20it%20%3D%3E%20it%20**%202%2C%20()%20%3D%3E%203)%3B%20%2F%2F%20%3D%3E%203%0A%0Alog(map)%3B%20%2F%2F%20%3D%3E%20%7B%20'a'%3A%204%2C%20'b'%3A%203%20%7D): ```js diff --git a/packages/core-js-compat/src/data.js b/packages/core-js-compat/src/data.js index 90497d7035a0..80c90fa792d6 100644 --- a/packages/core-js-compat/src/data.js +++ b/packages/core-js-compat/src/data.js @@ -1417,6 +1417,8 @@ const data = { }, 'esnext.weak-map.of': { }, + 'esnext.weak-map.upsert': { + }, 'esnext.weak-set.add-all': { }, 'esnext.weak-set.delete-all': { diff --git a/packages/core-js-compat/src/modules-by-versions.js b/packages/core-js-compat/src/modules-by-versions.js index 6cf155b54740..ca5c6c50a01d 100644 --- a/packages/core-js-compat/src/modules-by-versions.js +++ b/packages/core-js-compat/src/modules-by-versions.js @@ -41,5 +41,6 @@ module.exports = { 'esnext.iterator.take', 'esnext.iterator.to-array', 'esnext.map.upsert', + 'esnext.weak-map.upsert', ], }; diff --git a/packages/core-js/features/weak-map/index.js b/packages/core-js/features/weak-map/index.js index a4f4bae85a94..9a3ffd4ab46a 100644 --- a/packages/core-js/features/weak-map/index.js +++ b/packages/core-js/features/weak-map/index.js @@ -3,3 +3,4 @@ module.exports = require('../../es/weak-map'); require('../../modules/esnext.weak-map.from'); require('../../modules/esnext.weak-map.of'); require('../../modules/esnext.weak-map.delete-all'); +require('../../modules/esnext.weak-map.upsert'); diff --git a/packages/core-js/features/weak-map/upsert.js b/packages/core-js/features/weak-map/upsert.js new file mode 100644 index 000000000000..509f9edc8a24 --- /dev/null +++ b/packages/core-js/features/weak-map/upsert.js @@ -0,0 +1,5 @@ +require('../../modules/es.weak-map'); +require('../../modules/esnext.weak-map.upsert'); +var entryUnbind = require('../../internals/entry-unbind'); + +module.exports = entryUnbind('WeakMap', 'upsert'); diff --git a/packages/core-js/modules/esnext.weak-map.upsert.js b/packages/core-js/modules/esnext.weak-map.upsert.js new file mode 100644 index 000000000000..1e0d60c97105 --- /dev/null +++ b/packages/core-js/modules/esnext.weak-map.upsert.js @@ -0,0 +1,10 @@ +'use strict'; +var $ = require('../internals/export'); +var IS_PURE = require('../internals/is-pure'); +var $upsert = require('../internals/map-upsert'); + +// `WeakMap.prototype.upsert` method +// https://github.com/thumbsupep/proposal-upsert +$({ target: 'WeakMap', proto: true, real: true, forced: IS_PURE }, { + upsert: $upsert +}); diff --git a/packages/core-js/proposals/map-upsert.js b/packages/core-js/proposals/map-upsert.js index e8ccfdfa2948..ee94275fac0a 100644 --- a/packages/core-js/proposals/map-upsert.js +++ b/packages/core-js/proposals/map-upsert.js @@ -2,3 +2,4 @@ // TODO: remove from `core-js@4` require('../modules/esnext.map.update-or-insert'); require('../modules/esnext.map.upsert'); +require('../modules/esnext.weak-map.upsert'); diff --git a/tests/commonjs.js b/tests/commonjs.js index 2eefb0e4e439..d9ecc417f352 100644 --- a/tests/commonjs.js +++ b/tests/commonjs.js @@ -344,6 +344,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(load('features/set/symmetric-difference')(new Set([1, 2, 3]), [3, 4, 5]).size === 4); ok(load('features/set/union')(new Set([1, 2, 3]), [3, 4, 5]).size === 5); ok(load('features/weak-map/delete-all')(new WeakMap(), [], {}) === false); + ok(load('features/weak-map/upsert')(new WeakMap(), {}, null, () => 42) === 42); ok(load('features/weak-set/add-all')(new WeakSet(), [], {}) instanceof WeakSet); ok(load('features/weak-set/delete-all')(new WeakSet(), [], {}) === false); let Promise = load('features/promise'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index 6b30bd76ae90..994b86fb7aa0 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1333,6 +1333,9 @@ GLOBAL.tests = { 'esnext.weak-map.of': function () { return WeakMap.of; }, + 'esnext.weak-map.upsert': function () { + return WeakMap.prototype.upsert; + }, 'esnext.weak-set.add-all': function () { return WeakSet.prototype.addAll; }, diff --git a/tests/pure/esnext.weak-map.upsert.js b/tests/pure/esnext.weak-map.upsert.js new file mode 100644 index 000000000000..215ca10ba983 --- /dev/null +++ b/tests/pure/esnext.weak-map.upsert.js @@ -0,0 +1,39 @@ +import WeakMap from 'core-js-pure/features/weak-map'; + +QUnit.test('WeakMap#upsert', assert => { + const { upsert } = WeakMap.prototype; + assert.isFunction(upsert); + assert.arity(upsert, 3); + assert.nonEnumerable(WeakMap.prototype, 'upsert'); + + const a = {}; + const b = {}; + + const map = new WeakMap([[a, 2]]); + assert.same(map.upsert(a, function (value) { + assert.same(arguments.length, 1, 'correct number of callback arguments'); + assert.same(value, 2, 'correct value in callback'); + return value ** 2; + }, () => { + assert.ok(false, 'should not be called'); + return 3; + }), 4, 'returns a correct value'); + assert.same(map.upsert(b, value => { + assert.ok(false, 'should not be called'); + return value ** 2; + }, function () { + assert.same(arguments.length, 0, 'correct number of callback arguments'); + return 3; + }), 3, 'returns a correct value'); + assert.same(map.get(a), 4, 'correct result #1'); + assert.same(map.get(b), 3, 'correct result #2'); + + assert.same(new WeakMap([[a, 2]]).upsert(b, null, () => 3), 3); + assert.same(new WeakMap([[a, 2]]).upsert(a, value => value ** 2), 4); + + assert.throws(() => new WeakMap().upsert(a), TypeError); + assert.throws(() => upsert.call({}, a, () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call([], a, () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(undefined, a, () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(null, a, () => { /* empty */ }, () => { /* empty */ }), TypeError); +}); diff --git a/tests/pure/index.js b/tests/pure/index.js index dd03a466e22f..8b71cfe1012a 100644 --- a/tests/pure/index.js +++ b/tests/pure/index.js @@ -245,6 +245,7 @@ import './esnext.symbol.replace-all'; import './esnext.weak-map.delete-all'; import './esnext.weak-map.from'; import './esnext.weak-map.of'; +import './esnext.weak-map.upsert'; import './esnext.weak-set.add-all'; import './esnext.weak-set.delete-all'; import './esnext.weak-set.from'; diff --git a/tests/tests/esnext.weak-map.upsert.js b/tests/tests/esnext.weak-map.upsert.js new file mode 100644 index 000000000000..3ce05b360e02 --- /dev/null +++ b/tests/tests/esnext.weak-map.upsert.js @@ -0,0 +1,39 @@ +QUnit.test('WeakMap#upsert', assert => { + const { upsert } = WeakMap.prototype; + assert.isFunction(upsert); + assert.arity(upsert, 3); + assert.name(upsert, 'upsert'); + assert.looksNative(upsert); + assert.nonEnumerable(WeakMap.prototype, 'upsert'); + + const a = {}; + const b = {}; + + const map = new WeakMap([[a, 2]]); + assert.same(map.upsert(a, function (value) { + assert.same(arguments.length, 1, 'correct number of callback arguments'); + assert.same(value, 2, 'correct value in callback'); + return value ** 2; + }, () => { + assert.ok(false, 'should not be called'); + return 3; + }), 4, 'returns a correct value'); + assert.same(map.upsert(b, value => { + assert.ok(false, 'should not be called'); + return value ** 2; + }, function () { + assert.same(arguments.length, 0, 'correct number of callback arguments'); + return 3; + }), 3, 'returns a correct value'); + assert.same(map.get(a), 4, 'correct result #1'); + assert.same(map.get(b), 3, 'correct result #2'); + + assert.same(new WeakMap([[a, 2]]).upsert(b, null, () => 3), 3); + assert.same(new WeakMap([[a, 2]]).upsert(a, value => value ** 2), 4); + + assert.throws(() => new WeakMap().upsert(a), TypeError); + assert.throws(() => upsert.call({}, a, () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call([], a, () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(undefined, a, () => { /* empty */ }, () => { /* empty */ }), TypeError); + assert.throws(() => upsert.call(null, a, () => { /* empty */ }, () => { /* empty */ }), TypeError); +}); diff --git a/tests/tests/index.js b/tests/tests/index.js index d3e27d30e14a..594b128973ee 100644 --- a/tests/tests/index.js +++ b/tests/tests/index.js @@ -297,6 +297,7 @@ import './esnext.symbol.replace-all'; import './esnext.weak-map.delete-all'; import './esnext.weak-map.from'; import './esnext.weak-map.of'; +import './esnext.weak-map.upsert'; import './esnext.weak-set.add-all'; import './esnext.weak-set.delete-all'; import './esnext.weak-set.from'; From 6f67b65cc3a878cf093ff9133de59fdd8664838d Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Tue, 1 Oct 2019 15:22:09 +0700 Subject: [PATCH 4/4] minor fixes --- tests/pure/esnext.map.update-or-insert.js | 2 +- tests/pure/esnext.weak-map.upsert.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pure/esnext.map.update-or-insert.js b/tests/pure/esnext.map.update-or-insert.js index 19093ee9cbac..a671f548daaa 100644 --- a/tests/pure/esnext.map.update-or-insert.js +++ b/tests/pure/esnext.map.update-or-insert.js @@ -4,7 +4,7 @@ QUnit.test('Map#updateOrInsert', assert => { const { updateOrInsert } = Map.prototype; assert.isFunction(updateOrInsert); assert.arity(updateOrInsert, 3); - assert.name(updateOrInsert, 'updateOrInsert'); + assert.name(updateOrInsert, 'upsert'); assert.nonEnumerable(Map.prototype, 'updateOrInsert'); const map = new Map([['a', 2]]); diff --git a/tests/pure/esnext.weak-map.upsert.js b/tests/pure/esnext.weak-map.upsert.js index 215ca10ba983..5c7302ee09f5 100644 --- a/tests/pure/esnext.weak-map.upsert.js +++ b/tests/pure/esnext.weak-map.upsert.js @@ -4,6 +4,7 @@ QUnit.test('WeakMap#upsert', assert => { const { upsert } = WeakMap.prototype; assert.isFunction(upsert); assert.arity(upsert, 3); + assert.name(upsert, 'upsert'); assert.nonEnumerable(WeakMap.prototype, 'upsert'); const a = {};