[Pkg-javascript-commits] [node-es6-shim] 01/03: Imported Upstream version 0.34.1+ds

Julien Puydt julien.puydt at laposte.net
Fri Jan 8 16:41:06 UTC 2016


This is an automated email from the git hooks/post-receive script.

jpuydt-guest pushed a commit to branch master
in repository node-es6-shim.

commit 13ea6c6df849e3fceff182655a97e4661888f9f9
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Fri Jan 8 14:59:21 2016 +0100

    Imported Upstream version 0.34.1+ds
---
 .gitignore          |    2 -
 .jscs.json          |    4 +-
 .jshintrc           |    3 +-
 .npmignore          |    3 -
 .travis.yml         |    4 +
 CHANGELOG.md        |   17 +
 component.json      |    2 +-
 es6-sham.js         |    4 +-
 es6-sham.min.js     |    4 +-
 es6-shim.js         |  396 +++++++++++------
 es6-shim.map        |    2 +-
 es6-shim.min.js     |    8 +-
 package.json        |    6 +-
 test/collections.js | 1173 ---------------------------------------------------
 test/index.html     |    3 +-
 test/map.js         |  605 ++++++++++++++++++++++++++
 test/native.html    |    3 +-
 test/set.js         |  639 ++++++++++++++++++++++++++++
 test/string.js      |    9 +-
 testling.html       |    3 +-
 20 files changed, 1565 insertions(+), 1325 deletions(-)

diff --git a/.gitignore b/.gitignore
index e66145f..646ac51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,2 @@
 .DS_Store
 node_modules/
-components
-build
diff --git a/.jscs.json b/.jscs.json
index a31c73a..f00a97b 100644
--- a/.jscs.json
+++ b/.jscs.json
@@ -147,6 +147,8 @@
 
 	"requireEnhancedObjectLiterals": false,
 
-	"requireObjectDestructuring": false
+	"requireObjectDestructuring": false,
+
+	"requireEarlyReturn": false
 }
 
diff --git a/.jshintrc b/.jshintrc
index 36d4331..d2e7ca6 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -2,5 +2,6 @@
 	"es3": true,
 	"esnext": true,
 	"sub": true,
-	"newcap": false
+	"newcap": false,
+	"notypeof": true
 }
diff --git a/.npmignore b/.npmignore
index d681c02..70cf7b1 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,7 +1,4 @@
 .DS_Store
 node_modules/
-test/
-components
-build
 .travis.yml
 testling.html
diff --git a/.travis.yml b/.travis.yml
index 841474b..3d0baf5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,7 @@
 language: node_js
 node_js:
+  - "5.3"
+  - "5.2"
   - "5.1"
   - "5.0"
   - "4.2"
@@ -51,6 +53,8 @@ matrix:
     - node_js: "0.12"
       env: TRAVIS_RUN_SAUCE=false TRAVIS_RUN_LINT_ONLY=false
   allow_failures:
+    - node_js: "5.2"
+    - node_js: "5.1"
     - node_js: "5.0"
     - node_js: "4.1"
     - node_js: "4.0"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4850d79..5a57682 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
 # es6-shim x.x.x (not yet released)
 
