From 2777bd7f6573b085b261635e92e73a303e3b783a Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Sat, 5 May 2018 21:11:35 +0700 Subject: [PATCH] add `compositeKey` method from richer keys proposal --- .eslintrc.js | 1 + README.md | 30 ++++++++++++ packages/core-js-builder/config.js | 1 + packages/core-js/features/composite-key.js | 3 ++ packages/core-js/index.js | 1 + .../core-js/modules/esnext.composite-key.js | 44 ++++++++++++++++++ packages/core-js/stage/1.js | 1 + tests/commonjs.js | 1 + tests/pure/esnext.composite-key.js | 46 +++++++++++++++++++ tests/pure/index.js | 1 + tests/tests/esnext.composite-key.js | 46 +++++++++++++++++++ tests/tests/index.js | 1 + 12 files changed, 176 insertions(+) create mode 100644 packages/core-js/features/composite-key.js create mode 100644 packages/core-js/modules/esnext.composite-key.js create mode 100644 tests/pure/esnext.composite-key.js create mode 100644 tests/tests/esnext.composite-key.js diff --git a/.eslintrc.js b/.eslintrc.js index 96c596897735..05b2b5bbb53e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -470,6 +470,7 @@ module.exports = { }, globals: { asap: true, + compositeKey: true, Observable: true, }, }, diff --git a/README.md b/README.md index f50b34015437..254f0d703d67 100644 --- a/README.md +++ b/README.md @@ -1691,6 +1691,36 @@ Set.of(1, 2, 3, 2, 1); // => Set {1, 2, 3} Map.from([[1, 2], [3, 4]], ([key, value]) => [key ** 2, value ** 2]); // => Map {1: 4, 9: 16} ``` +* `compositeKey` method from richer keys [proposal](https://github.com/bmeck/proposal-richer-keys/tree/master/compositeKey) - module [`esnext.composite-key`](https://github.com/zloirock/core-js/blob/v3/packages/core-js/modules/esnext.composite-key.js) +```js +function compositeKey(...args: Array): object; +``` +[*CommonJS entry points:*](#commonjs) +```js +core-js(-pure)/features/composite-key +``` +[*Examples*](https://goo.gl/2oPAH7): +```js +// returns a plain frozen object without a prototype +const key = compositeKey({}); +console.log(typeof key); // => 'object' +console.log({}.toString.call(key)); // => '[object Object]' +console.log(Object.getPrototypeOf(key)); // => null +console.log(Object.isFrozen(key)); // => true + +const a = ['a']; +const b = ['b']; +const c = ['c']; + +console.log(compositeKey(a) === compositeKey(a)); // => true +console.log(compositeKey(a) !== compositeKey(['a'])); // => true +console.log(compositeKey(a, 1) === compositeKey(a, 1)); // => true +console.log(compositeKey(a, b) !== compositeKey(b, a)); // => true +console.log(compositeKey(a, b, c) === compositeKey(a, b, c)); // => true +console.log(compositeKey(1, a) === compositeKey(1, a)); // => true +console.log(compositeKey(1, a, 2, b) === compositeKey(1, a, 2, b)); // => true +console.log(compositeKey(a, a) === compositeKey(a, a)); // => true +``` * `Observable` [proposal](https://github.com/zenparsing/es-observable) - modules [`esnext.observable`](https://github.com/zloirock/core-js/blob/v3/packages/core-js/modules/esnext.observable.js) and [`esnext.symbol.observable`](https://github.com/zloirock/core-js/blob/v3/packages/core-js/modules/esnext.symbol.observable.js) ```js class Observable { diff --git a/packages/core-js-builder/config.js b/packages/core-js-builder/config.js index f768e4c3d6a8..323082ba7d3e 100644 --- a/packages/core-js-builder/config.js +++ b/packages/core-js-builder/config.js @@ -196,6 +196,7 @@ module.exports = { 'esnext.array.last-index', 'esnext.array.last-item', 'esnext.asap', + 'esnext.composite-key', 'esnext.global', 'esnext.map.from', 'esnext.map.group-by', diff --git a/packages/core-js/features/composite-key.js b/packages/core-js/features/composite-key.js new file mode 100644 index 000000000000..962e8d37bf0c --- /dev/null +++ b/packages/core-js/features/composite-key.js @@ -0,0 +1,3 @@ +require('../modules/esnext.composite-key'); + +module.exports = require('../internals/path').compositeKey; diff --git a/packages/core-js/index.js b/packages/core-js/index.js index a32d34d22d89..99b79b4d6134 100644 --- a/packages/core-js/index.js +++ b/packages/core-js/index.js @@ -193,6 +193,7 @@ require('./modules/esnext.array.flat-map'); require('./modules/esnext.array.last-index'); require('./modules/esnext.array.last-item'); require('./modules/esnext.asap'); +require('./modules/esnext.composite-key'); require('./modules/esnext.global'); require('./modules/esnext.map.from'); require('./modules/esnext.map.group-by'); diff --git a/packages/core-js/modules/esnext.composite-key.js b/packages/core-js/modules/esnext.composite-key.js new file mode 100644 index 000000000000..fb23ce16f891 --- /dev/null +++ b/packages/core-js/modules/esnext.composite-key.js @@ -0,0 +1,44 @@ +var Map = require('../modules/es.map'); +var WeakMap = require('../modules/es.weak-map'); +var create = require('../internals/object-create'); +var isObject = require('../internals/is-object'); +var $Object = require('../internals/path').Object; +var freeze = $Object && $Object.freeze; + +var Node = function () { + this.value = null; + this.primitives = null; + this.objectsByIndex = create(null); +}; + +Node.prototype.get = function () { + return this.value || (this.value = freeze ? freeze(create(null)) : create(null)); +}; + +Node.prototype.next = function (i, it, IS_OBJECT) { + var store = IS_OBJECT + ? this.objectsByIndex[i] || (this.objectsByIndex[i] = new WeakMap()) + : this.primitives || (this.primitives = new Map()); + var entry = store.get(it); + if (!entry) store.set(it, entry = new Node()); + return entry; +}; + +var root = new Node(); + +require('../internals/export')({ global: true }, { + compositeKey: function compositeKey() { + var active = root; + var length = arguments.length; + var i, it; + // for prevent leaking, start from objects + for (i = 0; i < length; i++) { + if (isObject(it = arguments[i])) active = active.next(i, it, true); + } + if (active === root) throw TypeError('Composite keys must contain a non-primitive component'); + for (i = 0; i < length; i++) { + if (!isObject(it = arguments[i])) active = active.next(i, it, false); + } + return active.get(); + } +}); diff --git a/packages/core-js/stage/1.js b/packages/core-js/stage/1.js index 96c16fa9aee3..33e0fb5abc2e 100644 --- a/packages/core-js/stage/1.js +++ b/packages/core-js/stage/1.js @@ -42,5 +42,6 @@ require('../modules/esnext.weak-map.from'); require('../modules/esnext.weak-map.of'); require('../modules/esnext.weak-set.from'); require('../modules/esnext.weak-set.of'); +require('../modules/esnext.composite-key'); module.exports = require('./2'); diff --git a/tests/commonjs.js b/tests/commonjs.js index dffde7aef641..a378274f519c 100644 --- a/tests/commonjs.js +++ b/tests/commonjs.js @@ -325,6 +325,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(typeof load('features/set-immediate') === 'function'); ok(typeof load('features/clear-immediate') === 'function'); ok(typeof load('features/asap') === 'function'); + ok(typeof load('features/composite-key')({}, 1, {}) === 'object'); ok(load('features/is-iterable')([])); ok(typeof load('features/get-iterator-method')([]) === 'function'); ok('next' in load('features/get-iterator')([])); diff --git a/tests/pure/esnext.composite-key.js b/tests/pure/esnext.composite-key.js new file mode 100644 index 000000000000..c83932d20adf --- /dev/null +++ b/tests/pure/esnext.composite-key.js @@ -0,0 +1,46 @@ +/* eslint-disable no-self-compare */ +import { FREEZING } from '../helpers/constants'; + +import compositeKey from 'core-js-pure/features/composite-key'; +import { getPrototypeOf, isFrozen } from 'core-js-pure/features/object'; + +QUnit.test('compositeKey', assert => { + assert.isFunction(compositeKey); + if (compositeKey.name) assert.name(compositeKey, 'compositeKey'); + + const key = compositeKey({}); + assert.same(typeof key, 'object'); + assert.same({}.toString.call(key), '[object Object]'); + assert.same(getPrototypeOf(key), null); + if (FREEZING) assert.ok(isFrozen(key)); + + const a = ['a']; + const b = ['b']; + const c = ['c']; + + assert.ok(compositeKey(a) === compositeKey(a)); + assert.ok(compositeKey(a) !== compositeKey(['a'])); + assert.ok(compositeKey(a) !== compositeKey(a, 1)); + assert.ok(compositeKey(a) !== compositeKey(a, b)); + assert.ok(compositeKey(a, 1) === compositeKey(a, 1)); + assert.ok(compositeKey(a, b) === compositeKey(a, b)); + assert.ok(compositeKey(a, b) !== compositeKey(b, a)); + assert.ok(compositeKey(a, b, c) === compositeKey(a, b, c)); + assert.ok(compositeKey(a, b, c) !== compositeKey(c, b, a)); + assert.ok(compositeKey(a, b, c) !== compositeKey(a, c, b)); + assert.ok(compositeKey(a, b, c, 1) !== compositeKey(a, b, c)); + assert.ok(compositeKey(a, b, c, 1) === compositeKey(a, b, c, 1)); + assert.ok(compositeKey(1, a) === compositeKey(1, a)); + assert.ok(compositeKey(1, a) !== compositeKey(a, 1)); + assert.ok(compositeKey(1, a, 2, b) === compositeKey(1, a, 2, b)); + assert.ok(compositeKey(1, a, 2, b) !== compositeKey(1, a, b, 2)); + assert.ok(compositeKey(1, 2, a, b) === compositeKey(1, 2, a, b)); + assert.ok(compositeKey(1, 2, a, b) !== compositeKey(1, a, b, 2)); + assert.ok(compositeKey(a, a) === compositeKey(a, a)); + assert.ok(compositeKey(a, a) !== compositeKey(a, ['a'])); + assert.ok(compositeKey(a, a) !== compositeKey(a, b)); + + assert.throws(() => compositeKey(), TypeError); + assert.throws(() => compositeKey(1, 2), TypeError); + assert.throws(() => compositeKey('foo', null, true), TypeError); +}); diff --git a/tests/pure/index.js b/tests/pure/index.js index 30897286ccb9..47fa362516c2 100644 --- a/tests/pure/index.js +++ b/tests/pure/index.js @@ -132,6 +132,7 @@ QUnit.module('ESNext'); import './esnext.array.flat-map'; import './esnext.array.flatten'; import './esnext.asap'; +import './esnext.composite-key'; import './esnext.global'; import './esnext.math.clamp'; import './esnext.math.deg-per-rad'; diff --git a/tests/tests/esnext.composite-key.js b/tests/tests/esnext.composite-key.js new file mode 100644 index 000000000000..2b3232e0ceb4 --- /dev/null +++ b/tests/tests/esnext.composite-key.js @@ -0,0 +1,46 @@ +/* eslint-disable no-self-compare */ +import { FREEZING } from '../helpers/constants'; + +const { getPrototypeOf, isFrozen } = Object; + +QUnit.test('compositeKey', assert => { + assert.isFunction(compositeKey); + assert.name(compositeKey, 'compositeKey'); + assert.looksNative(compositeKey); + + const key = compositeKey({}); + assert.same(typeof key, 'object'); + assert.same({}.toString.call(key), '[object Object]'); + assert.same(getPrototypeOf(key), null); + if (FREEZING) assert.ok(isFrozen(key)); + + const a = ['a']; + const b = ['b']; + const c = ['c']; + + assert.ok(compositeKey(a) === compositeKey(a)); + assert.ok(compositeKey(a) !== compositeKey(['a'])); + assert.ok(compositeKey(a) !== compositeKey(a, 1)); + assert.ok(compositeKey(a) !== compositeKey(a, b)); + assert.ok(compositeKey(a, 1) === compositeKey(a, 1)); + assert.ok(compositeKey(a, b) === compositeKey(a, b)); + assert.ok(compositeKey(a, b) !== compositeKey(b, a)); + assert.ok(compositeKey(a, b, c) === compositeKey(a, b, c)); + assert.ok(compositeKey(a, b, c) !== compositeKey(c, b, a)); + assert.ok(compositeKey(a, b, c) !== compositeKey(a, c, b)); + assert.ok(compositeKey(a, b, c, 1) !== compositeKey(a, b, c)); + assert.ok(compositeKey(a, b, c, 1) === compositeKey(a, b, c, 1)); + assert.ok(compositeKey(1, a) === compositeKey(1, a)); + assert.ok(compositeKey(1, a) !== compositeKey(a, 1)); + assert.ok(compositeKey(1, a, 2, b) === compositeKey(1, a, 2, b)); + assert.ok(compositeKey(1, a, 2, b) !== compositeKey(1, a, b, 2)); + assert.ok(compositeKey(1, 2, a, b) === compositeKey(1, 2, a, b)); + assert.ok(compositeKey(1, 2, a, b) !== compositeKey(1, a, b, 2)); + assert.ok(compositeKey(a, a) === compositeKey(a, a)); + assert.ok(compositeKey(a, a) !== compositeKey(a, ['a'])); + assert.ok(compositeKey(a, a) !== compositeKey(a, b)); + + assert.throws(() => compositeKey(), TypeError); + assert.throws(() => compositeKey(1, 2), TypeError); + assert.throws(() => compositeKey('foo', null, true), TypeError); +}); diff --git a/tests/tests/index.js b/tests/tests/index.js index 3dc4c0e0fb93..716cca18552d 100644 --- a/tests/tests/index.js +++ b/tests/tests/index.js @@ -182,6 +182,7 @@ import './esnext.array.last-index'; import './esnext.array.flat-map'; import './esnext.array.flatten'; import './esnext.asap'; +import './esnext.composite-key'; import './esnext.global'; import './esnext.map.from'; import './esnext.map.of';