Skip to content

Commit

Permalink
improve IE11 WeakMap frozen objects fallback, fixes #384
Browse files Browse the repository at this point in the history
  • Loading branch information
zloirock committed Apr 20, 2018
1 parent b5069b1 commit b0f4a3c
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 17 deletions.
47 changes: 34 additions & 13 deletions packages/core-js/modules/es.weak-map.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';
var forEach = require('../internals/array-methods')(0);
var redefine = require('../internals/redefine');
var redefineAll = require('../internals/redefine-all');
var InternalMetadataModule = require('../internals/internal-metadata');
var weak = require('../internals/collection-weak');
var isObject = require('../internals/is-object');
Expand All @@ -24,18 +23,40 @@ var $WeakMap = module.exports = require('../internals/collection')(WEAK_MAP, wra
if (fails(function () { return new $WeakMap().set((Object.freeze || Object)(test), 7).get(test) != 7; })) {
InternalWeakMap = weak.getConstructor(wrapper, WEAK_MAP, true);
InternalMetadataModule.REQUIRED = true;
forEach(['delete', 'has', 'get', 'set'], function (METHOD_NAME) {
var WeakMapPrototype = $WeakMap.prototype;
var method = WeakMapPrototype[METHOD_NAME];
redefine(WeakMapPrototype, METHOD_NAME, function (a, b) {
// store frozen objects on internal weakmap shim
if (isObject(a) && !isExtensible(a)) {
var WeakMapPrototype = $WeakMap.prototype;
var nativeDelete = WeakMapPrototype['delete'];
var nativeHas = WeakMapPrototype.has;
var nativeGet = WeakMapPrototype.get;
var nativeSet = WeakMapPrototype.set;
redefineAll(WeakMapPrototype, {
'delete': function (key) {
if (isObject(key) && !isExtensible(key)) {
var state = enforceIternalState(this);
if (!state.frozen) state.frozen = new InternalWeakMap();
var result = state.frozen[METHOD_NAME](a, b);
return METHOD_NAME == 'set' ? this : result;
// store all the rest on native weakmap
} return method.call(this, a, b);
});
return nativeDelete.call(this, key) || state.frozen['delete'](key);
} return nativeDelete.call(this, key);
},
has: function has(key) {
if (isObject(key) && !isExtensible(key)) {
var state = enforceIternalState(this);
if (!state.frozen) state.frozen = new InternalWeakMap();
return nativeHas.call(this, key) || state.frozen.has(key);
} return nativeHas.call(this, key);
},
get: function get(key) {
if (isObject(key) && !isExtensible(key)) {
var state = enforceIternalState(this);
if (!state.frozen) state.frozen = new InternalWeakMap();
return nativeHas.call(this, key) ? nativeGet.call(this, key) : state.frozen.get(key);
} return nativeGet.call(this, key);
},
set: function set(key, value) {
if (isObject(key) && !isExtensible(key)) {
var state = enforceIternalState(this);
if (!state.frozen) state.frozen = new InternalWeakMap();
nativeHas.call(this, key) ? nativeSet.call(this, key, value) : state.frozen.set(key, value);
} else nativeSet.call(this, key, value);
return this;
}
});
}
33 changes: 31 additions & 2 deletions tests/pure/es.weak-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,30 +76,48 @@ QUnit.test('WeakMap#delete', assert => {
weakmap.delete(a);
assert.ok(!weakmap.has(a) && weakmap.has(b), 'WeakMap hasn`t value after .delete()');
assert.notThrows(() => !weakmap.delete(1), 'return false on primitive');
const object = {};
weakmap.set(object, 42);
freeze(object);
assert.ok(weakmap.has(object), 'works with frozen objects #1');
weakmap.delete(object);
assert.ok(!weakmap.has(object), 'works with frozen objects #2');
});

QUnit.test('WeakMap#get', assert => {
assert.isFunction(WeakMap.prototype.get);
const weakmap = new WeakMap();
assert.strictEqual(weakmap.get({}), undefined, 'WeakMap .get() before .set() return undefined');
const object = {};
let object = {};
weakmap.set(object, 42);
assert.strictEqual(weakmap.get(object), 42, 'WeakMap .get() return value');
weakmap.delete(object);
assert.strictEqual(weakmap.get(object), undefined, 'WeakMap .get() after .delete() return undefined');
assert.notThrows(() => weakmap.get(1) === undefined, 'return undefined on primitive');
object = {};
weakmap.set(object, 42);
freeze(object);
assert.same(weakmap.get(object), 42, 'works with frozen objects #1');
weakmap.delete(object);
assert.same(weakmap.get(object), undefined, 'works with frozen objects #2');
});

QUnit.test('WeakMap#has', assert => {
assert.isFunction(WeakMap.prototype.has);
const weakmap = new WeakMap();
assert.ok(!weakmap.has({}), 'WeakMap .has() before .set() return false');
const object = {};
let object = {};
weakmap.set(object, 42);
assert.ok(weakmap.has(object), 'WeakMap .has() return true');
weakmap.delete(object);
assert.ok(!weakmap.has(object), 'WeakMap .has() after .delete() return false');
assert.notThrows(() => !weakmap.has(1), 'return false on primitive');
object = {};
weakmap.set(object, 42);
freeze(object);
assert.ok(weakmap.has(object), 'works with frozen objects #1');
weakmap.delete(object);
assert.ok(!weakmap.has(object), 'works with frozen objects #2');
});

QUnit.test('WeakMap#set', assert => {
Expand All @@ -110,6 +128,17 @@ QUnit.test('WeakMap#set', assert => {
assert.same(weakmap.get(object), 33, 'works with object as keys');
assert.ok(weakmap.set({}, 42) === weakmap, 'chaining');
assert.throws(() => new WeakMap().set(42, 42), 'throws with primitive keys');
const object1 = freeze({});
const object2 = {};
weakmap.set(object1, 42);
weakmap.set(object2, 42);
freeze(object);
assert.same(weakmap.get(object1), 42, 'works with frozen objects #1');
assert.same(weakmap.get(object2), 42, 'works with frozen objects #2');
weakmap.delete(object1);
weakmap.delete(object2);
assert.same(weakmap.get(object1), undefined, 'works with frozen objects #3');
assert.same(weakmap.get(object2), undefined, 'works with frozen objects #4');
});

QUnit.test('WeakMap#@@toStringTag', assert => {
Expand Down
33 changes: 31 additions & 2 deletions tests/tests/es.weak-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ QUnit.test('WeakMap#delete', assert => {
weakmap.delete(a);
assert.ok(!weakmap.has(a) && weakmap.has(b), 'WeakMap hasn`t value after .delete()');
assert.notThrows(() => !weakmap.delete(1), 'return false on primitive');
const object = {};
weakmap.set(object, 42);
freeze(object);
assert.ok(weakmap.has(object), 'works with frozen objects #1');
weakmap.delete(object);
assert.ok(!weakmap.has(object), 'works with frozen objects #2');
});

QUnit.test('WeakMap#get', assert => {
Expand All @@ -92,12 +98,18 @@ QUnit.test('WeakMap#get', assert => {
assert.nonEnumerable(WeakMap.prototype, 'get');
const weakmap = new WeakMap();
assert.strictEqual(weakmap.get({}), undefined, 'WeakMap .get() before .set() return undefined');
const object = {};
let object = {};
weakmap.set(object, 42);
assert.strictEqual(weakmap.get(object), 42, 'WeakMap .get() return value');
weakmap.delete(object);
assert.strictEqual(weakmap.get(object), undefined, 'WeakMap .get() after .delete() return undefined');
assert.notThrows(() => weakmap.get(1) === undefined, 'return undefined on primitive');
object = {};
weakmap.set(object, 42);
freeze(object);
assert.same(weakmap.get(object), 42, 'works with frozen objects #1');
weakmap.delete(object);
assert.same(weakmap.get(object), undefined, 'works with frozen objects #2');
});

QUnit.test('WeakMap#has', assert => {
Expand All @@ -108,12 +120,18 @@ QUnit.test('WeakMap#has', assert => {
assert.nonEnumerable(WeakMap.prototype, 'has');
const weakmap = new WeakMap();
assert.ok(!weakmap.has({}), 'WeakMap .has() before .set() return false');
const object = {};
let object = {};
weakmap.set(object, 42);
assert.ok(weakmap.has(object), 'WeakMap .has() return true');
weakmap.delete(object);
assert.ok(!weakmap.has(object), 'WeakMap .has() after .delete() return false');
assert.notThrows(() => !weakmap.has(1), 'return false on primitive');
object = {};
weakmap.set(object, 42);
freeze(object);
assert.ok(weakmap.has(object), 'works with frozen objects #1');
weakmap.delete(object);
assert.ok(!weakmap.has(object), 'works with frozen objects #2');
});

QUnit.test('WeakMap#set', assert => {
Expand All @@ -128,6 +146,17 @@ QUnit.test('WeakMap#set', assert => {
assert.same(weakmap.get(object), 33, 'works with object as keys');
assert.ok(weakmap.set({}, 42) === weakmap, 'chaining');
assert.throws(() => new WeakMap().set(42, 42), 'throws with primitive keys');
const object1 = freeze({});
const object2 = {};
weakmap.set(object1, 42);
weakmap.set(object2, 42);
freeze(object);
assert.same(weakmap.get(object1), 42, 'works with frozen objects #1');
assert.same(weakmap.get(object2), 42, 'works with frozen objects #2');
weakmap.delete(object1);
weakmap.delete(object2);
assert.same(weakmap.get(object1), undefined, 'works with frozen objects #3');
assert.same(weakmap.get(object2), undefined, 'works with frozen objects #4');
});

QUnit.test('WeakMap#@@toStringTag', assert => {
Expand Down

0 comments on commit b0f4a3c

Please sign in to comment.