+# es6-shim 0.34.1 (5 Jan 2016)
+* [Fix] `RegExp#[Symbol.search]` was broken with a regex argument (#394)
+* [Fix] ensure that Set#clear works with both primitive and object values
+* [Fix] static Promise methods have the wrong length in Firefox
+* [Robustness] Cache `Object.keys`
+* [Performance] Avoid accessing arguments array without length check
+* [Performance] Optimize ES.TypeIsObject (#388)
+* [Performance] Promises: lots of improvements (#383)
+* [Performance] Only use slow implementation of IsCallable where necessary (old browsers)
+* [Performance] Promises: remove unnecessary `.bind` on `setImmediate`
+* [Refactor] extract “decode fast Map key” logic
+* [Dev Deps] update `s5-shim`, `@ljharb/eslint-config`
+* Don’t npmignore tests
+* [Tests] Fix a bug with “deep equal” wrt NaN
+* [Tests] split up Map and Set test files
+* [Tests] up to `node` `v5.3`
+
 # es6-shim 0.34.0 (14 Dec 2015)
 * [Breaking] Remove `Symbol.species` from `Promise.all` and `Promise.race` (#34)
 * [Fix] Firefox has enumerable Promise static methods
diff --git a/component.json b/component.json
index 22bea44..c971adf 100644
--- a/component.json
+++ b/component.json
@@ -1,6 +1,6 @@
 {
   "name": "es6-shim",
-  "version": "0.34.0",
+  "version": "0.34.1",
   "repo": "paulmillr/es6-shim",
   "description": "ECMAScript 6 (Harmony) compatibility shims for legacy JavaScript engines",
   "keywords": [
diff --git a/es6-sham.js b/es6-sham.js
index d3f16a6..7e49121 100644
--- a/es6-sham.js
+++ b/es6-sham.js
@@ -2,8 +2,8 @@
   * https://github.com/paulmillr/es6-shim
   * @license es6-shim Copyright 2013-2015 by Paul Miller (http://paulmillr.com)
   *   and contributors,  MIT License
-  * es6-sham: v0.34.0
-  * see https://github.com/paulmillr/es6-shim/blob/0.34.0/LICENSE
+  * es6-sham: v0.34.1
+  * see https://github.com/paulmillr/es6-shim/blob/0.34.1/LICENSE
   * Details and documentation:
   * https://github.com/paulmillr/es6-shim/
   */
diff --git a/es6-sham.min.js b/es6-sham.min.js
index 909edd8..1dda0d0 100644
--- a/es6-sham.min.js
+++ b/es6-sham.min.js
@@ -2,8 +2,8 @@
   * https://github.com/paulmillr/es6-shim
   * @license es6-shim Copyright 2013-2015 by Paul Miller (http://paulmillr.com)
   *   and contributors,  MIT License
-  * es6-sham: v0.34.0
-  * see https://github.com/paulmillr/es6-shim/blob/0.34.0/LICENSE
+  * es6-sham: v0.34.1
+  * see https://github.com/paulmillr/es6-shim/blob/0.34.1/LICENSE
   * Details and documentation:
   * https://github.com/paulmillr/es6-shim/
   */
diff --git a/es6-shim.js b/es6-shim.js
index 18b90be..f6058ae 100644
--- a/es6-shim.js
+++ b/es6-shim.js
@@ -2,8 +2,8 @@
   * https://github.com/paulmillr/es6-shim
   * @license es6-shim Copyright 2013-2015 by Paul Miller (http://paulmillr.com)
   *   and contributors,  MIT License
-  * es6-shim: v0.34.0
-  * see https://github.com/paulmillr/es6-shim/blob/0.34.0/LICENSE
+  * es6-shim: v0.34.1
+  * see https://github.com/paulmillr/es6-shim/blob/0.34.1/LICENSE
   * Details and documentation:
   * https://github.com/paulmillr/es6-shim/
   */
@@ -30,6 +30,7 @@
   var _apply = Function.call.bind(Function.apply);
   var _call = Function.call.bind(Function.call);
   var isArray = Array.isArray;
+  var keys = Object.keys;
 
   var not = function notThunker(func) {
     return function notThunk() { return !_apply(func, this, arguments); };
@@ -80,17 +81,17 @@
   // Define configurable, writable and non-enumerable props
   // if they don’t exist.
   var defineProperties = function (object, map, forceOverride) {
-    _forEach(Object.keys(map), function (name) {
+    _forEach(keys(map), function (name) {
       var method = map[name];
       defineProperty(object, name, method, !!forceOverride);
     });
   };
 
   var _toString = Function.call.bind(Object.prototype.toString);
-  var isCallable = function isCallable(x) {
-    // some old browsers (IE, FF) say that typeof /abc/ === 'function'
+  var isCallable = typeof /abc/ === 'function' ? function IsCallableSlow(x) {
+    // Some old browsers (IE, FF) say that typeof /abc/ === 'function'
     return typeof x === 'function' && _toString(x) === '[object Function]';
-  };
+  } : function IsCallableFast(x) { return typeof x === 'function'; };
 
   var Value = {
     getter: function (object, name, getter) {
@@ -145,7 +146,7 @@
     Prototype.prototype = prototype;
     var object = new Prototype();
     if (typeof properties !== 'undefined') {
-      Object.keys(properties).forEach(function (key) {
+      keys(properties).forEach(function (key) {
         Value.defineByDescriptor(object, key, properties[key]);
       });
     }
@@ -285,11 +286,20 @@
       return x;
     },
 
+    // This might miss the "(non-standard exotic and does not implement
+    // [[Call]])" case from
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-typeof-operator-runtime-semantics-evaluation
+    // but we can't find any evidence these objects exist in practice.
+    // If we find some in the future, you could test `Object(x) === x`,
+    // which is reliable according to
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-toobject
+    // but is not well optimized by runtimes and creates an object
+    // whenever it returns false, and thus is very slow.
     TypeIsObject: function (x) {
-      /* jshint eqnull:true */
-      // this is expensive when it returns false; use this function
-      // when you expect it to return true in the common case.
-      return x != null && Object(x) === x;
+      if (x === void 0 || x === null || x === true || x === false) {
+        return false;
+      }
+      return typeof x === 'function' || typeof x === 'object';
     },
 
     ToObject: function (o, optMessage) {
@@ -506,7 +516,7 @@
       var symbolSearch = defineWellKnownSymbol('search');
       var originalSearch = String.prototype.search;
       defineProperty(RegExp.prototype, symbolSearch, function search(string) {
-        return ES.Call(originalSearch, string, [ES.ToString(this)]);
+        return ES.Call(originalSearch, string, [this]);
       });
       var searchShim = function search(regexp) {
         var O = ES.RequireObjectCoercible(this);
@@ -752,27 +762,33 @@
     },
 
     startsWith: function startsWith(searchString) {
-      var thisStr = ES.ToString(ES.RequireObjectCoercible(this));
+      var S = ES.ToString(ES.RequireObjectCoercible(this));
       if (ES.IsRegExp(searchString)) {
         throw new TypeError('Cannot call method "startsWith" with a regex');
       }
       var searchStr = ES.ToString(searchString);
-      var startArg = arguments.length > 1 ? arguments[1] : void 0;
-      var start = _max(ES.ToInteger(startArg), 0);
-      return _strSlice(thisStr, start, start + searchStr.length) === searchStr;
+      var position;
+      if (arguments.length > 1) {
+        position = arguments[1];
+      }
+      var start = _max(ES.ToInteger(position), 0);
+      return _strSlice(S, start, start + searchStr.length) === searchStr;
     },
 
     endsWith: function endsWith(searchString) {
-      var thisStr = ES.ToString(ES.RequireObjectCoercible(this));
+      var S = ES.ToString(ES.RequireObjectCoercible(this));
       if (ES.IsRegExp(searchString)) {
         throw new TypeError('Cannot call method "endsWith" with a regex');
       }
       var searchStr = ES.ToString(searchString);
-      var thisLen = thisStr.length;
-      var posArg = arguments.length > 1 ? arguments[1] : void 0;
-      var pos = typeof posArg === 'undefined' ? thisLen : ES.ToInteger(posArg);
-      var end = _min(_max(pos, 0), thisLen);
-      return _strSlice(thisStr, end - searchStr.length, end) === searchStr;
+      var len = S.length;
+      var endPosition;
+      if (arguments.length > 1) {
+        endPosition = arguments[1];
+      }
+      var pos = typeof endPosition === 'undefined' ? len : ES.ToInteger(endPosition);
+      var end = _min(_max(pos, 0), len);
+      return _strSlice(S, end - searchStr.length, end) === searchStr;
     },
 
     includes: function includes(searchString) {
@@ -894,15 +910,20 @@
   var ArrayShims = {
     from: function from(items) {
       var C = this;
-      var mapFn = arguments.length > 1 ? arguments[1] : void 0;
+      var mapFn;
+      if (arguments.length > 1) {
+        mapFn = arguments[1];
+      }
       var mapping, T;
-      if (mapFn === void 0) {
+      if (typeof mapFn === 'undefined') {
         mapping = false;
       } else {
         if (!ES.IsCallable(mapFn)) {
           throw new TypeError('Array.from: when provided, the second argument must be a function');
         }
-        T = arguments.length > 2 ? arguments[2] : void 0;
+        if (arguments.length > 2) {
+          T = arguments[2];
+        }
         mapping = true;
       }
 
@@ -925,7 +946,7 @@
           nextValue = next.value;
           try {
             if (mapping) {
-              nextValue = T === undefined ? mapFn(nextValue, i) : _call(mapFn, T, nextValue, i);
+              nextValue = typeof T === 'undefined' ? mapFn(nextValue, i) : _call(mapFn, T, nextValue, i);
             }
             result[i] = nextValue;
           } catch (e) {
@@ -943,7 +964,7 @@
         for (i = 0; i < length; ++i) {
           value = arrayLike[i];
           if (mapping) {
-            value = T !== undefined ? _call(mapFn, T, value, i) : mapFn(value, i);
+            value = typeof T === 'undefined' ? mapFn(value, i) : _call(mapFn, T, value, i);
           }
           result[i] = value;
         }
@@ -1092,16 +1113,19 @@
 
   var ArrayPrototypeShims = {
     copyWithin: function copyWithin(target, start) {
-      var end = arguments[2]; // copyWithin.length must be 2
       var o = ES.ToObject(this);
       var len = ES.ToLength(o.length);
       var relativeTarget = ES.ToInteger(target);
       var relativeStart = ES.ToInteger(start);
       var to = relativeTarget < 0 ? _max(len + relativeTarget, 0) : _min(relativeTarget, len);
       var from = relativeStart < 0 ? _max(len + relativeStart, 0) : _min(relativeStart, len);
-      end = typeof end === 'undefined' ? len : ES.ToInteger(end);
-      var fin = end < 0 ? _max(len + end, 0) : _min(end, len);
-      var count = _min(fin - from, len - to);
+      var end;
+      if (arguments.length > 2) {
+        end = arguments[2];
+      }
+      var relativeEnd = typeof end === 'undefined' ? len : ES.ToInteger(end);
+      var finalItem = relativeEnd < 0 ? _max(len + relativeEnd, 0) : _min(relativeEnd, len);
+      var count = _min(finalItem - from, len - to);
       var direction = 1;
       if (from < to && to < (from + count)) {
         direction = -1;
@@ -1122,8 +1146,14 @@
     },
 
     fill: function fill(value) {
-      var start = arguments.length > 1 ? arguments[1] : void 0;
-      var end = arguments.length > 2 ? arguments[2] : void 0;
+      var start;
+      if (arguments.length > 1) {
+        start = arguments[1];
+      }
+      var end;
+      if (arguments.length > 2) {
+        end = arguments[2];
+      }
       var O = ES.ToObject(this);
       var len = ES.ToLength(O.length);
       start = ES.ToInteger(typeof start === 'undefined' ? 0 : start);
@@ -1234,12 +1264,12 @@
   var arrayFromHandlesUndefinedMapFunction = (function () {
     // Microsoft Edge v0.11 throws if the mapFn argument is *provided* but undefined,
     // but the spec doesn't care if it's provided or not - undefined doesn't throw.
-    return valueOrFalseIfThrows(function () { return Array.from([0], undefined); });
+    return valueOrFalseIfThrows(function () { return Array.from([0], void 0); });
   }());
   if (!arrayFromHandlesUndefinedMapFunction) {
     var origArrayFrom = Array.from;
     overrideNative(Array, 'from', function from(items) {
-      if (arguments.length > 0 && typeof arguments[1] !== 'undefined') {
+      if (arguments.length > 1 && typeof arguments[1] !== 'undefined') {
         return ES.Call(origArrayFrom, this, arguments);
       } else {
         return _call(origArrayFrom, this, items);
@@ -1439,12 +1469,12 @@
     };
   };
   var assignReducer = function (target, source) {
-    var keys = Object.keys(Object(source));
+    var sourceKeys = keys(Object(source));
     var symbols;
     if (ES.IsCallable(Object.getOwnPropertySymbols)) {
       symbols = _filter(Object.getOwnPropertySymbols(Object(source)), isEnumerableOn(source));
     }
-    return _reduce(_concat(keys, symbols || []), assignTo(source), target);
+    return _reduce(_concat(sourceKeys, symbols || []), assignTo(source), target);
   };
 
   var ObjectShims = {
@@ -1559,6 +1589,7 @@
     overrideNative(Object, 'keys', function keys(value) {
       return originalObjectKeys(ES.ToObject(value));
     });
+    keys = Object.keys;
   }
 
   if (Object.getOwnPropertyNames) {
@@ -1747,7 +1778,7 @@
       leftContext: '$`',
       rightContext: '$\''
     };
-    _forEach(Object.keys(regexGlobals), function (prop) {
+    _forEach(keys(regexGlobals), function (prop) {
       if (prop in RegExp && !(regexGlobals[prop] in RegExp)) {
         Value.getter(RegExp, regexGlobals[prop], function get() {
           return RegExp[prop];
@@ -2033,6 +2064,9 @@
         capability.resolve = resolve;
         capability.reject = reject;
       };
+      // Initialize fields to inform optimizers about the object shape.
+      capability.resolve = void 0;
+      capability.reject = void 0;
       capability.promise = new C(resolver);
       if (!(ES.IsCallable(capability.resolve) && ES.IsCallable(capability.reject))) {
         throw new TypeError('Bad promise constructor');
@@ -2069,14 +2103,15 @@
       // global Promise below (in order to workaround bugs)
       // https://github.com/Raynos/observ-hash/issues/2#issuecomment-35857671
       var P = globals.Promise;
-      return P && P.resolve && function (task) {
-        return P.resolve().then(task);
+      var pr = P && P.resolve && P.resolve();
+      return pr && function (task) {
+        return pr.then(task);
       };
     };
     /*global process */
     /* jscs:disable disallowMultiLineTernary */
     var enqueue = ES.IsCallable(globals.setImmediate) ?
-      globals.setImmediate.bind(globals) :
+      globals.setImmediate :
       typeof process === 'object' && process.nextTick ? process.nextTick :
       makePromiseAsap() ||
       (ES.IsCallable(makeZeroTimeout) ? makeZeroTimeout() :
@@ -2084,59 +2119,99 @@
     /* jscs:enable disallowMultiLineTernary */
 
     // Constants for Promise implementation
-    var PROMISE_IDENTITY = 1;
-    var PROMISE_THROWER = 2;
-    var PROMISE_PENDING = 3;
-    var PROMISE_FULFILLED = 4;
-    var PROMISE_REJECTED = 5;
-
-    var promiseReactionJob = function (reaction, argument) {
-      var promiseCapability = reaction.capabilities;
-      var handler = reaction.handler;
-      var handlerResult, handlerException = false, f;
-      if (handler === PROMISE_IDENTITY) {
-        handlerResult = argument;
-      } else if (handler === PROMISE_THROWER) {
-        handlerResult = argument;
-        handlerException = true;
-      } else {
-        try {
-          handlerResult = handler(argument);
-        } catch (e) {
-          handlerResult = e;
-          handlerException = true;
-        }
-      }
-      f = handlerException ? promiseCapability.reject : promiseCapability.resolve;
-      f(handlerResult);
+    var PROMISE_IDENTITY = function (x) { return x; };
+    var PROMISE_THROWER = function (e) { throw e; };
+    var PROMISE_PENDING = 0;
+    var PROMISE_FULFILLED = 1;
+    var PROMISE_REJECTED = 2;
+    // We store fulfill/reject handlers and capabilities in a single array.
+    var PROMISE_FULFILL_OFFSET = 0;
+    var PROMISE_REJECT_OFFSET = 1;
+    var PROMISE_CAPABILITY_OFFSET = 2;
+    // This is used in an optimization for chaining promises via then.
+    var PROMISE_FAKE_CAPABILITY = {};
+
+    var enqueuePromiseReactionJob = function (handler, capability, argument) {
+      enqueue(function () {
+        promiseReactionJob(handler, capability, argument);
+      });
     };
 
-    var triggerPromiseReactions = function (reactions, argument) {
-      _forEach(reactions, function (reaction) {
-        enqueue(function () {
-          promiseReactionJob(reaction, argument);
-        });
-      });
+    var promiseReactionJob = function (handler, promiseCapability, argument) {
+      var handlerResult, f;
+      if (promiseCapability === PROMISE_FAKE_CAPABILITY) {
+        // Fast case, when we don't actually need to chain through to a
+        // (real) promiseCapability.
+        return handler(argument);
+      }
+      try {
+        handlerResult = handler(argument);
+        f = promiseCapability.resolve;
+      } catch (e) {
+        handlerResult = e;
+        f = promiseCapability.reject;
+      }
+      f(handlerResult);
     };
 
     var fulfillPromise = function (promise, value) {
       var _promise = promise._promise;
-      var reactions = _promise.fulfillReactions;
+      var length = _promise.reactionLength;
+      if (length > 0) {
+        enqueuePromiseReactionJob(
+          _promise.fulfillReactionHandler0,
+          _promise.reactionCapability0,
+          value
+        );
+        _promise.fulfillReactionHandler0 = void 0;
+        _promise.rejectReactions0 = void 0;
+        _promise.reactionCapability0 = void 0;
+        if (length > 1) {
+          for (var i = 1, idx = 0; i < length; i++, idx += 3) {
+            enqueuePromiseReactionJob(
+              _promise[idx + PROMISE_FULFILL_OFFSET],
+              _promise[idx + PROMISE_CAPABILITY_OFFSET],
+              value
+            );
+            promise[idx + PROMISE_FULFILL_OFFSET] = void 0;
+            promise[idx + PROMISE_REJECT_OFFSET] = void 0;
+            promise[idx + PROMISE_CAPABILITY_OFFSET] = void 0;
+          }
+        }
+      }
       _promise.result = value;
-      _promise.fulfillReactions = void 0;
-      _promise.rejectReactions = void 0;
       _promise.state = PROMISE_FULFILLED;
-      triggerPromiseReactions(reactions, value);
+      _promise.reactionLength = 0;
     };
 
     var rejectPromise = function (promise, reason) {
       var _promise = promise._promise;
-      var reactions = _promise.rejectReactions;
+      var length = _promise.reactionLength;
+      if (length > 0) {
+        enqueuePromiseReactionJob(
+          _promise.rejectReactionHandler0,
+          _promise.reactionCapability0,
+          reason
+        );
+        _promise.fulfillReactionHandler0 = void 0;
+        _promise.rejectReactions0 = void 0;
+        _promise.reactionCapability0 = void 0;
+        if (length > 1) {
+          for (var i = 1, idx = 0; i < length; i++, idx += 3) {
+            enqueuePromiseReactionJob(
+              _promise[idx + PROMISE_REJECT_OFFSET],
+              _promise[idx + PROMISE_CAPABILITY_OFFSET],
+              reason
+            );
+            promise[idx + PROMISE_FULFILL_OFFSET] = void 0;
+            promise[idx + PROMISE_REJECT_OFFSET] = void 0;
+            promise[idx + PROMISE_CAPABILITY_OFFSET] = void 0;
+          }
+        }
+      }
       _promise.result = reason;
-      _promise.fulfillReactions = void 0;
-      _promise.rejectReactions = void 0;
       _promise.state = PROMISE_REJECTED;
-      triggerPromiseReactions(reactions, reason);
+      _promise.reactionLength = 0;
     };
 
     var createResolvingFunctions = function (promise) {
@@ -2171,18 +2246,29 @@
       return { resolve: resolve, reject: reject };
     };
 
+    var optimizedThen = function (then, thenable, resolve, reject) {
+      // Optimization: since we discard the result, we can pass our
+      // own then implementation a special hint to let it know it
+      // doesn't have to create it.  (The PROMISE_FAKE_CAPABILITY
+      // object is local to this implementation and unforgeable outside.)
+      if (then === Promise$prototype$then) {
+        _call(then, thenable, resolve, reject, PROMISE_FAKE_CAPABILITY);
+      } else {
+        _call(then, thenable, resolve, reject);
+      }
+    };
     var promiseResolveThenableJob = function (promise, thenable, then) {
       var resolvingFunctions = createResolvingFunctions(promise);
       var resolve = resolvingFunctions.resolve;
       var reject = resolvingFunctions.reject;
       try {
-        _call(then, thenable, resolve, reject);
+        optimizedThen(then, thenable, resolve, reject);
       } catch (e) {
         reject(e);
       }
     };
 
-    var Promise$prototype;
+    var Promise$prototype, Promise$prototype$then;
     var Promise = (function () {
       var PromiseShim = function Promise(resolver) {
         if (!(this instanceof PromiseShim)) {
@@ -2199,8 +2285,15 @@
           _promise: {
             result: void 0,
             state: PROMISE_PENDING,
-            fulfillReactions: [],
-            rejectReactions: []
+            // The first member of the "reactions" array is inlined here,
+            // since most promises only have one reaction.
+            // We've also exploded the 'reaction' object to inline the
+            // "handler" and "capability" fields, since both fulfill and
+            // reject reactions share the same capability.
+            reactionLength: 0,
+            fulfillReactionHandler0: void 0,
+            rejectReactionHandler0: void 0,
+            reactionCapability0: void 0
           }
         });
         var resolvingFunctions = createResolvingFunctions(promise);
@@ -2251,7 +2344,7 @@
           index, values, resultCapability, remaining
         );
         remaining.count += 1;
-        nextPromise.then(resolveElement, resultCapability.reject);
+        optimizedThen(nextPromise.then, nextPromise, resolveElement, resultCapability.reject);
         index += 1;
       }
       if ((--remaining.count) === 0) {
@@ -2280,7 +2373,7 @@
           throw e;
         }
         nextPromise = C.resolve(nextValue);
-        nextPromise.then(resultCapability.resolve, resultCapability.reject);
+        optimizedThen(nextPromise.then, nextPromise, resultCapability.resolve, resultCapability.reject);
       }
       return resultCapability.promise;
     };
@@ -2368,44 +2461,60 @@
 
     defineProperties(Promise$prototype, {
       'catch': function (onRejected) {
-        return this.then(void 0, onRejected);
+        return this.then(null, onRejected);
       },
 
       then: function then(onFulfilled, onRejected) {
         var promise = this;
         if (!ES.IsPromise(promise)) { throw new TypeError('not a promise'); }
         var C = ES.SpeciesConstructor(promise, Promise);
-        var resultCapability = new PromiseCapability(C);
+        var resultCapability;
+        var returnValueIsIgnored = arguments.length > 2 && arguments[2] === PROMISE_FAKE_CAPABILITY;
+        if (returnValueIsIgnored && C === Promise) {
+          resultCapability = PROMISE_FAKE_CAPABILITY;
+        } else {
+          resultCapability = new PromiseCapability(C);
+        }
         // PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability)
-        var fulfillReaction = {
-          capabilities: resultCapability,
-          handler: ES.IsCallable(onFulfilled) ? onFulfilled : PROMISE_IDENTITY
-        };
-        var rejectReaction = {
-          capabilities: resultCapability,
-          handler: ES.IsCallable(onRejected) ? onRejected : PROMISE_THROWER
-        };
+        // Note that we've split the 'reaction' object into its two
+        // components, "capabilities" and "handler"
+        // "capabilities" is always equal to `resultCapability`
+        var fulfillReactionHandler = ES.IsCallable(onFulfilled) ? onFulfilled : PROMISE_IDENTITY;
+        var rejectReactionHandler = ES.IsCallable(onRejected) ? onRejected : PROMISE_THROWER;
         var _promise = promise._promise;
         var value;
         if (_promise.state === PROMISE_PENDING) {
-          _push(_promise.fulfillReactions, fulfillReaction);
-          _push(_promise.rejectReactions, rejectReaction);
+          if (_promise.reactionLength === 0) {
+            _promise.fulfillReactionHandler0 = fulfillReactionHandler;
+            _promise.rejectReactionHandler0 = rejectReactionHandler;
+            _promise.reactionCapability0 = resultCapability;
+          } else {
+            var idx = 3 * (_promise.reactionLength - 1);
+            _promise[idx + PROMISE_FULFILL_OFFSET] = fulfillReactionHandler;
+            _promise[idx + PROMISE_REJECT_OFFSET] = rejectReactionHandler;
+            _promise[idx + PROMISE_CAPABILITY_OFFSET] = resultCapability;
+          }
+          _promise.reactionLength += 1;
         } else if (_promise.state === PROMISE_FULFILLED) {
           value = _promise.result;
-          enqueue(function () {
-            promiseReactionJob(fulfillReaction, value);
-          });
+          enqueuePromiseReactionJob(
+            fulfillReactionHandler, resultCapability, value
+          );
         } else if (_promise.state === PROMISE_REJECTED) {
           value = _promise.result;
-          enqueue(function () {
-            promiseReactionJob(rejectReaction, value);
-          });
+          enqueuePromiseReactionJob(
+            rejectReactionHandler, resultCapability, value
+          );
         } else {
           throw new TypeError('unexpected Promise state');
         }
         return resultCapability.promise;
       }
     });
+    // This helps the optimizer by ensuring that methods which take
+    // capabilities aren't polymorphic.
+    PROMISE_FAKE_CAPABILITY = new PromiseCapability(Promise);
+    Promise$prototype$then = Promise$prototype.then;
 
     return Promise;
   }());
@@ -2471,6 +2580,30 @@
       /*globals Promise: false */
       overrideNative(globals, 'Promise', PromiseShim);
     }
+    if (Promise.all.length !== 1) {
+      var origAll = Promise.all;
+      overrideNative(Promise, 'all', function all(iterable) {
+        return ES.Call(origAll, this, arguments);
+      });
+    }
+    if (Promise.race.length !== 1) {
+      var origRace = Promise.race;
+      overrideNative(Promise, 'race', function race(iterable) {
+        return ES.Call(origRace, this, arguments);
+      });
+    }
+    if (Promise.resolve.length !== 1) {
+      var origResolve = Promise.resolve;
+      overrideNative(Promise, 'resolve', function resolve(x) {
+        return ES.Call(origResolve, this, arguments);
+      });
+    }
+    if (Promise.reject.length !== 1) {
+      var origReject = Promise.reject;
+      overrideNative(Promise, 'reject', function reject(r) {
+        return ES.Call(origReject, this, arguments);
+      });
+    }
     ensureEnumerable(Promise, 'all');
     ensureEnumerable(Promise, 'race');
     ensureEnumerable(Promise, 'resolve');
@@ -2482,7 +2615,7 @@
   // Their fast path also requires that the environment preserve
   // property insertion order, which is not guaranteed by the spec.
   var testOrder = function (a) {
-    var b = Object.keys(_reduce(a, function (o, k) {
+    var b = keys(_reduce(a, function (o, k) {
       o[k] = true;
       return o;
     }, {}));
@@ -2870,36 +3003,42 @@
         };
         Set$prototype = SetShim.prototype;
 
+        var decodeKey = function (key) {
+          var k = key;
+          if (k === '^null') {
+            return null;
+          } else if (k === '^undefined') {
+            return void 0;
+          } else {
+            var first = k.charAt(0);
+            if (first === '$') {
+              return _strSlice(k, 1);
+            } else if (first === 'n') {
+              return +_strSlice(k, 1);
+            } else if (first === 'b') {
+              return k === 'btrue';
+            }
+          }
+          return +k;
+        };
         // Switch from the object backing storage to a full Map.
         var ensureMap = function ensureMap(set) {
           if (!set['[[SetData]]']) {
             var m = set['[[SetData]]'] = new collectionShims.Map();
-            _forEach(Object.keys(set._storage), function (key) {
-              var k = key;
-              if (k === '^null') {
-                k = null;
-              } else if (k === '^undefined') {
-                k = void 0;
-              } else {
-                var first = k.charAt(0);
-                if (first === '$') {
-                  k = _strSlice(k, 1);
-                } else if (first === 'n') {
-                  k = +_strSlice(k, 1);
-                } else if (first === 'b') {
-                  k = k === 'btrue';
-                } else {
-                  k = +k;
-                }
-              }
+            _forEach(keys(set._storage), function (key) {
+              var k = decodeKey(key);
               m.set(k, k);
             });
-            set._storage = null; // free old backing storage
+            set['[[SetData]]'] = m;
           }
+          set._storage = null; // free old backing storage
         };
 
         Value.getter(SetShim.prototype, 'size', function () {
           requireSetSlot(this, 'size');
+          if (this._storage) {
+            return keys(this._storage).length;
+          }
           ensureMap(this);
           return this['[[SetData]]'].size;
         });
@@ -2942,7 +3081,8 @@
             requireSetSlot(this, 'clear');
             if (this._storage) {
               this._storage = emptyObject();
-            } else {
+            }
+            if (this['[[SetData]]']) {
               this['[[SetData]]'].clear();
             }
           },
@@ -3194,7 +3334,7 @@
       if (!ES.IsConstructor(constructor)) {
         throw new TypeError('First argument must be a constructor.');
       }
-      var newTarget = arguments.length < 3 ? constructor : arguments[2];
+      var newTarget = arguments.length > 2 ? arguments[2] : constructor;
       if (!ES.IsConstructor(newTarget)) {
         throw new TypeError('new.target must be a constructor.');
       }
@@ -3277,7 +3417,7 @@
         var parent = Object.getPrototypeOf(target);
 
         if (parent === null) {
-          return undefined;
+          return void 0;
         }
 
         return internalGet(parent, key, receiver);
@@ -3291,7 +3431,7 @@
         return ES.Call(desc.get, receiver);
       }
 
-      return undefined;
+      return void 0;
     };
 
     var internalSet = function set(target, key, value, receiver) {
diff --git a/es6-shim.map b/es6-shim.map
index 811d7f8..00277e7 100644
--- a/es6-shim.map
+++ b/es6-shim.map
@@ -1 +1 @@
-{"version":3,"sources":["es6-shim.js"],"names":["root","factory","define","amd","exports","module","returnExports","this","_apply","Function","call","bind","apply","_call","isArray","Array","not","notThunker","func","notThunk","arguments","throwsError","e","valueOrFalseIfThrows","isCallableWithoutNew","arePropertyDescriptorsSupported","Object","defineProperty","get","supportsDescriptors","functionsHaveNames","foo","name","_forEach","prototype","forEach","_reduce","reduce","_filter","filt [...]
\ No newline at end of file
+{"version":3,"sources":["es6-shim.js"],"names":["root","factory","define","amd","exports","module","returnExports","this","_apply","Function","call","bind","apply","_call","isArray","Array","keys","Object","not","notThunker","func","notThunk","arguments","throwsError","e","valueOrFalseIfThrows","isCallableWithoutNew","arePropertyDescriptorsSupported","defineProperty","get","supportsDescriptors","functionsHaveNames","foo","name","_forEach","prototype","forEach","_reduce","reduce","_filter [...]
\ No newline at end of file
diff --git a/es6-shim.min.js b/es6-shim.min.js
index 8bd12d3..00241b8 100644
--- a/es6-shim.min.js
+++ b/es6-shim.min.js
@@ -2,11 +2,11 @@
   * https://github.com/paulmillr/es6-shim
   * @license es6-shim Copyright 2013-2015 by Paul Miller (http://paulmillr.com)
   *   and contributors,  MIT License
-  * es6-shim: v0.34.0
-  * see https://github.com/paulmillr/es6-shim/blob/0.34.0/LICENSE
+  * es6-shim: v0.34.1
+  * see https://github.com/paulmillr/es6-shim/blob/0.34.1/LICENSE
   * Details and documentation:
   * https://github.com/paulmillr/es6-shim/
   */
-(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else if(typeof exports==="object"){module.exports=t()}else{e.returnExports=t()}})(this,function(){"use strict";var e=Function.call.bind(Function.apply);var t=Function.call.bind(Function.call);var r=Array.isArray;var n=function notThunker(t){return function notThunk(){return!e(t,this,arguments)}};var o=function(e){try{e();return false}catch(t){return true}};var i=function valueOrFalseIfThrows(e){try{return e()}catch(t){re [...]
-throw new TypeError("Bad Promise implementation!")}t.resolve=e;t.reject=r};t.promise=new e(r);if(!(Y.IsCallable(t.resolve)&&Y.IsCallable(t.reject))){throw new TypeError("Bad promise constructor")}};var n;if(typeof window!=="undefined"&&Y.IsCallable(window.postMessage)){n=function(){var e=[];var t="zero-timeout-message";var r=function(r){P(e,r);window.postMessage(t,"*")};var n=function(r){if(r.source===window&&r.data===t){r.stopPropagation();if(e.length===0){return}var n=x(e);n()}};window [...]
+(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else if(typeof exports==="object"){module.exports=t()}else{e.returnExports=t()}})(this,function(){"use strict";var e=Function.call.bind(Function.apply);var t=Function.call.bind(Function.call);var r=Array.isArray;var n=Object.keys;var o=function notThunker(t){return function notThunk(){return!e(t,this,arguments)}};var i=function(e){try{e();return false}catch(t){return true}};var a=function valueOrFalseIfThrows(e){try{retu [...]
+if(typeof e!=="function"&&typeof e!=="object"){return}ee.IsPromise=function(e){if(!ee.TypeIsObject(e)){return false}if(typeof e._promise==="undefined"){return false}return true};var r=function(e){if(!ee.IsConstructor(e)){throw new TypeError("Bad promise constructor")}var t=this;var r=function(e,r){if(t.resolve!==void 0||t.reject!==void 0){throw new TypeError("Bad Promise implementation!")}t.resolve=e;t.reject=r};t.resolve=void 0;t.reject=void 0;t.promise=new e(r);if(!(ee.IsCallable(t.res [...]
 //# sourceMappingURL=es6-shim.map
diff --git a/package.json b/package.json
index aee8525..feca237 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "es6-shim",
-  "version": "0.34.0",
+  "version": "0.34.1",
   "author": "Paul Miller (http://paulmillr.com)",
   "description": "ECMAScript 6 (Harmony) compatibility shims for legacy JavaScript engines",
   "keywords": [
@@ -68,9 +68,9 @@
   "dependencies": {},
   "devDependencies": {
     "chai": "^3.4.1",
-    "es5-shim": "^4.4.0",
+    "es5-shim": "^4.4.1",
     "eslint": "^1.10.3",
-    "@ljharb/eslint-config": "^1.6.0",
+    "@ljharb/eslint-config": "^1.6.1",
     "grunt": "^0.4.5",
     "grunt-contrib-connect": "^0.11.2",
     "grunt-contrib-watch": "^0.6.1",
diff --git a/test/collections.js b/test/collections.js
deleted file mode 100644
index 7a48f34..0000000
--- a/test/collections.js
+++ /dev/null
@@ -1,1173 +0,0 @@
-/* global describe, it, xit, expect, require, beforeEach, afterEach */
-
-// Big thanks to V8 folks for test ideas.
-// v8/test/mjsunit/harmony/collections.js
-
-var Assertion = expect().constructor;
-Assertion.addMethod('theSameSet', function (otherArray) {
-  var array = this._obj;
-
-  expect(Array.isArray(array)).to.equal(true);
-  expect(Array.isArray(otherArray)).to.equal(true);
-
-  var diff = array.filter(function (value) {
-    return otherArray.every(function (otherValue) {
-      var areBothNaN = typeof value === 'number' && typeof otherValue === 'number' && value !== value && otherValue !== otherValue;
-      return !areBothNaN && value !== otherValue;
-    });
-  });
-
-  this.assert(
-    diff.length === 0,
-    'expected #{this} to be equal to #{exp} (as sets, i.e. no order)',
-    array,
-    otherArray
-  );
-});
-
-Assertion.addMethod('entries', function (expected) {
-  var collection = this._obj;
-
-  expect(Array.isArray(expected)).to.equal(true);
-  var expectedEntries = expected.slice();
-
-  var iterator = collection.entries();
-  var result;
-  do {
-    result = iterator.next();
-    expect(result.value).to.be.eql(expectedEntries.shift());
-  } while (!result.done);
-});
-
-describe('Collections', function () {
-  var functionsHaveNames = (function foo() {}).name === 'foo';
-  var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
-  var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
-
-  var range = function (from, to) {
-    var result = [];
-    for (var value = from; value < to; value++) {
-      result.push(value);
-    }
-    return result;
-  };
-
-  var prototypePropIsEnumerable = (function () {}).propertyIsEnumerable('prototype');
-  var expectNotEnumerable = function (object) {
-    if (prototypePropIsEnumerable && typeof object === 'function') {
-      expect(Object.keys(object)).to.eql(['prototype']);
-    } else {
-      expect(Object.keys(object)).to.eql([]);
-    }
-  };
-
-  var Sym = typeof Symbol === 'undefined' ? {} : Symbol;
-  var isSymbol = function (sym) {
-    return typeof Sym === 'function' && typeof sym === 'symbol';
-  };
-  var ifSymbolIteratorIt = isSymbol(Sym.iterator) ? it : xit;
-
-  var testMapping = function (map, key, value) {
-    expect(map.has(key)).to.equal(false);
-    expect(map.get(key)).to.equal(undefined);
-    expect(map.set(key, value)).to.equal(map);
-    expect(map.get(key)).to.equal(value);
-    expect(map.has(key)).to.equal(true);
-  };
-
-  describe('Map', function () {
-    if (typeof Map === 'undefined') {
-      return it('exists', function () {
-        expect(typeof Map).to.equal('function');
-      });
-    }
-
-    var map;
-    beforeEach(function () {
-      map = new Map();
-    });
-
-    afterEach(function () {
-      map = null;
-    });
-
-    ifShimIt('is on the exported object', function () {
-      var exported = require('../');
-      expect(exported.Map).to.equal(Map);
-    });
-
-    it('should exist in global namespace', function () {
-      expect(typeof Map).to.equal('function');
-    });
-
-    it('should have the right arity', function () {
-      expect(Map).to.have.property('length', 0);
-    });
-
-    it('should has valid getter and setter calls', function () {
-      ['get', 'set', 'has', 'delete'].forEach(function (method) {
-        expect(function () {
-          map[method]({});
-        }).to.not['throw']();
-      });
-    });
-
-    it('should accept an iterable as argument', function () {
-      testMapping(map, 'a', 'b');
-      testMapping(map, 'c', 'd');
-      var map2;
-      expect(function () { map2 = new Map(map); }).not.to['throw'](Error);
-      expect(map2).to.be.an.instanceOf(Map);
-      expect(map2.has('a')).to.equal(true);
-      expect(map2.has('c')).to.equal(true);
-      expect(map2).to.have.entries([['a', 'b'], ['c', 'd']]);
-    });
-
-    it('should throw with iterables that return primitives', function () {
-      expect(function () { return new Map('123'); }).to['throw'](TypeError);
-      expect(function () { return new Map([1, 2, 3]); }).to['throw'](TypeError);
-      expect(function () { return new Map(['1', '2', '3']); }).to['throw'](TypeError);
-      expect(function () { return new Map([true]); }).to['throw'](TypeError);
-    });
-
-    it('should not be callable without "new"', function () {
-      expect(Map).to['throw'](TypeError);
-    });
-
-    it('should be subclassable', function () {
-      if (!Object.setPrototypeOf) { return; } // skip test if on IE < 11
-      var MyMap = function MyMap() {
-        var testMap = new Map([['a', 'b']]);
-        Object.setPrototypeOf(testMap, MyMap.prototype);
-        return testMap;
-      };
-      Object.setPrototypeOf(MyMap, Map);
-      MyMap.prototype = Object.create(Map.prototype, {
-        constructor: { value: MyMap }
-      });
-
-      var myMap = new MyMap();
-      testMapping(myMap, 'c', 'd');
-      expect(myMap).to.have.entries([['a', 'b'], ['c', 'd']]);
-    });
-
-    it('uses SameValueZero even on a Map of size > 4', function () {
-      // Chrome 38-42, node 0.11/0.12, iojs 1/2 have a bug when the Map has a size > 4
-      var firstFour = [[1, 0], [2, 0], [3, 0], [4, 0]];
-      var fourMap = new Map(firstFour);
-      expect(fourMap.size).to.equal(4);
-      expect(fourMap.has(-0)).to.equal(false);
-      expect(fourMap.has(0)).to.equal(false);
-
-      fourMap.set(-0, fourMap);
-
-      expect(fourMap.has(0)).to.equal(true);
-      expect(fourMap.has(-0)).to.equal(true);
-    });
-
-    it('treats positive and negative zero the same', function () {
-      var value1 = {};
-      var value2 = {};
-      testMapping(map, +0, value1);
-      expect(map.has(-0)).to.equal(true);
-      expect(map.get(-0)).to.equal(value1);
-      expect(map.set(-0, value2)).to.equal(map);
-      expect(map.get(-0)).to.equal(value2);
-      expect(map.get(+0)).to.equal(value2);
-    });
-
-    it('should map values correctly', function () {
-      // Run this test twice, one with the "fast" implementation (which only
-      // allows string and numeric keys) and once with the "slow" impl.
-      [true, false].forEach(function (slowkeys) {
-        map = new Map();
-
-        range(1, 20).forEach(function (number) {
-          if (slowkeys) { testMapping(map, number, {}); }
-          testMapping(map, number / 100, {});
-          testMapping(map, 'key-' + number, {});
-          testMapping(map, String(number), {});
-          if (slowkeys) { testMapping(map, Object(String(number)), {}); }
-        });
-
-        var testkeys = [Infinity, -Infinity, NaN];
-        if (slowkeys) {
-          testkeys.push(true, false, null, undefined);
-        }
-        testkeys.forEach(function (key) {
-          testMapping(map, key, {});
-          testMapping(map, String(key), {});
-        });
-        testMapping(map, '', {});
-
-        // verify that properties of Object don't peek through.
-        [
-          'hasOwnProperty',
-          'constructor',
-          'toString',
-          'isPrototypeOf',
-         '__proto__',
-          '__parent__',
-          '__count__'
-         ].forEach(function (key) {
-           testMapping(map, key, {});
-         });
-      });
-    });
-
-    it('should map empty values correctly', function () {
-      testMapping(map, {}, true);
-      testMapping(map, null, true);
-      testMapping(map, undefined, true);
-      testMapping(map, '', true);
-      testMapping(map, NaN, true);
-      testMapping(map, 0, true);
-    });
-
-    it('should has correct querying behavior', function () {
-      var key = {};
-      testMapping(map, key, 'to-be-present');
-      expect(map.has(key)).to.equal(true);
-      expect(map.has({})).to.equal(false);
-      expect(map.set(key, void 0)).to.equal(map);
-      expect(map.get(key)).to.equal(undefined);
-      expect(map.has(key)).to.equal(true);
-      expect(map.has({})).to.equal(false);
-    });
-
-    it('should allow NaN values as keys', function () {
-      expect(map.has(NaN)).to.equal(false);
-      expect(map.has(NaN + 1)).to.equal(false);
-      expect(map.has(23)).to.equal(false);
-      expect(map.set(NaN, 'value')).to.equal(map);
-      expect(map.has(NaN)).to.equal(true);
-      expect(map.has(NaN + 1)).to.equal(true);
-      expect(map.has(23)).to.equal(false);
-    });
-
-    it('should not have [[Enumerable]] props', function () {
-      expectNotEnumerable(Map);
-      expectNotEnumerable(Map.prototype);
-      expectNotEnumerable(new Map());
-    });
-
-    it('should not have an own constructor', function () {
-      var m = new Map();
-      expect(m).not.to.haveOwnPropertyDescriptor('constructor');
-      expect(m.constructor).to.equal(Map);
-    });
-
-    it('should allow common ecmascript idioms', function () {
-      expect(map).to.be.an.instanceOf(Map);
-      expect(typeof Map.prototype.get).to.equal('function');
-      expect(typeof Map.prototype.set).to.equal('function');
-      expect(typeof Map.prototype.has).to.equal('function');
-      expect(typeof Map.prototype['delete']).to.equal('function');
-    });
-
-    it('should have a unique constructor', function () {
-      expect(Map.prototype).to.not.equal(Object.prototype);
-    });
-
-    describe('#clear()', function () {
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Map.prototype.clear).to.have.property('name', 'clear');
-      });
-
-      it('is not enumerable', function () {
-        expect(Map.prototype).ownPropertyDescriptor('clear').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Map.prototype.clear).to.have.property('length', 0);
-      });
-
-      it('should have #clear method', function () {
-        expect(map.set(1, 2)).to.equal(map);
-        expect(map.set(5, 2)).to.equal(map);
-        expect(map.size).to.equal(2);
-        expect(map.has(5)).to.equal(true);
-        map.clear();
-        expect(map.size).to.equal(0);
-        expect(map.has(5)).to.equal(false);
-      });
-    });
-
-    describe('#keys()', function () {
-      if (!Map.prototype.hasOwnProperty('keys')) {
-        return it('exists', function () {
-          expect(Map.prototype).to.have.property('keys');
-        });
-      }
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Map.prototype.keys).to.have.property('name', 'keys');
-      });
-
-      it('is not enumerable', function () {
-        expect(Map.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Map.prototype.keys).to.have.property('length', 0);
-      });
-    });
-
-    describe('#values()', function () {
-      if (!Map.prototype.hasOwnProperty('values')) {
-        return it('exists', function () {
-          expect(Map.prototype).to.have.property('values');
-        });
-      }
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Map.prototype.values).to.have.property('name', 'values');
-      });
-
-      it('is not enumerable', function () {
-        expect(Map.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Map.prototype.values).to.have.property('length', 0);
-      });
-    });
-
-    describe('#entries()', function () {
-      if (!Map.prototype.hasOwnProperty('entries')) {
-        return it('exists', function () {
-          expect(Map.prototype).to.have.property('entries');
-        });
-      }
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Map.prototype.entries).to.have.property('name', 'entries');
-      });
-
-      it('is not enumerable', function () {
-        expect(Map.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Map.prototype.entries).to.have.property('length', 0);
-      });
-
-      it('throws when called on a non-Map', function () {
-        var expectedMessage = /^(Method )?Map.prototype.entries called on incompatible receiver |^entries method called on incompatible |^Cannot create a Map entry iterator for a non-Map object.|^Map\.prototype\.entries: 'this' is not a Map object$/;
-        var nonMaps = [true, false, 'abc', NaN, new Set([1, 2]), { a: true }, [1], Object('abc'), Object(NaN)];
-        nonMaps.forEach(function (nonMap) {
-          expect(function () { return Map.prototype.entries.call(nonMap); }).to['throw'](TypeError, expectedMessage);
-        });
-      });
-    });
-
-    describe('#size', function () {
-      it('throws TypeError when accessed directly', function () {
-        // see https://github.com/paulmillr/es6-shim/issues/176
-        expect(function () { return Map.prototype.size; }).to['throw'](TypeError);
-        expect(function () { return Map.prototype.size; }).to['throw'](TypeError);
-      });
-
-      it('is an accessor function on the prototype', function () {
-        expect(Map.prototype).ownPropertyDescriptor('size').to.have.property('get');
-        expect(typeof Object.getOwnPropertyDescriptor(Map.prototype, 'size').get).to.equal('function');
-        expect(new Map()).not.to.haveOwnPropertyDescriptor('size');
-      });
-    });
-
-    it('should return false when deleting a nonexistent key', function () {
-      expect(map.has('a')).to.equal(false);
-      expect(map['delete']('a')).to.equal(false);
-    });
-
-    it('should have keys, values and size props', function () {
-      expect(map.set('a', 1)).to.equal(map);
-      expect(map.set('b', 2)).to.equal(map);
-      expect(map.set('c', 3)).to.equal(map);
-      expect(typeof map.keys).to.equal('function');
-      expect(typeof map.values).to.equal('function');
-      expect(map.size).to.equal(3);
-      expect(map['delete']('a')).to.equal(true);
-      expect(map.size).to.equal(2);
-    });
-
-    it('should have an iterator that works with Array.from', function () {
-      expect(Array).to.have.property('from');
-
-      expect(map.set('a', 1)).to.equal(map);
-      expect(map.set('b', NaN)).to.equal(map);
-      expect(map.set('c', false)).to.equal(map);
-      expect(Array.from(map)).to.eql([['a', 1], ['b', NaN], ['c', false]]);
-      expect(Array.from(map.keys())).to.eql(['a', 'b', 'c']);
-      expect(Array.from(map.values())).to.eql([1, NaN, false]);
-      expect(map).to.have.entries(Array.from(map.entries()));
-    });
-
-    ifSymbolIteratorIt('has the right default iteration function', function () {
-      // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
-      expect(Map.prototype).to.have.property(Sym.iterator, Map.prototype.entries);
-    });
-
-    describe('#forEach', function () {
-      var mapToIterate;
-
-      beforeEach(function () {
-        mapToIterate = new Map();
-        expect(mapToIterate.set('a', 1)).to.equal(mapToIterate);
-        expect(mapToIterate.set('b', 2)).to.equal(mapToIterate);
-        expect(mapToIterate.set('c', 3)).to.equal(mapToIterate);
-      });
-
-      afterEach(function () {
-        mapToIterate = null;
-      });
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Map.prototype.forEach).to.have.property('name', 'forEach');
-      });
-
-      it('is not enumerable', function () {
-        expect(Map.prototype).ownPropertyDescriptor('forEach').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Map.prototype.forEach).to.have.property('length', 1);
-      });
-
-      it('should be iterable via forEach', function () {
-        var expectedMap = {
-          a: 1,
-          b: 2,
-          c: 3
-        };
-        var foundMap = {};
-        mapToIterate.forEach(function (value, key, entireMap) {
-          expect(entireMap).to.equal(mapToIterate);
-          foundMap[key] = value;
-        });
-        expect(foundMap).to.eql(expectedMap);
-      });
-
-      it('should iterate over empty keys', function () {
-        var mapWithEmptyKeys = new Map();
-        var expectedKeys = [{}, null, undefined, '', NaN, 0];
-        expectedKeys.forEach(function (key) {
-          expect(mapWithEmptyKeys.set(key, true)).to.equal(mapWithEmptyKeys);
-        });
-        var foundKeys = [];
-        mapWithEmptyKeys.forEach(function (value, key, entireMap) {
-          expect(entireMap.get(key)).to.equal(value);
-          foundKeys.push(key);
-        });
-        expect(foundKeys).to.be.theSameSet(expectedKeys);
-      });
-
-      it('should support the thisArg', function () {
-        var context = function () {};
-        mapToIterate.forEach(function () {
-          expect(this).to.equal(context);
-        }, context);
-      });
-
-      it('should have a length of 1', function () {
-        expect(Map.prototype.forEach.length).to.equal(1);
-      });
-
-      it('should not revisit modified keys', function () {
-        var hasModifiedA = false;
-        mapToIterate.forEach(function (value, key) {
-          if (!hasModifiedA && key === 'a') {
-            expect(mapToIterate.set('a', 4)).to.equal(mapToIterate);
-            hasModifiedA = true;
-          } else {
-            expect(key).not.to.equal('a');
-          }
-        });
-      });
-
-      it('returns the map from #set() for chaining', function () {
-        expect(mapToIterate.set({}, {})).to.equal(mapToIterate);
-        expect(mapToIterate.set(42, {})).to.equal(mapToIterate);
-        expect(mapToIterate.set(0, {})).to.equal(mapToIterate);
-        expect(mapToIterate.set(NaN, {})).to.equal(mapToIterate);
-        expect(mapToIterate.set(-0, {})).to.equal(mapToIterate);
-      });
-
-      it('visits keys added in the iterator', function () {
-        var hasAdded = false;
-        var hasFoundD = false;
-        mapToIterate.forEach(function (value, key) {
-          if (!hasAdded) {
-            mapToIterate.set('d', 5);
-            hasAdded = true;
-          } else if (key === 'd') {
-            hasFoundD = true;
-          }
-        });
-        expect(hasFoundD).to.equal(true);
-      });
-
-      it('visits keys added in the iterator when there is a deletion', function () {
-        var hasSeenFour = false;
-        var mapToMutate = new Map();
-        mapToMutate.set('0', 42);
-        mapToMutate.forEach(function (value, key) {
-          if (key === '0') {
-            expect(mapToMutate['delete']('0')).to.equal(true);
-            mapToMutate.set('4', 'a value');
-          } else if (key === '4') {
-            hasSeenFour = true;
-          }
-        });
-        expect(hasSeenFour).to.equal(true);
-      });
-
-      it('does not visit keys deleted before a visit', function () {
-        var hasVisitedC = false;
-        var hasDeletedC = false;
-        mapToIterate.forEach(function (value, key) {
-          if (key === 'c') {
-            hasVisitedC = true;
-          }
-          if (!hasVisitedC && !hasDeletedC) {
-            hasDeletedC = mapToIterate['delete']('c');
-            expect(hasDeletedC).to.equal(true);
-          }
-        });
-        expect(hasVisitedC).to.equal(false);
-      });
-
-      it('should work after deletion of the current key', function () {
-        var expectedMap = {
-          a: 1,
-          b: 2,
-          c: 3
-        };
-        var foundMap = {};
-        mapToIterate.forEach(function (value, key) {
-          foundMap[key] = value;
-          expect(mapToIterate['delete'](key)).to.equal(true);
-        });
-        expect(foundMap).to.eql(expectedMap);
-      });
-
-      it('should convert key -0 to +0', function () {
-        var zeroMap = new Map();
-        var result = [];
-        zeroMap.set(-0, 'a');
-        zeroMap.forEach(function (value, key) {
-          result.push(String(1 / key) + ' ' + value);
-        });
-        zeroMap.set(1, 'b');
-        zeroMap.set(0, 'c'); // shouldn't cause reordering
-        zeroMap.forEach(function (value, key) {
-          result.push(String(1 / key) + ' ' + value);
-        });
-        expect(result.join(', ')).to.equal(
-          'Infinity a, Infinity c, 1 b'
-        );
-      });
-    });
-
-    it('should preserve insertion order', function () {
-      var convertToPairs = function (item) { return [item, true]; };
-      var arr1 = ['d', 'a', 'b'];
-      var arr2 = [3, 2, 'z', 'a', 1];
-      var arr3 = [3, 2, 'z', {}, 'a', 1];
-
-      [arr1, arr2, arr3].forEach(function (array) {
-        var entries = array.map(convertToPairs);
-        expect(new Map(entries)).to.have.entries(entries);
-      });
-    });
-  });
-
-  it('map iteration', function () {
-    var map = new Map();
-    map.set('a', 1);
-    map.set('b', 2);
-    map.set('c', 3);
-    map.set('d', 4);
-
-    var keys = [];
-    var iterator = map.keys();
-    keys.push(iterator.next().value);
-    expect(map['delete']('a')).to.equal(true);
-    expect(map['delete']('b')).to.equal(true);
-    expect(map['delete']('c')).to.equal(true);
-    map.set('e');
-    keys.push(iterator.next().value);
-    keys.push(iterator.next().value);
-
-    expect(iterator.next().done).to.equal(true);
-    map.set('f');
-    expect(iterator.next().done).to.equal(true);
-    expect(keys).to.eql(['a', 'd', 'e']);
-  });
-
-  it('set iteration', function () {
-    var set = new Set();
-    expect(set.add('a')).to.equal(set);
-    expect(set.add('b')).to.equal(set);
-    expect(set.add('c')).to.equal(set);
-    expect(set.add('d')).to.equal(set);
-
-    var keys = [];
-    var iterator = set.keys();
-    keys.push(iterator.next().value);
-    expect(set['delete']('a')).to.equal(true);
-    expect(set['delete']('b')).to.equal(true);
-    expect(set['delete']('c')).to.equal(true);
-    expect(set.add('e')).to.equal(set);
-    keys.push(iterator.next().value);
-    keys.push(iterator.next().value);
-
-    expect(iterator.next().done).to.equal(true);
-    expect(set.add('f')).to.equal(set);
-    expect(iterator.next().done).to.equal(true);
-    expect(keys).to.eql(['a', 'd', 'e']);
-  });
-
-  var testSet = function (set, key) {
-    expect(set.has(key)).to.equal(false);
-    expect(set['delete'](key)).to.equal(false);
-    expect(set.add(key)).to.equal(set);
-    expect(set.has(key)).to.equal(true);
-    expect(set['delete'](key)).to.equal(true);
-    expect(set.has(key)).to.equal(false);
-    expect(set.add(key)).to.equal(set); // add it back
-  };
-
-  describe('Set', function () {
-    if (typeof Set === 'undefined') {
-      return it('exists', function () {
-        expect(typeof Set).to.equal('function');
-      });
-    }
-
-    var set;
-    beforeEach(function () {
-      set = new Set();
-    });
-
-    afterEach(function () {
-      set = null;
-    });
-
-    ifShimIt('is on the exported object', function () {
-      var exported = require('../');
-      expect(exported.Set).to.equal(Set);
-    });
-
-    it('should exist in global namespace', function () {
-      expect(typeof Set).to.equal('function');
-    });
-
-    it('has the right arity', function () {
-      expect(Set).to.have.property('length', 0);
-    });
-
-    it('returns the set from #add() for chaining', function () {
-      expect(set.add({})).to.equal(set);
-    });
-
-    it('should return false when deleting an item not in the set', function () {
-      expect(set.has('a')).to.equal(false);
-      expect(set['delete']('a')).to.equal(false);
-    });
-
-    it('should accept an iterable as argument', function () {
-      testSet(set, 'a');
-      testSet(set, 'b');
-      var set2 = new Set(set);
-      expect(set2.has('a')).to.equal(true);
-      expect(set2.has('b')).to.equal(true);
-      expect(set2).to.have.entries([['a', 'a'], ['b', 'b']]);
-    });
-
-    it('accepts an array as an argument', function () {
-      var arr = ['a', 'b', 'c'];
-      var setFromArray = new Set(arr);
-      expect(setFromArray).to.have.entries([['a', 'a'], ['b', 'b'], ['c', 'c']]);
-    });
-
-    it('should not be callable without "new"', function () {
-      expect(Set).to['throw'](TypeError);
-    });
-
-    it('should be subclassable', function () {
-      if (!Object.setPrototypeOf) { return; } // skip test if on IE < 11
-      var MySet = function MySet() {
-        var actualSet = new Set(['a', 'b']);
-        Object.setPrototypeOf(actualSet, MySet.prototype);
-        return actualSet;
-      };
-      Object.setPrototypeOf(MySet, Set);
-      MySet.prototype = Object.create(Set.prototype, {
-        constructor: { value: MySet }
-      });
-
-      var mySet = new MySet();
-      testSet(mySet, 'c');
-      testSet(mySet, 'd');
-      expect(mySet).to.have.entries([['a', 'a'], ['b', 'b'], ['c', 'c'], ['d', 'd']]);
-    });
-
-    it('should has valid getter and setter calls', function () {
-      ['add', 'has', 'delete'].forEach(function (method) {
-        expect(function () {
-          set[method]({});
-        }).to.not['throw']();
-      });
-    });
-
-    it('uses SameValueZero even on a Set of size > 4', function () {
-      var firstFour = [1, 2, 3, 4];
-      var fourSet = new Set(firstFour);
-      expect(fourSet.size).to.equal(4);
-      expect(fourSet.has(-0)).to.equal(false);
-      expect(fourSet.has(0)).to.equal(false);
-
-      fourSet.add(-0);
-
-      expect(fourSet.size).to.equal(5);
-      expect(fourSet.has(0)).to.equal(true);
-      expect(fourSet.has(-0)).to.equal(true);
-    });
-
-    it('should work as expected', function () {
-      // Run this test twice, one with the "fast" implementation (which only
-      // allows string and numeric keys) and once with the "slow" impl.
-      [true, false].forEach(function (slowkeys) {
-        set = new Set();
-
-        range(1, 20).forEach(function (number) {
-          if (slowkeys) { testSet(set, {}); }
-          testSet(set, number);
-          testSet(set, number / 100);
-          testSet(set, 'key-' + number);
-          testSet(set, String(number));
-          if (slowkeys) { testSet(set, Object(String(number))); }
-        });
-
-        var testkeys = [+0, Infinity, -Infinity, NaN];
-        if (slowkeys) {
-          testkeys.push(true, false, null, undefined);
-        }
-        testkeys.forEach(function (number) {
-          testSet(set, number);
-          testSet(set, String(number));
-        });
-        testSet(set, '');
-
-        // -0 and +0 should be the same key (Set uses SameValueZero)
-        expect(set.has(-0)).to.equal(true);
-        expect(set['delete'](+0)).to.equal(true);
-        testSet(set, -0);
-        expect(set.has(+0)).to.equal(true);
-
-        // verify that properties of Object don't peek through.
-        [
-          'hasOwnProperty',
-          'constructor',
-          'toString',
-          'isPrototypeOf',
-          '__proto__',
-          '__parent__',
-          '__count__'
-        ].forEach(function (prop) { testSet(set, prop); });
-      });
-    });
-
-    describe('#size', function () {
-      it('returns the expected size', function () {
-        expect(set.add(1)).to.equal(set);
-        expect(set.add(5)).to.equal(set);
-        expect(set.size).to.equal(2);
-      });
-    });
-
-    describe('#clear()', function () {
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Set.prototype.clear).to.have.property('name', 'clear');
-      });
-
-      it('is not enumerable', function () {
-        expect(Set.prototype).ownPropertyDescriptor('clear').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Set.prototype.clear).to.have.property('length', 0);
-      });
-
-      it('should have #clear method', function () {
-        expect(set.add(1)).to.equal(set);
-        expect(set.add(5)).to.equal(set);
-        expect(set.size).to.equal(2);
-        expect(set.has(5)).to.equal(true);
-        set.clear();
-        expect(set.size).to.equal(0);
-        expect(set.has(5)).to.equal(false);
-      });
-    });
-
-    describe('#keys()', function () {
-      if (!Set.prototype.hasOwnProperty('keys')) {
-        return it('exists', function () {
-          expect(Set.prototype).to.have.property('keys');
-        });
-      }
-
-      it('is the same object as #values()', function () {
-        expect(Set.prototype.keys).to.equal(Set.prototype.values);
-      });
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Set.prototype.keys).to.have.property('name', 'values');
-      });
-
-      it('is not enumerable', function () {
-        expect(Set.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Set.prototype.keys).to.have.property('length', 0);
-      });
-    });
-
-    describe('#values()', function () {
-      if (!Set.prototype.hasOwnProperty('values')) {
-        return it('exists', function () {
-          expect(Set.prototype).to.have.property('values');
-        });
-      }
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Set.prototype.values).to.have.property('name', 'values');
-      });
-
-      it('is not enumerable', function () {
-        expect(Set.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Set.prototype.values).to.have.property('length', 0);
-      });
-
-      it('throws when called on a non-Set', function () {
-        var expectedMessage = /^(Method )?Set.prototype.values called on incompatible receiver |^values method called on incompatible |^Cannot create a Set value iterator for a non-Set object.$|^Set.prototype.values: 'this' is not a Set object$/;
-        var nonSets = [true, false, 'abc', NaN, new Map([[1, 2]]), { a: true }, [1], Object('abc'), Object(NaN)];
-        nonSets.forEach(function (nonSet) {
-          expect(function () { return Set.prototype.values.call(nonSet); }).to['throw'](TypeError, expectedMessage);
-        });
-      });
-    });
-
-    describe('#entries()', function () {
-      if (!Set.prototype.hasOwnProperty('entries')) {
-        return it('exists', function () {
-          expect(Set.prototype).to.have.property('entries');
-        });
-      }
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Set.prototype.entries).to.have.property('name', 'entries');
-      });
-
-      it('is not enumerable', function () {
-        expect(Set.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Set.prototype.entries).to.have.property('length', 0);
-      });
-    });
-
-    describe('#has()', function () {
-      if (!Set.prototype.hasOwnProperty('has')) {
-        return it('exists', function () {
-          expect(Set.prototype).to.have.property('has');
-        });
-      }
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Set.prototype.has).to.have.property('name', 'has');
-      });
-
-      it('is not enumerable', function () {
-        expect(Set.prototype).ownPropertyDescriptor('has').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Set.prototype.has).to.have.property('length', 1);
-      });
-    });
-
-    it('should allow NaN values as keys', function () {
-      expect(set.has(NaN)).to.equal(false);
-      expect(set.has(NaN + 1)).to.equal(false);
-      expect(set.has(23)).to.equal(false);
-      expect(set.add(NaN)).to.equal(set);
-      expect(set.has(NaN)).to.equal(true);
-      expect(set.has(NaN + 1)).to.equal(true);
-      expect(set.has(23)).to.equal(false);
-    });
-
-    it('should not have [[Enumerable]] props', function () {
-      expectNotEnumerable(Set);
-      expectNotEnumerable(Set.prototype);
-      expectNotEnumerable(new Set());
-    });
-
-    it('should not have an own constructor', function () {
-      var s = new Set();
-      expect(s).not.to.haveOwnPropertyDescriptor('constructor');
-      expect(s.constructor).to.equal(Set);
-    });
-
-    it('should allow common ecmascript idioms', function () {
-      expect(set instanceof Set).to.equal(true);
-      expect(typeof Set.prototype.add).to.equal('function');
-      expect(typeof Set.prototype.has).to.equal('function');
-      expect(typeof Set.prototype['delete']).to.equal('function');
-    });
-
-    it('should have a unique constructor', function () {
-      expect(Set.prototype).to.not.equal(Object.prototype);
-    });
-
-    describe('has an iterator that works with Array.from', function () {
-      if (!Array.hasOwnProperty('from')) {
-        return it('requires Array.from to exist', function () {
-          expect(Array).to.have.property('from');
-        });
-      }
-
-      var setToIterate;
-      beforeEach(function () {
-        setToIterate = new Set([1, NaN, false, true, null, undefined, 'a']);
-      });
-
-      afterEach(function () {
-        setToIterate = null;
-      });
-
-      it('works with the full set', function () {
-        expect(Array.from(setToIterate)).to.eql([1, NaN, false, true, null, undefined, 'a']);
-      });
-
-      it('works with Set#keys()', function () {
-        expect(Array.from(setToIterate.keys())).to.eql(Array.from(setToIterate));
-      });
-
-      it('works with Set#values()', function () {
-        expect(Array.from(setToIterate.values())).to.eql(Array.from(setToIterate));
-      });
-
-      it('works with Set#entries()', function () {
-        expect(Array.from(setToIterate.entries())).to.eql([
-          [1, 1],
-          [NaN, NaN],
-          [false, false],
-          [true, true],
-          [null, null],
-          [undefined, undefined],
-          ['a', 'a']
-        ]);
-      });
-    });
-
-    ifSymbolIteratorIt('has the right default iteration function', function () {
-      // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
-      expect(Set.prototype).to.have.property(Sym.iterator, Set.prototype.values);
-    });
-
-    it('should preserve insertion order', function () {
-      var arr1 = ['d', 'a', 'b'];
-      var arr2 = [3, 2, 'z', 'a', 1];
-      var arr3 = [3, 2, 'z', {}, 'a', 1];
-
-      var makeEntries = function (n) { return [n, n]; };
-      [arr1, arr2, arr3].forEach(function (array) {
-        var entries = array.map(makeEntries);
-        expect(new Set(array)).to.have.entries(entries);
-      });
-    });
-
-    describe('#forEach', function () {
-      var setToIterate;
-      beforeEach(function () {
-        setToIterate = new Set();
-        expect(setToIterate.add('a')).to.equal(setToIterate);
-        expect(setToIterate.add('b')).to.equal(setToIterate);
-        expect(setToIterate.add('c')).to.equal(setToIterate);
-      });
-
-      afterEach(function () {
-        setToIterate = null;
-      });
-
-      ifFunctionsHaveNamesIt('has the right name', function () {
-        expect(Set.prototype.forEach).to.have.property('name', 'forEach');
-      });
-
-      it('is not enumerable', function () {
-        expect(Set.prototype).ownPropertyDescriptor('forEach').to.have.property('enumerable', false);
-      });
-
-      it('has the right arity', function () {
-        expect(Set.prototype.forEach).to.have.property('length', 1);
-      });
-
-      it('should be iterable via forEach', function () {
-        var expectedSet = ['a', 'b', 'c'];
-        var foundSet = [];
-        setToIterate.forEach(function (value, alsoValue, entireSet) {
-          expect(entireSet).to.equal(setToIterate);
-          expect(value).to.equal(alsoValue);
-          foundSet.push(value);
-        });
-        expect(foundSet).to.eql(expectedSet);
-      });
-
-      it('should iterate over empty keys', function () {
-        var setWithEmptyKeys = new Set();
-        var expectedKeys = [{}, null, undefined, '', NaN, 0];
-        expectedKeys.forEach(function (key) {
-          expect(setWithEmptyKeys.add(key)).to.equal(setWithEmptyKeys);
-        });
-        var foundKeys = [];
-        setWithEmptyKeys.forEach(function (value, key, entireSet) {
-          expect([key]).to.be.theSameSet([value]); // handles NaN correctly
-          expect(entireSet.has(key)).to.equal(true);
-          foundKeys.push(key);
-        });
-        expect(foundKeys).to.be.theSameSet(expectedKeys);
-      });
-
-      it('should support the thisArg', function () {
-        var context = function () {};
-        setToIterate.forEach(function () {
-          expect(this).to.equal(context);
-        }, context);
-      });
-
-      it('should have a length of 1', function () {
-        expect(Set.prototype.forEach.length).to.equal(1);
-      });
-
-      it('should not revisit modified keys', function () {
-        var hasModifiedA = false;
-        setToIterate.forEach(function (value, key) {
-          if (!hasModifiedA && key === 'a') {
-            expect(setToIterate.add('a')).to.equal(setToIterate);
-            hasModifiedA = true;
-          } else {
-            expect(key).not.to.equal('a');
-          }
-        });
-      });
-
-      it('visits keys added in the iterator', function () {
-        var hasAdded = false;
-        var hasFoundD = false;
-        setToIterate.forEach(function (value, key) {
-          if (!hasAdded) {
-            expect(setToIterate.add('d')).to.equal(setToIterate);
-            hasAdded = true;
-          } else if (key === 'd') {
-            hasFoundD = true;
-          }
-        });
-        expect(hasFoundD).to.equal(true);
-      });
-
-      it('visits keys added in the iterator when there is a deletion (slow path)', function () {
-        var hasSeenFour = false;
-        var setToMutate = new Set();
-        expect(setToMutate.add({})).to.equal(setToMutate); // force use of the slow O(N) implementation
-        expect(setToMutate.add('0')).to.equal(setToMutate);
-        setToMutate.forEach(function (value, key) {
-          if (key === '0') {
-            expect(setToMutate['delete']('0')).to.equal(true);
-            expect(setToMutate.add('4')).to.equal(setToMutate);
-          } else if (key === '4') {
-            hasSeenFour = true;
-          }
-        });
-        expect(hasSeenFour).to.equal(true);
-      });
-
-      it('visits keys added in the iterator when there is a deletion (fast path)', function () {
-        var hasSeenFour = false;
-        var setToMutate = new Set();
-        expect(setToMutate.add('0')).to.equal(setToMutate);
-        setToMutate.forEach(function (value, key) {
-          if (key === '0') {
-            expect(setToMutate['delete']('0')).to.equal(true);
-            expect(setToMutate.add('4')).to.equal(setToMutate);
-          } else if (key === '4') {
-            hasSeenFour = true;
-          }
-        });
-        expect(hasSeenFour).to.equal(true);
-      });
-
-      it('does not visit keys deleted before a visit', function () {
-        var hasVisitedC = false;
-        var hasDeletedC = false;
-        setToIterate.forEach(function (value, key) {
-          if (key === 'c') {
-            hasVisitedC = true;
-          }
-          if (!hasVisitedC && !hasDeletedC) {
-            hasDeletedC = setToIterate['delete']('c');
-            expect(hasDeletedC).to.equal(true);
-          }
-        });
-        expect(hasVisitedC).to.equal(false);
-      });
-
-      it('should work after deletion of the current key', function () {
-        var expectedSet = {
-          a: 'a',
-          b: 'b',
-          c: 'c'
-        };
-        var foundSet = {};
-        setToIterate.forEach(function (value, key) {
-          foundSet[key] = value;
-          expect(setToIterate['delete'](key)).to.equal(true);
-        });
-        expect(foundSet).to.eql(expectedSet);
-      });
-
-      it('should convert key -0 to +0', function () {
-        var zeroSet = new Set();
-        var result = [];
-        expect(zeroSet.add(-0)).to.equal(zeroSet);
-        zeroSet.forEach(function (key) {
-          result.push(String(1 / key));
-        });
-        expect(zeroSet.add(1)).to.equal(zeroSet);
-        expect(zeroSet.add(0)).to.equal(zeroSet); // shouldn't cause reordering
-        zeroSet.forEach(function (key) {
-          result.push(String(1 / key));
-        });
-        expect(result.join(', ')).to.equal(
-          'Infinity, Infinity, 1'
-        );
-      });
-    });
-
-    it('Set.prototype.size should throw TypeError', function () {
-      // see https://github.com/paulmillr/es6-shim/issues/176
-      expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
-      expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
-    });
-
-    it.skip('should throw proper errors when user invokes methods with wrong types of receiver', function () {
-
-    });
-  });
-});
diff --git a/test/index.html b/test/index.html
index 64e09b7..f2e14a7 100644
--- a/test/index.html
+++ b/test/index.html
@@ -11,7 +11,8 @@
   <script src="../es6-shim.js"></script>
   <script src="browser-setup.js"></script>
   <script src="array.js"></script>
-  <script src="collections.js"></script>
+  <script src="map.js"></script>
+  <script src="set.js"></script>
   <script src="json.js"></script>
   <script src="math.js"></script>
   <script src="number.js"></script>
diff --git a/test/map.js b/test/map.js
new file mode 100644
index 0000000..a16b086
--- /dev/null
+++ b/test/map.js
@@ -0,0 +1,605 @@
+/* global describe, it, xit, expect, require, beforeEach, afterEach */
+
+// Big thanks to V8 folks for test ideas.
+// v8/test/mjsunit/harmony/collections.js
+
+var Assertion = expect().constructor;
+Assertion.addMethod('theSameSet', function (otherArray) {
+  var array = this._obj;
+
+  expect(Array.isArray(array)).to.equal(true);
+  expect(Array.isArray(otherArray)).to.equal(true);
+
+  var diff = array.filter(function (value) {
+    return otherArray.every(function (otherValue) {
+      var areBothNaN = typeof value === 'number' && typeof otherValue === 'number' && value !== value && otherValue !== otherValue;
+      return !areBothNaN && value !== otherValue;
+    });
+  });
+
+  this.assert(
+    diff.length === 0,
+    'expected #{this} to be equal to #{exp} (as sets, i.e. no order)',
+    array,
+    otherArray
+  );
+});
+
+Assertion.addMethod('entries', function (expected) {
+  var collection = this._obj;
+
+  expect(Array.isArray(expected)).to.equal(true);
+  var expectedEntries = expected.slice();
+
+  var iterator = collection.entries();
+  var result;
+  do {
+    result = iterator.next();
+    expect(result.value).to.be.eql(expectedEntries.shift());
+  } while (!result.done);
+});
+
+describe('Map', function () {
+  var functionsHaveNames = (function foo() {}).name === 'foo';
+  var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
+  var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
+
+  var range = function range(from, to) {
+    var result = [];
+    for (var value = from; value < to; value++) {
+      result.push(value);
+    }
+    return result;
+  };
+
+  var prototypePropIsEnumerable = (function () {}).propertyIsEnumerable('prototype');
+  var expectNotEnumerable = function (object) {
+    if (prototypePropIsEnumerable && typeof object === 'function') {
+      expect(Object.keys(object)).to.eql(['prototype']);
+    } else {
+      expect(Object.keys(object)).to.eql([]);
+    }
+  };
+
+  var Sym = typeof Symbol === 'undefined' ? {} : Symbol;
+  var isSymbol = function (sym) {
+    return typeof Sym === 'function' && typeof sym === 'symbol';
+  };
+  var ifSymbolIteratorIt = isSymbol(Sym.iterator) ? it : xit;
+
+  var testMapping = function (map, key, value) {
+    expect(map.has(key)).to.equal(false);
+    expect(map.get(key)).to.equal(undefined);
+    expect(map.set(key, value)).to.equal(map);
+    expect(map.get(key)).to.equal(value);
+    expect(map.has(key)).to.equal(true);
+  };
+
+  if (typeof Map === 'undefined') {
+    return it('exists', function () {
+      expect(typeof Map).to.equal('function');
+    });
+  }
+
+  var map;
+  beforeEach(function () {
+    map = new Map();
+  });
+
+  afterEach(function () {
+    map = null;
+  });
+
+  ifShimIt('is on the exported object', function () {
+    var exported = require('../');
+    expect(exported.Map).to.equal(Map);
+  });
+
+  it('should exist in global namespace', function () {
+    expect(typeof Map).to.equal('function');
+  });
+
+  it('should have the right arity', function () {
+    expect(Map).to.have.property('length', 0);
+  });
+
+  it('should has valid getter and setter calls', function () {
+    ['get', 'set', 'has', 'delete'].forEach(function (method) {
+      expect(function () {
+        map[method]({});
+      }).to.not['throw']();
+    });
+  });
+
+  it('should accept an iterable as argument', function () {
+    testMapping(map, 'a', 'b');
+    testMapping(map, 'c', 'd');
+    var map2;
+    expect(function () { map2 = new Map(map); }).not.to['throw'](Error);
+    expect(map2).to.be.an.instanceOf(Map);
+    expect(map2.has('a')).to.equal(true);
+    expect(map2.has('c')).to.equal(true);
+    expect(map2).to.have.entries([['a', 'b'], ['c', 'd']]);
+  });
+
+  it('should throw with iterables that return primitives', function () {
+    expect(function () { return new Map('123'); }).to['throw'](TypeError);
+    expect(function () { return new Map([1, 2, 3]); }).to['throw'](TypeError);
+    expect(function () { return new Map(['1', '2', '3']); }).to['throw'](TypeError);
+    expect(function () { return new Map([true]); }).to['throw'](TypeError);
+  });
+
+  it('should not be callable without "new"', function () {
+    expect(Map).to['throw'](TypeError);
+  });
+
+  it('should be subclassable', function () {
+    if (!Object.setPrototypeOf) { return; } // skip test if on IE < 11
+    var MyMap = function MyMap() {
+      var testMap = new Map([['a', 'b']]);
+      Object.setPrototypeOf(testMap, MyMap.prototype);
+      return testMap;
+    };
+    Object.setPrototypeOf(MyMap, Map);
+    MyMap.prototype = Object.create(Map.prototype, {
+      constructor: { value: MyMap }
+    });
+
+    var myMap = new MyMap();
+    testMapping(myMap, 'c', 'd');
+    expect(myMap).to.have.entries([['a', 'b'], ['c', 'd']]);
+  });
+
+  it('uses SameValueZero even on a Map of size > 4', function () {
+    // Chrome 38-42, node 0.11/0.12, iojs 1/2 have a bug when the Map has a size > 4
+    var firstFour = [[1, 0], [2, 0], [3, 0], [4, 0]];
+    var fourMap = new Map(firstFour);
+    expect(fourMap.size).to.equal(4);
+    expect(fourMap.has(-0)).to.equal(false);
+    expect(fourMap.has(0)).to.equal(false);
+
+    fourMap.set(-0, fourMap);
+
+    expect(fourMap.has(0)).to.equal(true);
+    expect(fourMap.has(-0)).to.equal(true);
+  });
+
+  it('treats positive and negative zero the same', function () {
+    var value1 = {};
+    var value2 = {};
+    testMapping(map, +0, value1);
+    expect(map.has(-0)).to.equal(true);
+    expect(map.get(-0)).to.equal(value1);
+    expect(map.set(-0, value2)).to.equal(map);
+    expect(map.get(-0)).to.equal(value2);
+    expect(map.get(+0)).to.equal(value2);
+  });
+
+  it('should map values correctly', function () {
+    // Run this test twice, one with the "fast" implementation (which only
+    // allows string and numeric keys) and once with the "slow" impl.
+    [true, false].forEach(function (slowkeys) {
+      map = new Map();
+
+      range(1, 20).forEach(function (number) {
+        if (slowkeys) { testMapping(map, number, {}); }
+        testMapping(map, number / 100, {});
+        testMapping(map, 'key-' + number, {});
+        testMapping(map, String(number), {});
+        if (slowkeys) { testMapping(map, Object(String(number)), {}); }
+      });
+
+      var testkeys = [Infinity, -Infinity, NaN];
+      if (slowkeys) {
+        testkeys.push(true, false, null, undefined);
+      }
+      testkeys.forEach(function (key) {
+        testMapping(map, key, {});
+        testMapping(map, String(key), {});
+      });
+      testMapping(map, '', {});
+
+      // verify that properties of Object don't peek through.
+      [
+        'hasOwnProperty',
+        'constructor',
+        'toString',
+        'isPrototypeOf',
+       '__proto__',
+        '__parent__',
+        '__count__'
+       ].forEach(function (key) {
+         testMapping(map, key, {});
+       });
+    });
+  });
+
+  it('should map empty values correctly', function () {
+    testMapping(map, {}, true);
+    testMapping(map, null, true);
+    testMapping(map, undefined, true);
+    testMapping(map, '', true);
+    testMapping(map, NaN, true);
+    testMapping(map, 0, true);
+  });
+
+  it('should has correct querying behavior', function () {
+    var key = {};
+    testMapping(map, key, 'to-be-present');
+    expect(map.has(key)).to.equal(true);
+    expect(map.has({})).to.equal(false);
+    expect(map.set(key, void 0)).to.equal(map);
+    expect(map.get(key)).to.equal(undefined);
+    expect(map.has(key)).to.equal(true);
+    expect(map.has({})).to.equal(false);
+  });
+
+  it('should allow NaN values as keys', function () {
+    expect(map.has(NaN)).to.equal(false);
+    expect(map.has(NaN + 1)).to.equal(false);
+    expect(map.has(23)).to.equal(false);
+    expect(map.set(NaN, 'value')).to.equal(map);
+    expect(map.has(NaN)).to.equal(true);
+    expect(map.has(NaN + 1)).to.equal(true);
+    expect(map.has(23)).to.equal(false);
+  });
+
+  it('should not have [[Enumerable]] props', function () {
+    expectNotEnumerable(Map);
+    expectNotEnumerable(Map.prototype);
+    expectNotEnumerable(new Map());
+  });
+
+  it('should not have an own constructor', function () {
+    var m = new Map();
+    expect(m).not.to.haveOwnPropertyDescriptor('constructor');
+    expect(m.constructor).to.equal(Map);
+  });
+
+  it('should allow common ecmascript idioms', function () {
+    expect(map).to.be.an.instanceOf(Map);
+    expect(typeof Map.prototype.get).to.equal('function');
+    expect(typeof Map.prototype.set).to.equal('function');
+    expect(typeof Map.prototype.has).to.equal('function');
+    expect(typeof Map.prototype['delete']).to.equal('function');
+  });
+
+  it('should have a unique constructor', function () {
+    expect(Map.prototype).to.not.equal(Object.prototype);
+  });
+
+  describe('#clear()', function () {
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Map.prototype.clear).to.have.property('name', 'clear');
+    });
+
+    it('is not enumerable', function () {
+      expect(Map.prototype).ownPropertyDescriptor('clear').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Map.prototype.clear).to.have.property('length', 0);
+    });
+
+    it('should have #clear method', function () {
+      expect(map.set(1, 2)).to.equal(map);
+      expect(map.set(5, 2)).to.equal(map);
+      expect(map.size).to.equal(2);
+      expect(map.has(5)).to.equal(true);
+      map.clear();
+      expect(map.size).to.equal(0);
+      expect(map.has(5)).to.equal(false);
+    });
+  });
+
+  describe('#keys()', function () {
+    if (!Map.prototype.hasOwnProperty('keys')) {
+      return it('exists', function () {
+        expect(Map.prototype).to.have.property('keys');
+      });
+    }
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Map.prototype.keys).to.have.property('name', 'keys');
+    });
+
+    it('is not enumerable', function () {
+      expect(Map.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Map.prototype.keys).to.have.property('length', 0);
+    });
+  });
+
+  describe('#values()', function () {
+    if (!Map.prototype.hasOwnProperty('values')) {
+      return it('exists', function () {
+        expect(Map.prototype).to.have.property('values');
+      });
+    }
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Map.prototype.values).to.have.property('name', 'values');
+    });
+
+    it('is not enumerable', function () {
+      expect(Map.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Map.prototype.values).to.have.property('length', 0);
+    });
+  });
+
+  describe('#entries()', function () {
+    if (!Map.prototype.hasOwnProperty('entries')) {
+      return it('exists', function () {
+        expect(Map.prototype).to.have.property('entries');
+      });
+    }
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Map.prototype.entries).to.have.property('name', 'entries');
+    });
+
+    it('is not enumerable', function () {
+      expect(Map.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Map.prototype.entries).to.have.property('length', 0);
+    });
+
+    it('throws when called on a non-Map', function () {
+      var expectedMessage = /^(Method )?Map.prototype.entries called on incompatible receiver |^entries method called on incompatible |^Cannot create a Map entry iterator for a non-Map object.|^Map\.prototype\.entries: 'this' is not a Map object$/;
+      var nonMaps = [true, false, 'abc', NaN, new Set([1, 2]), { a: true }, [1], Object('abc'), Object(NaN)];
+      nonMaps.forEach(function (nonMap) {
+        expect(function () { return Map.prototype.entries.call(nonMap); }).to['throw'](TypeError, expectedMessage);
+      });
+    });
+  });
+
+  describe('#size', function () {
+    it('throws TypeError when accessed directly', function () {
+      // see https://github.com/paulmillr/es6-shim/issues/176
+      expect(function () { return Map.prototype.size; }).to['throw'](TypeError);
+      expect(function () { return Map.prototype.size; }).to['throw'](TypeError);
+    });
+
+    it('is an accessor function on the prototype', function () {
+      expect(Map.prototype).ownPropertyDescriptor('size').to.have.property('get');
+      expect(typeof Object.getOwnPropertyDescriptor(Map.prototype, 'size').get).to.equal('function');
+      expect(new Map()).not.to.haveOwnPropertyDescriptor('size');
+    });
+  });
+
+  it('should return false when deleting a nonexistent key', function () {
+    expect(map.has('a')).to.equal(false);
+    expect(map['delete']('a')).to.equal(false);
+  });
+
+  it('should have keys, values and size props', function () {
+    expect(map.set('a', 1)).to.equal(map);
+    expect(map.set('b', 2)).to.equal(map);
+    expect(map.set('c', 3)).to.equal(map);
+    expect(typeof map.keys).to.equal('function');
+    expect(typeof map.values).to.equal('function');
+    expect(map.size).to.equal(3);
+    expect(map['delete']('a')).to.equal(true);
+    expect(map.size).to.equal(2);
+  });
+
+  it('should have an iterator that works with Array.from', function () {
+    expect(Array).to.have.property('from');
+
+    expect(map.set('a', 1)).to.equal(map);
+    expect(map.set('b', NaN)).to.equal(map);
+    expect(map.set('c', false)).to.equal(map);
+    expect(Array.from(map)).to.eql([['a', 1], ['b', NaN], ['c', false]]);
+    expect(Array.from(map.keys())).to.eql(['a', 'b', 'c']);
+    expect(Array.from(map.values())).to.eql([1, NaN, false]);
+    expect(map).to.have.entries(Array.from(map.entries()));
+  });
+
+  ifSymbolIteratorIt('has the right default iteration function', function () {
+    // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
+    expect(Map.prototype).to.have.property(Sym.iterator, Map.prototype.entries);
+  });
+
+  describe('#forEach', function () {
+    var mapToIterate;
+
+    beforeEach(function () {
+      mapToIterate = new Map();
+      expect(mapToIterate.set('a', 1)).to.equal(mapToIterate);
+      expect(mapToIterate.set('b', 2)).to.equal(mapToIterate);
+      expect(mapToIterate.set('c', 3)).to.equal(mapToIterate);
+    });
+
+    afterEach(function () {
+      mapToIterate = null;
+    });
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Map.prototype.forEach).to.have.property('name', 'forEach');
+    });
+
+    it('is not enumerable', function () {
+      expect(Map.prototype).ownPropertyDescriptor('forEach').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Map.prototype.forEach).to.have.property('length', 1);
+    });
+
+    it('should be iterable via forEach', function () {
+      var expectedMap = {
+        a: 1,
+        b: 2,
+        c: 3
+      };
+      var foundMap = {};
+      mapToIterate.forEach(function (value, key, entireMap) {
+        expect(entireMap).to.equal(mapToIterate);
+        foundMap[key] = value;
+      });
+      expect(foundMap).to.eql(expectedMap);
+    });
+
+    it('should iterate over empty keys', function () {
+      var mapWithEmptyKeys = new Map();
+      var expectedKeys = [{}, null, undefined, '', NaN, 0];
+      expectedKeys.forEach(function (key) {
+        expect(mapWithEmptyKeys.set(key, true)).to.equal(mapWithEmptyKeys);
+      });
+      var foundKeys = [];
+      mapWithEmptyKeys.forEach(function (value, key, entireMap) {
+        expect(entireMap.get(key)).to.equal(value);
+        foundKeys.push(key);
+      });
+      expect(foundKeys).to.be.theSameSet(expectedKeys);
+    });
+
+    it('should support the thisArg', function () {
+      var context = function () {};
+      mapToIterate.forEach(function () {
+        expect(this).to.equal(context);
+      }, context);
+    });
+
+    it('should have a length of 1', function () {
+      expect(Map.prototype.forEach.length).to.equal(1);
+    });
+
+    it('should not revisit modified keys', function () {
+      var hasModifiedA = false;
+      mapToIterate.forEach(function (value, key) {
+        if (!hasModifiedA && key === 'a') {
+          expect(mapToIterate.set('a', 4)).to.equal(mapToIterate);
+          hasModifiedA = true;
+        } else {
+          expect(key).not.to.equal('a');
+        }
+      });
+    });
+
+    it('returns the map from #set() for chaining', function () {
+      expect(mapToIterate.set({}, {})).to.equal(mapToIterate);
+      expect(mapToIterate.set(42, {})).to.equal(mapToIterate);
+      expect(mapToIterate.set(0, {})).to.equal(mapToIterate);
+      expect(mapToIterate.set(NaN, {})).to.equal(mapToIterate);
+      expect(mapToIterate.set(-0, {})).to.equal(mapToIterate);
+    });
+
+    it('visits keys added in the iterator', function () {
+      var hasAdded = false;
+      var hasFoundD = false;
+      mapToIterate.forEach(function (value, key) {
+        if (!hasAdded) {
+          mapToIterate.set('d', 5);
+          hasAdded = true;
+        } else if (key === 'd') {
+          hasFoundD = true;
+        }
+      });
+      expect(hasFoundD).to.equal(true);
+    });
+
+    it('visits keys added in the iterator when there is a deletion', function () {
+      var hasSeenFour = false;
+      var mapToMutate = new Map();
+      mapToMutate.set('0', 42);
+      mapToMutate.forEach(function (value, key) {
+        if (key === '0') {
+          expect(mapToMutate['delete']('0')).to.equal(true);
+          mapToMutate.set('4', 'a value');
+        } else if (key === '4') {
+          hasSeenFour = true;
+        }
+      });
+      expect(hasSeenFour).to.equal(true);
+    });
+
+    it('does not visit keys deleted before a visit', function () {
+      var hasVisitedC = false;
+      var hasDeletedC = false;
+      mapToIterate.forEach(function (value, key) {
+        if (key === 'c') {
+          hasVisitedC = true;
+        }
+        if (!hasVisitedC && !hasDeletedC) {
+          hasDeletedC = mapToIterate['delete']('c');
+          expect(hasDeletedC).to.equal(true);
+        }
+      });
+      expect(hasVisitedC).to.equal(false);
+    });
+
+    it('should work after deletion of the current key', function () {
+      var expectedMap = {
+        a: 1,
+        b: 2,
+        c: 3
+      };
+      var foundMap = {};
+      mapToIterate.forEach(function (value, key) {
+        foundMap[key] = value;
+        expect(mapToIterate['delete'](key)).to.equal(true);
+      });
+      expect(foundMap).to.eql(expectedMap);
+    });
+
+    it('should convert key -0 to +0', function () {
+      var zeroMap = new Map();
+      var result = [];
+      zeroMap.set(-0, 'a');
+      zeroMap.forEach(function (value, key) {
+        result.push(String(1 / key) + ' ' + value);
+      });
+      zeroMap.set(1, 'b');
+      zeroMap.set(0, 'c'); // shouldn't cause reordering
+      zeroMap.forEach(function (value, key) {
+        result.push(String(1 / key) + ' ' + value);
+      });
+      expect(result.join(', ')).to.equal(
+        'Infinity a, Infinity c, 1 b'
+      );
+    });
+  });
+
+  it('should preserve insertion order', function () {
+    var convertToPairs = function (item) { return [item, true]; };
+    var arr1 = ['d', 'a', 'b'];
+    var arr2 = [3, 2, 'z', 'a', 1];
+    var arr3 = [3, 2, 'z', {}, 'a', 1];
+
+    [arr1, arr2, arr3].forEach(function (array) {
+      var entries = array.map(convertToPairs);
+      expect(new Map(entries)).to.have.entries(entries);
+    });
+  });
+
+  it('map iteration', function () {
+    var map = new Map();
+    map.set('a', 1);
+    map.set('b', 2);
+    map.set('c', 3);
+    map.set('d', 4);
+
+    var keys = [];
+    var iterator = map.keys();
+    keys.push(iterator.next().value);
+    expect(map['delete']('a')).to.equal(true);
+    expect(map['delete']('b')).to.equal(true);
+    expect(map['delete']('c')).to.equal(true);
+    map.set('e');
+    keys.push(iterator.next().value);
+    keys.push(iterator.next().value);
+
+    expect(iterator.next().done).to.equal(true);
+    map.set('f');
+    expect(iterator.next().done).to.equal(true);
+    expect(keys).to.eql(['a', 'd', 'e']);
+  });
+});
diff --git a/test/native.html b/test/native.html
index 251efc8..51f8dc0 100644
--- a/test/native.html
+++ b/test/native.html
@@ -9,7 +9,8 @@
   <script src="../node_modules/chai/chai.js"></script>
   <script src="browser-setup.js"></script>
   <script src="array.js"></script>
