diff --git a/features.json b/features.json index d9caf942b7a..428c5b6a348 100644 --- a/features.json +++ b/features.json @@ -3,7 +3,6 @@ "features-stripped-test": null, "ember-libraries-isregistered": null, "ember-improved-instrumentation": null, - "ember-metal-weakmap": null, "ember-glimmer-allow-backtracking-rerender": null, "ember-routing-router-service": true, "ember-engines-mount-params": true, diff --git a/packages/ember-glimmer/lib/utils/references.ts b/packages/ember-glimmer/lib/utils/references.ts index cbf81425b3e..82e579eb17d 100644 --- a/packages/ember-glimmer/lib/utils/references.ts +++ b/packages/ember-glimmer/lib/utils/references.ts @@ -24,7 +24,6 @@ import { watchKey, } from 'ember-metal'; import { - HAS_NATIVE_WEAKMAP, symbol, } from 'ember-utils'; import { @@ -50,7 +49,7 @@ if (DEBUG) { // performance penalty on Chrome (tested through 59). // // See: https://bugs.chromium.org/p/v8/issues/detail?id=6450 - if (!Object.isFrozen(obj) && HAS_NATIVE_WEAKMAP) { + if (!Object.isFrozen(obj)) { Object.freeze(obj); } }; diff --git a/packages/ember-glimmer/tests/integration/helpers/custom-helper-test.js b/packages/ember-glimmer/tests/integration/helpers/custom-helper-test.js index 7a79f630071..cc63f7e2e9e 100644 --- a/packages/ember-glimmer/tests/integration/helpers/custom-helper-test.js +++ b/packages/ember-glimmer/tests/integration/helpers/custom-helper-test.js @@ -2,7 +2,6 @@ import { RenderingTest, moduleFor } from '../../utils/test-case'; import { runDestroy } from 'internal-test-helpers'; import { set } from 'ember-metal'; -import { HAS_NATIVE_WEAKMAP } from 'ember-utils'; let assert = QUnit.assert; @@ -634,7 +633,7 @@ let addingPropertyToFrozenObjectThrows = (() => { } })(); -if (!EmberDev.runningProdBuild && HAS_NATIVE_WEAKMAP && ( +if (!EmberDev.runningProdBuild && ( pushingIntoFrozenArrayThrows || assigningExistingFrozenPropertyThrows || addingPropertyToFrozenObjectThrows diff --git a/packages/ember-metal/lib/index.js b/packages/ember-metal/lib/index.js index 1ed903f78e5..a32958ef991 100644 --- a/packages/ember-metal/lib/index.js +++ b/packages/ember-metal/lib/index.js @@ -38,7 +38,6 @@ export { set, trySet } from './property_set'; -export { default as WeakMap, WeakMapPolyfill } from './weak_map'; export { addListener, hasListeners, diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index ffd0d17ec13..3e2277dbc4a 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -1,5 +1,4 @@ import { - HAS_NATIVE_WEAKMAP, lookupDescriptor, symbol, toString @@ -460,53 +459,35 @@ if (MANDATORY_SETTER) { }; } -let setMeta, peekMeta; -// choose the one appropriate for given platform -if (HAS_NATIVE_WEAKMAP) { - let getPrototypeOf = Object.getPrototypeOf; - let metaStore = new WeakMap(); +let getPrototypeOf = Object.getPrototypeOf; +let metaStore = new WeakMap(); - setMeta = function WeakMap_setMeta(obj, meta) { - if (DEBUG) { - counters.setCalls++; - } - metaStore.set(obj, meta); - }; - - peekMeta = function WeakMap_peekParentMeta(obj) { - let pointer = obj; - let meta; - while (pointer !== undefined && pointer !== null) { - meta = metaStore.get(pointer); - // jshint loopfunc:true - if (DEBUG) { - counters.peekCalls++; - } - if (meta !== undefined) { - return meta; - } +export function setMeta(obj, meta) { + if (DEBUG) { + counters.setCalls++; + } + metaStore.set(obj, meta); +} - pointer = getPrototypeOf(pointer); - if (DEBUG) { - counters.peekPrototypeWalks++; - } +export function peekMeta(obj) { + let pointer = obj; + let meta; + while (pointer !== undefined && pointer !== null) { + meta = metaStore.get(pointer); + // jshint loopfunc:true + if (DEBUG) { + counters.peekCalls++; } - }; -} else { - setMeta = function Fallback_setMeta(obj, meta) { - if (obj.__defineNonEnumerable) { - obj.__defineNonEnumerable(EMBER_META_PROPERTY); - } else { - Object.defineProperty(obj, META_FIELD, META_DESC); + if (meta !== undefined) { + return meta; } - obj[META_FIELD] = meta; - }; - - peekMeta = function Fallback_peekMeta(obj) { - return obj[META_FIELD]; - }; + pointer = getPrototypeOf(pointer); + if (DEBUG) { + counters.peekPrototypeWalks++; + } + } } /** @@ -569,8 +550,4 @@ export function meta(obj) { return newMeta; } -export { - peekMeta, - setMeta, - counters -}; +export { counters }; diff --git a/packages/ember-metal/lib/transaction.js b/packages/ember-metal/lib/transaction.js index db474518c76..cf5854083c2 100644 --- a/packages/ember-metal/lib/transaction.js +++ b/packages/ember-metal/lib/transaction.js @@ -1,6 +1,4 @@ import { meta as metaFor } from './meta'; -import WeakMap from './weak_map'; -import { HAS_NATIVE_WEAKMAP } from 'ember-utils'; import { assert, deprecate } from 'ember-debug'; import { DEBUG } from 'ember-env-flags'; import { @@ -14,27 +12,16 @@ let runInTransaction, didRender, assertNotRendered; // detect-glimmer-allow-backtracking-rerender can be enabled in custom builds if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER || EMBER_GLIMMER_ALLOW_BACKTRACKING_RERENDER) { - // there are 4 states + // there are 2 states - // NATIVE WEAKMAP AND DEBUG + // DEBUG // tracks lastRef and lastRenderedIn per rendered object and key during a transaction // release everything via normal weakmap semantics by just derefencing the weakmap - // NATIVE WEAKMAP AND RELEASE + // RELEASE // tracks transactionId per rendered object and key during a transaction // release everything via normal weakmap semantics by just derefencing the weakmap - // WEAKMAP POLYFILL AND DEBUG - // tracks lastRef and lastRenderedIn per rendered object and key during a transaction - // since lastRef retains a lot of app state (will have a ref to the Container) - // if the object rendered is retained (like a immutable POJO in module state) - // during acceptance tests this adds up and obfuscates finding other leaks. - - // WEAKMAP POLYFILL AND RELEASE - // tracks transactionId per rendered object and key during a transaction - // leaks it because small and likely not worth tracking it since it will only - // be leaked if the object is retained - class TransactionRunner { constructor() { this.transactionId = 0; @@ -44,12 +31,6 @@ if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER || EMBER_GLIMMER_ALLOW_BACKTRACKI if (DEBUG) { // track templates this.debugStack = undefined; - - if (!HAS_NATIVE_WEAKMAP) { - // DEBUG AND POLYFILL - // needs obj tracking - this.objs = []; - } } } @@ -137,11 +118,6 @@ if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER || EMBER_GLIMMER_ALLOW_BACKTRACKI createMap(object) { let map = Object.create(null); this.weakMap.set(object, map); - if (DEBUG && !HAS_NATIVE_WEAKMAP) { - // POLYFILL AND DEBUG - // requires tracking objects - this.objs.push(object); - } return map; } @@ -166,26 +142,7 @@ if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER || EMBER_GLIMMER_ALLOW_BACKTRACKI } clearObjectMap() { - if (HAS_NATIVE_WEAKMAP) { - // NATIVE AND (DEBUG OR RELEASE) - // if we have a real native weakmap - // releasing the ref will allow the values to be GCed - this.weakMap = new WeakMap(); - } else if (DEBUG) { - // POLYFILL AND DEBUG - // with a polyfill the weakmap keys must be cleared since - // they have the last reference, acceptance tests will leak - // the container if you render a immutable object retained - // in module scope. - let { objs, weakMap } = this; - this.objs = []; - for (let i = 0; i < objs.length; i++) { - weakMap.delete(objs[i]); - } - } - // POLYFILL AND RELEASE - // we leak the key map if the object is retained but this is - // a POJO of keys to transaction ids + this.weakMap = new WeakMap(); } } diff --git a/packages/ember-metal/lib/weak_map.js b/packages/ember-metal/lib/weak_map.js deleted file mode 100644 index 34e546b7507..00000000000 --- a/packages/ember-metal/lib/weak_map.js +++ /dev/null @@ -1,131 +0,0 @@ -import { GUID_KEY, HAS_NATIVE_WEAKMAP } from 'ember-utils'; -import { - peekMeta, - meta as metaFor, - UNDEFINED -} from './meta'; - -/** - @module ember -*/ -let id = 0; - -// Returns whether Type(value) is Object according to the terminology in the spec -function isObject(value) { - return (typeof value === 'object' && value !== null) || typeof value === 'function'; -} - -/* - * @class Ember.WeakMap - * @public - * @category ember-metal-weakmap - * - * A partial polyfill for [WeakMap](http://www.ecma-international.org/ecma-262/6.0/#sec-weakmap-objects). - * - * There is a small but important caveat. This implementation assumes that the - * weak map will live longer (in the sense of garbage collection) than all of its - * keys, otherwise it is possible to leak the values stored in the weak map. In - * practice, most use cases satisfy this limitation which is why it is included - * in ember-metal. - */ -export class WeakMapPolyfill { - constructor(iterable) { - this._id = GUID_KEY + (id++); - - if (iterable === null || iterable === undefined) { - return; - } else if (Array.isArray(iterable)) { - for (let i = 0; i < iterable.length; i++) { - let [key, value] = iterable[i]; - this.set(key, value); - } - } else { - throw new TypeError('The weak map constructor polyfill only supports an array argument'); - } - } - - /* - * @method get - * @param key {Object | Function} - * @return {Any} stored value - */ - get(obj) { - if (!isObject(obj)) { return undefined; } - - let meta = peekMeta(obj); - if (meta !== undefined) { - let map = meta.readableWeak(); - if (map !== undefined) { - let val = map[this._id]; - if (val === UNDEFINED) { - return undefined; - } - return val; - } - } - } - - /* - * @method set - * @param key {Object | Function} - * @param value {Any} - * @return {WeakMap} the weak map - */ - set(obj, value) { - if (!isObject(obj)) { - throw new TypeError('Invalid value used as weak map key'); - } - - if (value === undefined) { - value = UNDEFINED; - } - - metaFor(obj).writableWeak()[this._id] = value; - - return this; - } - - /* - * @method has - * @param key {Object | Function} - * @return {boolean} if the key exists - */ - has(obj) { - if (!isObject(obj)) { return false; } - - let meta = peekMeta(obj); - if (meta !== undefined) { - let map = meta.readableWeak(); - if (map !== undefined) { - return map[this._id] !== undefined; - } - } - - return false; - } - - /* - * @method delete - * @param key {Object | Function} - * @return {boolean} if the key was deleted - */ - delete(obj) { - if (this.has(obj)) { - delete peekMeta(obj).writableWeak()[this._id]; - return true; - } else { - return false; - } - } - - /* - * @method toString - * @return {String} - */ - toString() { - return '[object WeakMap]'; - } - -} - -export default HAS_NATIVE_WEAKMAP ? WeakMap : WeakMapPolyfill; diff --git a/packages/ember-metal/tests/weak_map_test.js b/packages/ember-metal/tests/weak_map_test.js deleted file mode 100644 index 4810083c691..00000000000 --- a/packages/ember-metal/tests/weak_map_test.js +++ /dev/null @@ -1,103 +0,0 @@ -import { WeakMapPolyfill as WeakMap } from '..'; - -QUnit.module('Ember.WeakMap'); - -QUnit.test('has weakMap like qualities', function(assert) { - let map = new WeakMap(); - let map2 = new WeakMap(); - - let a = {}; - let b = {}; - let c = {}; - - assert.strictEqual(map.get(a), undefined); - assert.strictEqual(map.get(b), undefined); - assert.strictEqual(map.get(c), undefined); - - assert.strictEqual(map2.get(a), undefined); - assert.strictEqual(map2.get(b), undefined); - assert.strictEqual(map2.get(c), undefined); - - assert.strictEqual(map.set(a, 1), map, 'map.set should return itself'); - assert.strictEqual(map.get(a), 1); - assert.strictEqual(map.set(b, undefined), map); - assert.strictEqual(map.set(a, 2), map); - assert.strictEqual(map.get(a), 2); - assert.strictEqual(map.set(b, undefined), map); - - assert.strictEqual(map2.get(a), undefined); - assert.strictEqual(map2.get(b), undefined); - assert.strictEqual(map2.get(c), undefined); - - assert.strictEqual(map.set(c, 1), map); - assert.strictEqual(map.get(c), 1); - assert.strictEqual(map.get(a), 2); - assert.strictEqual(map.get(b), undefined); - - assert.strictEqual(map2.set(a, 3), map2); - assert.strictEqual(map2.set(b, 4), map2); - assert.strictEqual(map2.set(c, 5), map2); - - assert.strictEqual(map2.get(a), 3); - assert.strictEqual(map2.get(b), 4); - assert.strictEqual(map2.get(c), 5); - - assert.strictEqual(map.get(c), 1); - assert.strictEqual(map.get(a), 2); - assert.strictEqual(map.get(b), undefined); -}); - -QUnit.test('constructing a WeakMap with an invalid iterator throws an error', function(assert) { - let expectedError = new TypeError('The weak map constructor polyfill only supports an array argument'); - - assert.throws(() => { new WeakMap({ a: 1 }); }, expectedError); -}); - -QUnit.test('constructing a WeakMap with a valid iterator inserts the entries', function(assert) { - let a = {}; - let b = {}; - let c = {}; - - let map = new WeakMap([[a, 1], [b, 2], [c, 3]]); - - assert.strictEqual(map.get(a), 1); - assert.strictEqual(map.get(b), 2); - assert.strictEqual(map.get(c), 3); -}); - -QUnit.test('that error is thrown when using a primitive key', function(assert) { - let expectedError = new TypeError('Invalid value used as weak map key'); - let map = new WeakMap(); - - assert.throws(() => map.set('a', 1), expectedError); - assert.throws(() => map.set(1, 1), expectedError); - assert.throws(() => map.set(true, 1), expectedError); - assert.throws(() => map.set(null, 1), expectedError); - assert.throws(() => map.set(undefined, 1), expectedError); -}); - -QUnit.test('that .has and .delete work as expected', function(assert) { - let map = new WeakMap(); - let a = {}; - let b = {}; - let foo = { id: 1, name: 'My file', progress: 0 }; - - assert.strictEqual(map.set(a, foo), map); - assert.strictEqual(map.get(a), foo); - assert.strictEqual(map.has(a), true); - assert.strictEqual(map.has(b), false); - assert.strictEqual(map.delete(a), true); - assert.strictEqual(map.has(a), false); - assert.strictEqual(map.delete(a), false); - assert.strictEqual(map.set(a, undefined), map); - assert.strictEqual(map.has(a), true); - assert.strictEqual(map.delete(a), true); - assert.strictEqual(map.delete(a), false); - assert.strictEqual(map.has(a), false); -}); - -QUnit.test('that .toString works as expected', function(assert) { - let map = new WeakMap(); - - assert.strictEqual(map.toString(), '[object WeakMap]'); -}); diff --git a/packages/ember-runtime/lib/computed/reduce_computed_macros.js b/packages/ember-runtime/lib/computed/reduce_computed_macros.js index 7ba2d7ec92b..671c5a792b0 100644 --- a/packages/ember-runtime/lib/computed/reduce_computed_macros.js +++ b/packages/ember-runtime/lib/computed/reduce_computed_macros.js @@ -10,8 +10,7 @@ import { computed, addObserver, removeObserver, - getProperties, - WeakMap + getProperties } from 'ember-metal'; import compare from '../compare'; import { isArray } from '../utils'; diff --git a/packages/ember-utils/lib/index.d.ts b/packages/ember-utils/lib/index.d.ts index 04694a4db12..90a008fd350 100644 --- a/packages/ember-utils/lib/index.d.ts +++ b/packages/ember-utils/lib/index.d.ts @@ -5,7 +5,6 @@ export function getOwner(obj: any): any; export function symbol(debugName: string): string; export function assign(original: any, ...args: any[]): any; export const OWNER: string; -export const HAS_NATIVE_WEAKMAP: boolean; export function generateGuid(obj: Opaque, prefix?: string): string; export function guidFor(obj: Opaque): string; diff --git a/packages/ember-utils/lib/index.js b/packages/ember-utils/lib/index.js index ad383fd0f3d..72a2b6170da 100644 --- a/packages/ember-utils/lib/index.js +++ b/packages/ember-utils/lib/index.js @@ -32,5 +32,4 @@ export { default as makeArray } from './make-array'; export { default as applyStr } from './apply-str'; export { default as NAME_KEY } from './name'; export { default as toString } from './to-string'; -export { HAS_NATIVE_WEAKMAP } from './weak-map-utils'; export { HAS_NATIVE_PROXY } from './proxy-utils'; diff --git a/packages/ember-utils/lib/weak-map-utils.js b/packages/ember-utils/lib/weak-map-utils.js deleted file mode 100644 index 57400e2a61d..00000000000 --- a/packages/ember-utils/lib/weak-map-utils.js +++ /dev/null @@ -1,10 +0,0 @@ -export const HAS_NATIVE_WEAKMAP = ((() => { - // detect if `WeakMap` is even present - let hasWeakMap = typeof WeakMap === 'function'; - if (!hasWeakMap) { return false; } - - let instance = new WeakMap(); - // use `Object`'s `.toString` directly to prevent us from detecting - // polyfills as native weakmaps - return Object.prototype.toString.call(instance) === '[object WeakMap]'; -}))(); diff --git a/packages/ember/lib/index.js b/packages/ember/lib/index.js index 03ff501ed02..fecaa2c84d5 100644 --- a/packages/ember/lib/index.js +++ b/packages/ember/lib/index.js @@ -10,7 +10,6 @@ import { Registry, Container } from 'container'; // ****ember-metal**** import Ember, * as metal from 'ember-metal'; -import { EMBER_METAL_WEAKMAP } from 'ember/features'; import * as FLAGS from 'ember/features'; // ember-utils exports @@ -139,9 +138,6 @@ Ember.bind = metal.bind; Ember.Binding = metal.Binding; Ember.isGlobalPath = metal.isGlobalPath; -if (EMBER_METAL_WEAKMAP) { - Ember.WeakMap = metal.WeakMap; -} Object.defineProperty(Ember, 'ENV', { get() { return ENV; }, diff --git a/packages/ember/tests/reexports_test.js b/packages/ember/tests/reexports_test.js index 7285cd855f1..3281f8782a7 100644 --- a/packages/ember/tests/reexports_test.js +++ b/packages/ember/tests/reexports_test.js @@ -1,6 +1,5 @@ import Ember from '../index'; import { confirmExport } from 'internal-test-helpers'; -import { EMBER_METAL_WEAKMAP } from 'ember/features'; import { DEBUG } from 'ember-env-flags'; QUnit.module('ember reexports'); @@ -228,12 +227,6 @@ QUnit.test('Ember.String.isHTMLSafe exports correctly', function(assert) { confirmExport(Ember, assert, 'String.isHTMLSafe', 'ember-glimmer', 'isHTMLSafe'); }); -if (EMBER_METAL_WEAKMAP) { - QUnit.test('Ember.WeakMap exports correctly', function(assert) { - confirmExport(Ember, assert, 'WeakMap', 'ember-metal', 'WeakMap'); - }); -} - if (DEBUG) { QUnit.test('Ember.MODEL_FACTORY_INJECTIONS', function(assert) { let descriptor = Object.getOwnPropertyDescriptor(Ember, 'MODEL_FACTORY_INJECTIONS');