| 'use strict'; |
| var defineProperty = require('../internals/object-define-property').f; |
| var create = require('../internals/object-create'); |
| var redefineAll = require('../internals/redefine-all'); |
| var bind = require('../internals/bind-context'); |
| var anInstance = require('../internals/an-instance'); |
| var iterate = require('../internals/iterate'); |
| var defineIterator = require('../internals/define-iterator'); |
| var setSpecies = require('../internals/set-species'); |
| var DESCRIPTORS = require('../internals/descriptors'); |
| var fastKey = require('../internals/internal-metadata').fastKey; |
| var InternalStateModule = require('../internals/internal-state'); |
| |
| var setInternalState = InternalStateModule.set; |
| var internalStateGetterFor = InternalStateModule.getterFor; |
| |
| module.exports = { |
| getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) { |
| var C = wrapper(function (that, iterable) { |
| anInstance(that, C, CONSTRUCTOR_NAME); |
| setInternalState(that, { |
| type: CONSTRUCTOR_NAME, |
| index: create(null), |
| first: undefined, |
| last: undefined, |
| size: 0 |
| }); |
| if (!DESCRIPTORS) that.size = 0; |
| if (iterable != undefined) iterate(iterable, that[ADDER], that, IS_MAP); |
| }); |
| |
| var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME); |
| |
| var define = function (that, key, value) { |
| var state = getInternalState(that); |
| var entry = getEntry(that, key); |
| var previous, index; |
| // change existing entry |
| if (entry) { |
| entry.value = value; |
| // create new entry |
| } else { |
| state.last = entry = { |
| index: index = fastKey(key, true), |
| key: key, |
| value: value, |
| previous: previous = state.last, |
| next: undefined, |
| removed: false |
| }; |
| if (!state.first) state.first = entry; |
| if (previous) previous.next = entry; |
| if (DESCRIPTORS) state.size++; |
| else that.size++; |
| // add to index |
| if (index !== 'F') state.index[index] = entry; |
| } return that; |
| }; |
| |
| var getEntry = function (that, key) { |
| var state = getInternalState(that); |
| // fast case |
| var index = fastKey(key); |
| var entry; |
| if (index !== 'F') return state.index[index]; |
| // frozen object case |
| for (entry = state.first; entry; entry = entry.next) { |
| if (entry.key == key) return entry; |
| } |
| }; |
| |
| redefineAll(C.prototype, { |
| // 23.1.3.1 Map.prototype.clear() |
| // 23.2.3.2 Set.prototype.clear() |
| clear: function clear() { |
| var that = this; |
| var state = getInternalState(that); |
| var data = state.index; |
| var entry = state.first; |
| while (entry) { |
| entry.removed = true; |
| if (entry.previous) entry.previous = entry.previous.next = undefined; |
| delete data[entry.index]; |
| entry = entry.next; |
| } |
| state.first = state.last = undefined; |
| if (DESCRIPTORS) state.size = 0; |
| else that.size = 0; |
| }, |
| // 23.1.3.3 Map.prototype.delete(key) |
| // 23.2.3.4 Set.prototype.delete(value) |
| 'delete': function (key) { |
| var that = this; |
| var state = getInternalState(that); |
| var entry = getEntry(that, key); |
| if (entry) { |
| var next = entry.next; |
| var prev = entry.previous; |
| delete state.index[entry.index]; |
| entry.removed = true; |
| if (prev) prev.next = next; |
| if (next) next.previous = prev; |
| if (state.first == entry) state.first = next; |
| if (state.last == entry) state.last = prev; |
| if (DESCRIPTORS) state.size--; |
| else that.size--; |
| } return !!entry; |
| }, |
| // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined) |
| // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined) |
| forEach: function forEach(callbackfn /* , that = undefined */) { |
| var state = getInternalState(this); |
| var boundFunction = bind(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3); |
| var entry; |
| while (entry = entry ? entry.next : state.first) { |
| boundFunction(entry.value, entry.key, this); |
| // revert to the last existing entry |
| while (entry && entry.removed) entry = entry.previous; |
| } |
| }, |
| // 23.1.3.7 Map.prototype.has(key) |
| // 23.2.3.7 Set.prototype.has(value) |
| has: function has(key) { |
| return !!getEntry(this, key); |
| } |
| }); |
| |
| redefineAll(C.prototype, IS_MAP ? { |
| // 23.1.3.6 Map.prototype.get(key) |
| get: function get(key) { |
| var entry = getEntry(this, key); |
| return entry && entry.value; |
| }, |
| // 23.1.3.9 Map.prototype.set(key, value) |
| set: function set(key, value) { |
| return define(this, key === 0 ? 0 : key, value); |
| } |
| } : { |
| // 23.2.3.1 Set.prototype.add(value) |
| add: function add(value) { |
| return define(this, value = value === 0 ? 0 : value, value); |
| } |
| }); |
| if (DESCRIPTORS) defineProperty(C.prototype, 'size', { |
| get: function () { |
| return getInternalState(this).size; |
| } |
| }); |
| return C; |
| }, |
| setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) { |
| var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator'; |
| var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME); |
| var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME); |
| // add .keys, .values, .entries, [@@iterator] |
| // 23.1.3.4, 23.1.3.8, 23.1.3.11, 23.1.3.12, 23.2.3.5, 23.2.3.8, 23.2.3.10, 23.2.3.11 |
| defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) { |
| setInternalState(this, { |
| type: ITERATOR_NAME, |
| target: iterated, |
| state: getInternalCollectionState(iterated), |
| kind: kind, |
| last: undefined |
| }); |
| }, function () { |
| var state = getInternalIteratorState(this); |
| var kind = state.kind; |
| var entry = state.last; |
| // revert to the last existing entry |
| while (entry && entry.removed) entry = entry.previous; |
| // get next entry |
| if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) { |
| // or finish the iteration |
| state.target = undefined; |
| return { value: undefined, done: true }; |
| } |
| // return step by kind |
| if (kind == 'keys') return { value: entry.key, done: false }; |
| if (kind == 'values') return { value: entry.value, done: false }; |
| return { value: [entry.key, entry.value], done: false }; |
| }, IS_MAP ? 'entries' : 'values', !IS_MAP, true); |
| |
| // add [@@species], 23.1.2.2, 23.2.2.2 |
| setSpecies(CONSTRUCTOR_NAME); |
| } |
| }; |