-  <script src="collections.js"></script>
+  <script src="map.js"></script>
+  <script src="set.js"></script>
   <script src="json.js"></script>
   <script src="math.js"></script>
   <script src="number.js"></script>
diff --git a/test/set.js b/test/set.js
new file mode 100644
index 0000000..1e5e40d
--- /dev/null
+++ b/test/set.js
@@ -0,0 +1,639 @@
+/* global describe, it, xit, expect, require, beforeEach, afterEach */
+
+// Big thanks to V8 folks for test ideas.
+// v8/test/mjsunit/harmony/collections.js
+
+var Assertion = expect().constructor;
+Assertion.addMethod('theSameSet', function (otherArray) {
+  var array = this._obj;
+
+  expect(Array.isArray(array)).to.equal(true);
+  expect(Array.isArray(otherArray)).to.equal(true);
+
+  var diff = array.filter(function (value) {
+    return otherArray.every(function (otherValue) {
+      var areBothNaN = typeof value === 'number' && typeof otherValue === 'number' && value !== value && otherValue !== otherValue;
+      return !areBothNaN && value !== otherValue;
+    });
+  });
+
+  this.assert(
+    diff.length === 0,
+    'expected #{this} to be equal to #{exp} (as sets, i.e. no order)',
+    array,
+    otherArray
+  );
+});
+
+var $iterator$ = typeof Symbol === 'function' ? Symbol.iterator : void 0;
+if (!$iterator$ && typeof Set === 'function') {
+  $iterator$ = typeof Set['@@iterator'] === 'function' ? '@@iterator' : '_es6-shim iterator_';
+}
+
+Assertion.addMethod('iterations', function (expected) {
+  var iterator = this._obj[$iterator$]();
+
+  expect(Array.isArray(expected)).to.equal(true);
+  var expectedValues = expected.slice();
+
+  var result;
+  do {
+    result = iterator.next();
+    expect(result.value).to.eql(expectedValues.shift());
+  } while (!result.done);
+});
+
+describe('Set', function () {
+  var functionsHaveNames = (function foo() {}).name === 'foo';
+  var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
+  var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
+
+  var range = function (from, to) {
+    var result = [];
+    for (var value = from; value < to; value++) {
+      result.push(value);
+    }
+    return result;
+  };
+
+  var prototypePropIsEnumerable = (function () {}).propertyIsEnumerable('prototype');
+  var expectNotEnumerable = function (object) {
+    if (prototypePropIsEnumerable && typeof object === 'function') {
+      expect(Object.keys(object)).to.eql(['prototype']);
+    } else {
+      expect(Object.keys(object)).to.eql([]);
+    }
+  };
+
+  var Sym = typeof Symbol === 'undefined' ? {} : Symbol;
+  var isSymbol = function (sym) {
+    return typeof Sym === 'function' && typeof sym === 'symbol';
+  };
+  var ifSymbolIteratorIt = isSymbol(Sym.iterator) ? it : xit;
+
+  var testSet = function (set, key) {
+    expect(set.has(key)).to.equal(false);
+    expect(set['delete'](key)).to.equal(false);
+    expect(set.add(key)).to.equal(set);
+    expect(set.has(key)).to.equal(true);
+    expect(set['delete'](key)).to.equal(true);
+    expect(set.has(key)).to.equal(false);
+    expect(set.add(key)).to.equal(set); // add it back
+  };
+
+  if (typeof Set === 'undefined') {
+    return it('exists', function () {
+      expect(typeof Set).to.equal('function');
+    });
+  }
+
+  var set;
+  beforeEach(function () {
+    set = new Set();
+  });
+
+  afterEach(function () {
+    set = null;
+  });
+
+  it('set iteration', function () {
+    expect(set.add('a')).to.equal(set);
+    expect(set.add('b')).to.equal(set);
+    expect(set.add('c')).to.equal(set);
+    expect(set.add('d')).to.equal(set);
+
+    var keys = [];
+    var iterator = set.keys();
+    keys.push(iterator.next().value);
+    expect(set['delete']('a')).to.equal(true);
+    expect(set['delete']('b')).to.equal(true);
+    expect(set['delete']('c')).to.equal(true);
+    expect(set.add('e')).to.equal(set);
+    keys.push(iterator.next().value);
+    keys.push(iterator.next().value);
+
+    expect(iterator.next().done).to.equal(true);
+    expect(set.add('f')).to.equal(set);
+    expect(iterator.next().done).to.equal(true);
+    expect(keys).to.eql(['a', 'd', 'e']);
+  });
+
+  ifShimIt('is on the exported object', function () {
+    var exported = require('../');
+    expect(exported.Set).to.equal(Set);
+  });
+
+  it('should exist in global namespace', function () {
+    expect(typeof Set).to.equal('function');
+  });
+
+  it('has the right arity', function () {
+    expect(Set).to.have.property('length', 0);
+  });
+
+  it('returns the set from #add() for chaining', function () {
+    expect(set.add({})).to.equal(set);
+  });
+
+  it('should return false when deleting an item not in the set', function () {
+    expect(set.has('a')).to.equal(false);
+    expect(set['delete']('a')).to.equal(false);
+  });
+
+  it('should accept an iterable as argument', function () {
+    testSet(set, 'a');
+    testSet(set, 'b');
+    var set2 = new Set(set);
+    expect(set2.has('a')).to.equal(true);
+    expect(set2.has('b')).to.equal(true);
+    expect(set2).to.have.iterations(['a', 'b']);
+  });
+
+  it('accepts an array as an argument', function () {
+    var arr = ['a', 'b', 'c'];
+    var setFromArray = new Set(arr);
+    expect(setFromArray).to.have.iterations(['a', 'b', 'c']);
+  });
+
+  it('should not be callable without "new"', function () {
+    expect(Set).to['throw'](TypeError);
+  });
+
+  it('should be subclassable', function () {
+    if (!Object.setPrototypeOf) { return; } // skip test if on IE < 11
+    var MySet = function MySet() {
+      var actualSet = new Set(['a', 'b']);
+      Object.setPrototypeOf(actualSet, MySet.prototype);
+      return actualSet;
+    };
+    Object.setPrototypeOf(MySet, Set);
+    MySet.prototype = Object.create(Set.prototype, {
+      constructor: { value: MySet }
+    });
+
+    var mySet = new MySet();
+    testSet(mySet, 'c');
+    testSet(mySet, 'd');
+    expect(mySet).to.have.iterations(['a', 'b', 'c', 'd']);
+  });
+
+  it('should has valid getter and setter calls', function () {
+    ['add', 'has', 'delete'].forEach(function (method) {
+      expect(function () {
+        set[method]({});
+      }).to.not['throw']();
+    });
+  });
+
+  it('uses SameValueZero even on a Set of size > 4', function () {
+    var firstFour = [1, 2, 3, 4];
+    var fourSet = new Set(firstFour);
+    expect(fourSet.size).to.equal(4);
+    expect(fourSet.has(-0)).to.equal(false);
+    expect(fourSet.has(0)).to.equal(false);
+
+    fourSet.add(-0);
+
+    expect(fourSet.size).to.equal(5);
+    expect(fourSet.has(0)).to.equal(true);
+    expect(fourSet.has(-0)).to.equal(true);
+  });
+
+  it('should work as expected', function () {
+    // Run this test twice, one with the "fast" implementation (which only
+    // allows string and numeric keys) and once with the "slow" impl.
+    [true, false].forEach(function (slowkeys) {
+      set = new Set();
+
+      range(1, 20).forEach(function (number) {
+        if (slowkeys) { testSet(set, {}); }
+        testSet(set, number);
+        testSet(set, number / 100);
+        testSet(set, 'key-' + number);
+        testSet(set, String(number));
+        if (slowkeys) { testSet(set, Object(String(number))); }
+      });
+
+      var testkeys = [+0, Infinity, -Infinity, NaN];
+      if (slowkeys) {
+        testkeys.push(true, false, null, undefined);
+      }
+      testkeys.forEach(function (number) {
+        testSet(set, number);
+        testSet(set, String(number));
+      });
+      testSet(set, '');
+
+      // -0 and +0 should be the same key (Set uses SameValueZero)
+      expect(set.has(-0)).to.equal(true);
+      expect(set['delete'](+0)).to.equal(true);
+      testSet(set, -0);
+      expect(set.has(+0)).to.equal(true);
+
+      // verify that properties of Object don't peek through.
+      [
+        'hasOwnProperty',
+        'constructor',
+        'toString',
+        'isPrototypeOf',
+        '__proto__',
+        '__parent__',
+        '__count__'
+      ].forEach(function (prop) { testSet(set, prop); });
+    });
+  });
+
+  describe('#size', function () {
+    it('returns the expected size', function () {
+      expect(set.add(1)).to.equal(set);
+      expect(set.add(5)).to.equal(set);
+      expect(set.size).to.equal(2);
+    });
+  });
+
+  describe('#clear()', function () {
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Set.prototype.clear).to.have.property('name', 'clear');
+    });
+
+    it('is not enumerable', function () {
+      expect(Set.prototype).ownPropertyDescriptor('clear').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Set.prototype.clear).to.have.property('length', 0);
+    });
+
+    it('clears a Set with only primitives', function () {
+      expect(set.add(1)).to.equal(set);
+      expect(set.size).to.equal(1);
+      expect(set.add(5)).to.equal(set);
+      expect(set.size).to.equal(2);
+      expect(set.has(5)).to.equal(true);
+      set.clear();
+      expect(set.size).to.equal(0);
+      expect(set.has(5)).to.equal(false);
+    });
+
+    it('clears a Set with primitives and objects', function () {
+      expect(set.add(1)).to.equal(set);
+      expect(set.size).to.equal(1);
+      var obj = {};
+      expect(set.add(obj)).to.equal(set);
+      expect(set.size).to.equal(2);
+      expect(set.has(obj)).to.equal(true);
+      set.clear();
+      expect(set.size).to.equal(0);
+      expect(set.has(obj)).to.equal(false);
+    });
+  });
+
+  describe('#keys()', function () {
+    if (!Set.prototype.hasOwnProperty('keys')) {
+      return it('exists', function () {
+        expect(Set.prototype).to.have.property('keys');
+      });
+    }
+
+    it('is the same object as #values()', function () {
+      expect(Set.prototype.keys).to.equal(Set.prototype.values);
+    });
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Set.prototype.keys).to.have.property('name', 'values');
+    });
+
+    it('is not enumerable', function () {
+      expect(Set.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Set.prototype.keys).to.have.property('length', 0);
+    });
+  });
+
+  describe('#values()', function () {
+    if (!Set.prototype.hasOwnProperty('values')) {
+      return it('exists', function () {
+        expect(Set.prototype).to.have.property('values');
+      });
+    }
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Set.prototype.values).to.have.property('name', 'values');
+    });
+
+    it('is not enumerable', function () {
+      expect(Set.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Set.prototype.values).to.have.property('length', 0);
+    });
+
+    it('throws when called on a non-Set', function () {
+      var expectedMessage = /^(Method )?Set.prototype.values called on incompatible receiver |^values method called on incompatible |^Cannot create a Set value iterator for a non-Set object.$|^Set.prototype.values: 'this' is not a Set object$/;
+      var nonSets = [true, false, 'abc', NaN, new Map([[1, 2]]), { a: true }, [1], Object('abc'), Object(NaN)];
+      nonSets.forEach(function (nonSet) {
+        expect(function () { return Set.prototype.values.call(nonSet); }).to['throw'](TypeError, expectedMessage);
+      });
+    });
+  });
+
+  describe('#entries()', function () {
+    if (!Set.prototype.hasOwnProperty('entries')) {
+      return it('exists', function () {
+        expect(Set.prototype).to.have.property('entries');
+      });
+    }
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Set.prototype.entries).to.have.property('name', 'entries');
+    });
+
+    it('is not enumerable', function () {
+      expect(Set.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Set.prototype.entries).to.have.property('length', 0);
+    });
+  });
+
+  describe('#has()', function () {
+    if (!Set.prototype.hasOwnProperty('has')) {
+      return it('exists', function () {
+        expect(Set.prototype).to.have.property('has');
+      });
+    }
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Set.prototype.has).to.have.property('name', 'has');
+    });
+
+    it('is not enumerable', function () {
+      expect(Set.prototype).ownPropertyDescriptor('has').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Set.prototype.has).to.have.property('length', 1);
+    });
+  });
+
+  it('should allow NaN values as keys', function () {
+    expect(set.has(NaN)).to.equal(false);
+    expect(set.has(NaN + 1)).to.equal(false);
+    expect(set.has(23)).to.equal(false);
+    expect(set.add(NaN)).to.equal(set);
+    expect(set.has(NaN)).to.equal(true);
+    expect(set.has(NaN + 1)).to.equal(true);
+    expect(set.has(23)).to.equal(false);
+  });
+
+  it('should not have [[Enumerable]] props', function () {
+    expectNotEnumerable(Set);
+    expectNotEnumerable(Set.prototype);
+    expectNotEnumerable(new Set());
+  });
+
+  it('should not have an own constructor', function () {
+    var s = new Set();
+    expect(s).not.to.haveOwnPropertyDescriptor('constructor');
+    expect(s.constructor).to.equal(Set);
+  });
+
+  it('should allow common ecmascript idioms', function () {
+    expect(set instanceof Set).to.equal(true);
+    expect(typeof Set.prototype.add).to.equal('function');
+    expect(typeof Set.prototype.has).to.equal('function');
+    expect(typeof Set.prototype['delete']).to.equal('function');
+  });
+
+  it('should have a unique constructor', function () {
+    expect(Set.prototype).to.not.equal(Object.prototype);
+  });
+
+  describe('has an iterator that works with Array.from', function () {
+    if (!Array.hasOwnProperty('from')) {
+      return it('requires Array.from to exist', function () {
+        expect(Array).to.have.property('from');
+      });
+    }
+
+    var values = [1, NaN, false, true, null, undefined, 'a'];
+
+    it('works with the full set', function () {
+      expect(new Set(values)).to.have.iterations(values);
+    });
+
+    it('works with Set#keys()', function () {
+      expect(new Set(values).keys()).to.have.iterations(values);
+    });
+
+    it('works with Set#values()', function () {
+      expect(new Set(values).values()).to.have.iterations(values);
+    });
+
+    it('works with Set#entries()', function () {
+      expect(new Set(values).entries()).to.have.iterations([
+        [1, 1],
+        [NaN, NaN],
+        [false, false],
+        [true, true],
+        [null, null],
+        [undefined, undefined],
+        ['a', 'a']
+      ]);
+    });
+  });
+
+  ifSymbolIteratorIt('has the right default iteration function', function () {
+    // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
+    expect(Set.prototype).to.have.property(Sym.iterator, Set.prototype.values);
+  });
+
+  it('should preserve insertion order', function () {
+    var arr1 = ['d', 'a', 'b'];
+    var arr2 = [3, 2, 'z', 'a', 1];
+    var arr3 = [3, 2, 'z', {}, 'a', 1];
+
+    [arr1, arr2, arr3].forEach(function (array) {
+      expect(new Set(array)).to.have.iterations(array);
+    });
+  });
+
+  describe('#forEach', function () {
+    var setToIterate;
+    beforeEach(function () {
+      setToIterate = new Set();
+      expect(setToIterate.add('a')).to.equal(setToIterate);
+      expect(setToIterate.add('b')).to.equal(setToIterate);
+      expect(setToIterate.add('c')).to.equal(setToIterate);
+    });
+
+    afterEach(function () {
+      setToIterate = null;
+    });
+
+    ifFunctionsHaveNamesIt('has the right name', function () {
+      expect(Set.prototype.forEach).to.have.property('name', 'forEach');
+    });
+
+    it('is not enumerable', function () {
+      expect(Set.prototype).ownPropertyDescriptor('forEach').to.have.property('enumerable', false);
+    });
+
+    it('has the right arity', function () {
+      expect(Set.prototype.forEach).to.have.property('length', 1);
+    });
+
+    it('should be iterable via forEach', function () {
+      var expectedSet = ['a', 'b', 'c'];
+      var foundSet = [];
+      setToIterate.forEach(function (value, alsoValue, entireSet) {
+        expect(entireSet).to.equal(setToIterate);
+        expect(value).to.equal(alsoValue);
+        foundSet.push(value);
+      });
+      expect(foundSet).to.eql(expectedSet);
+    });
+
+    it('should iterate over empty keys', function () {
+      var setWithEmptyKeys = new Set();
+      var expectedKeys = [{}, null, undefined, '', NaN, 0];
+      expectedKeys.forEach(function (key) {
+        expect(setWithEmptyKeys.add(key)).to.equal(setWithEmptyKeys);
+      });
+      var foundKeys = [];
+      setWithEmptyKeys.forEach(function (value, key, entireSet) {
+        expect([key]).to.be.theSameSet([value]); // handles NaN correctly
+        expect(entireSet.has(key)).to.equal(true);
+        foundKeys.push(key);
+      });
+      expect(foundKeys).to.be.theSameSet(expectedKeys);
+    });
+
+    it('should support the thisArg', function () {
+      var context = function () {};
+      setToIterate.forEach(function () {
+        expect(this).to.equal(context);
+      }, context);
+    });
+
+    it('should have a length of 1', function () {
+      expect(Set.prototype.forEach.length).to.equal(1);
+    });
+
+    it('should not revisit modified keys', function () {
+      var hasModifiedA = false;
+      setToIterate.forEach(function (value, key) {
+        if (!hasModifiedA && key === 'a') {
+          expect(setToIterate.add('a')).to.equal(setToIterate);
+          hasModifiedA = true;
+        } else {
+          expect(key).not.to.equal('a');
+        }
+      });
+    });
+
+    it('visits keys added in the iterator', function () {
+      var hasAdded = false;
+      var hasFoundD = false;
+      setToIterate.forEach(function (value, key) {
+        if (!hasAdded) {
+          expect(setToIterate.add('d')).to.equal(setToIterate);
+          hasAdded = true;
+        } else if (key === 'd') {
+          hasFoundD = true;
+        }
+      });
+      expect(hasFoundD).to.equal(true);
+    });
+
+    it('visits keys added in the iterator when there is a deletion (slow path)', function () {
+      var hasSeenFour = false;
+      var setToMutate = new Set();
+      expect(setToMutate.add({})).to.equal(setToMutate); // force use of the slow O(N) implementation
+      expect(setToMutate.add('0')).to.equal(setToMutate);
+      setToMutate.forEach(function (value, key) {
+        if (key === '0') {
+          expect(setToMutate['delete']('0')).to.equal(true);
+          expect(setToMutate.add('4')).to.equal(setToMutate);
+        } else if (key === '4') {
+          hasSeenFour = true;
+        }
+      });
+      expect(hasSeenFour).to.equal(true);
+    });
+
+    it('visits keys added in the iterator when there is a deletion (fast path)', function () {
+      var hasSeenFour = false;
+      var setToMutate = new Set();
+      expect(setToMutate.add('0')).to.equal(setToMutate);
+      setToMutate.forEach(function (value, key) {
+        if (key === '0') {
+          expect(setToMutate['delete']('0')).to.equal(true);
+          expect(setToMutate.add('4')).to.equal(setToMutate);
+        } else if (key === '4') {
+          hasSeenFour = true;
+        }
+      });
+      expect(hasSeenFour).to.equal(true);
+    });
+
+    it('does not visit keys deleted before a visit', function () {
+      var hasVisitedC = false;
+      var hasDeletedC = false;
+      setToIterate.forEach(function (value, key) {
+        if (key === 'c') {
+          hasVisitedC = true;
+        }
+        if (!hasVisitedC && !hasDeletedC) {
+          hasDeletedC = setToIterate['delete']('c');
+          expect(hasDeletedC).to.equal(true);
+        }
+      });
+      expect(hasVisitedC).to.equal(false);
+    });
+
+    it('should work after deletion of the current key', function () {
+      var expectedSet = {
+        a: 'a',
+        b: 'b',
+        c: 'c'
+      };
+      var foundSet = {};
+      setToIterate.forEach(function (value, key) {
+        foundSet[key] = value;
+        expect(setToIterate['delete'](key)).to.equal(true);
+      });
+      expect(foundSet).to.eql(expectedSet);
+    });
+
+    it('should convert key -0 to +0', function () {
+      var zeroSet = new Set();
+      var result = [];
+      expect(zeroSet.add(-0)).to.equal(zeroSet);
+      zeroSet.forEach(function (key) {
+        result.push(String(1 / key));
+      });
+      expect(zeroSet.add(1)).to.equal(zeroSet);
+      expect(zeroSet.add(0)).to.equal(zeroSet); // shouldn't cause reordering
+      zeroSet.forEach(function (key) {
+        result.push(String(1 / key));
+      });
+      expect(result.join(', ')).to.equal(
+        'Infinity, Infinity, 1'
+      );
+    });
+  });
+
+  it('Set.prototype.size should throw TypeError', function () {
+    // see https://github.com/paulmillr/es6-shim/issues/176
+    expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
+    expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
+  });
+
+  it.skip('should throw proper errors when user invokes methods with wrong types of receiver', function () {
+  });
+});
diff --git a/test/string.js b/test/string.js
index 3b497a7..5a714cd 100644
--- a/test/string.js
+++ b/test/string.js
@@ -684,13 +684,20 @@ var runStringTests = function (it) {
     });
 
     describe('#search()', function () {
-      it('works', function () {
+      it('works with strings', function () {
         expect('abc'.search('a')).to.equal(0);
         expect('abc'.search('b')).to.equal(1);
         expect('abc'.search('c')).to.equal(2);
         expect('abc'.search('d')).to.equal(-1);
       });
 
+      it('works with regexes', function () {
+        expect('abc'.search(/a/)).to.equal(0);
+        expect('abc'.search(/b/)).to.equal(1);
+        expect('abc'.search(/c/)).to.equal(2);
+        expect('abc'.search(/d/)).to.equal(-1);
+      });
+
       ifSymbolsDescribe('Symbol.search', function () {
         it('is a symbol', function () {
           expect(typeof Symbol.search).to.equal('symbol');
diff --git a/testling.html b/testling.html
index ec54071..e9746a4 100644
--- a/testling.html
+++ b/testling.html
@@ -11,7 +11,8 @@
   <script src="es6-shim.js"></script>
   <script>chai.config.includeStack=true; window.expect=chai.expect; window.assert=chai.assert; mocha.setup({ui:'bdd',reporter:'tap'});</script>
   <script src="test/array.js"></script>
-  <script src="test/collections.js"></script>
+  <script src="test/map.js"></script>
+  <script src="test/set.js"></script>
   <script src="test/math.js"></script>
   <script src="test/number.js"></script>
   <script src="test/object.js"></script>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-es6-shim.git



More information about the Pkg-javascript-commits mailing list