From f4665f6a850e64d3f61c8007404347ce3e188474 Mon Sep 17 00:00:00 2001 From: Sasha Aickin Date: Tue, 6 Dec 2016 17:29:10 -0800 Subject: [PATCH 1/4] Fix generator prototype chain to more correctly match ES2015 spec; now (Generator).prototype inherits from %IteratorPrototype%. --- packages/regenerator-runtime/runtime.js | 40 +++++++++++++++++++++++-- test/non-native.js | 4 +-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/packages/regenerator-runtime/runtime.js b/packages/regenerator-runtime/runtime.js index 1859369d7..d40b1ddab 100644 --- a/packages/regenerator-runtime/runtime.js +++ b/packages/regenerator-runtime/runtime.js @@ -366,13 +366,47 @@ }; } + // for browsers that support it, return the shared %IteratorPrototype%. + function getIteratorPrototype() { + if (!([][iteratorSymbol])) { + return null; + } + var iteratorInstance = [][iteratorSymbol](); + if (!iteratorInstance) { + return null; + } + var iteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(iteratorInstance)); + // some older browser engines don't support %IteratorPrototype% and have + // %ArrayIteratorPrototype% inherit directly from Object.prototype. these checks + // are to weed out those non-compliant browsers. + if (!iteratorPrototype + || !iteratorPrototype.hasOwnProperty(iteratorSymbol) + || iteratorPrototype === Object.prototype) { + return null; + } + return iteratorPrototype; + } + // Define Generator.prototype.{next,throw,return} in terms of the // unified ._invoke helper method. defineIteratorMethods(Gp); - Gp[iteratorSymbol] = function() { - return this; - }; + if (getIteratorPrototype()) { + // for browsers that do support %IteratorPrototype%, (Generator).prototype + // should inherit from %IteratorPrototype%. + if (Object.setPrototypeOf) { + Object.setPrototypeOf(Gp, getIteratorPrototype()); + } else { + Gp.__proto__ = getIteratorPrototype(); + } + } else { + // in browsers that don't support %IteratorPrototype%, set the [Symbol.iterator] + // property on (Generator).prototype, which is not spec-compliant, but the best + // we can do. + Gp[iteratorSymbol] = function() { + return this; + }; + } Gp[toStringTagSymbol] = "Generator"; diff --git a/test/non-native.js b/test/non-native.js index bd7ad702f..694ff1953 100644 --- a/test/non-native.js +++ b/test/non-native.js @@ -12,9 +12,9 @@ describe("@@iterator", function() { var iterator = gen(); assert.ok(!iterator.hasOwnProperty(Symbol.iterator)); assert.ok(!Object.getPrototypeOf(iterator).hasOwnProperty(Symbol.iterator)); - assert.ok(Object.getPrototypeOf( + assert.ok(Object.getPrototypeOf(Object.getPrototypeOf( Object.getPrototypeOf(iterator) - ).hasOwnProperty(Symbol.iterator)); + )).hasOwnProperty(Symbol.iterator)); assert.strictEqual(iterator[Symbol.iterator](), iterator); }); }); From 47d9b673c13435faa63fd9391955c1e2cb0eb21c Mon Sep 17 00:00:00 2001 From: Sasha Aickin Date: Wed, 7 Dec 2016 00:15:00 -0800 Subject: [PATCH 2/4] Reworked the code to be more correct for environments that don't support %IteratorPrototype% and environments that don't support resetting prototypes. --- packages/regenerator-runtime/runtime.js | 56 ++++++++++++++----------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/regenerator-runtime/runtime.js b/packages/regenerator-runtime/runtime.js index d40b1ddab..002ade8f3 100644 --- a/packages/regenerator-runtime/runtime.js +++ b/packages/regenerator-runtime/runtime.js @@ -366,43 +366,51 @@ }; } - // for browsers that support it, return the shared %IteratorPrototype%. + // Define Generator.prototype.{next,throw,return} in terms of the + // unified ._invoke helper method. + defineIteratorMethods(Gp); + + // for browsers that do not support %IteratorPrototype%, this is a polyfilled + // %IteratorPrototype% + var IteratorPrototype = {}; + IteratorPrototype[iteratorSymbol] = function() { + return this; + }; + + // for browsers that support it, return the shared %IteratorPrototype%. for ones, + // that don't, return the polyfill. function getIteratorPrototype() { if (!([][iteratorSymbol])) { - return null; + // arrays don't have an @@iterator property, return the polyfill. + return IteratorPrototype; } var iteratorInstance = [][iteratorSymbol](); if (!iteratorInstance) { - return null; + // for some reason, array iterators aren't callable. return the polyfill. + return IteratorPrototype; } - var iteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(iteratorInstance)); + var nativeIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(iteratorInstance)); // some older browser engines don't support %IteratorPrototype% and have // %ArrayIteratorPrototype% inherit directly from Object.prototype. these checks // are to weed out those non-compliant browsers. - if (!iteratorPrototype - || !iteratorPrototype.hasOwnProperty(iteratorSymbol) - || iteratorPrototype === Object.prototype) { - return null; + if (nativeIteratorPrototype + && nativeIteratorPrototype.hasOwnProperty(iteratorSymbol) + && nativeIteratorPrototype !== Object.prototype) { + // looks like the browser has an ES2015-compliant %IteratorPrototype%, so + // we can return it. + return nativeIteratorPrototype; } - return iteratorPrototype; + // otherwise, return the polyfill. + return IteratorPrototype; } - // Define Generator.prototype.{next,throw,return} in terms of the - // unified ._invoke helper method. - defineIteratorMethods(Gp); - - if (getIteratorPrototype()) { - // for browsers that do support %IteratorPrototype%, (Generator).prototype - // should inherit from %IteratorPrototype%. - if (Object.setPrototypeOf) { - Object.setPrototypeOf(Gp, getIteratorPrototype()); - } else { - Gp.__proto__ = getIteratorPrototype(); - } + if (Object.setPrototypeOf) { + Object.setPrototypeOf(Gp, getIteratorPrototype()); + } else if ('__proto__' in Object.prototype){ + Gp.__proto__ = getIteratorPrototype(); } else { - // in browsers that don't support %IteratorPrototype%, set the [Symbol.iterator] - // property on (Generator).prototype, which is not spec-compliant, but the best - // we can do. + // this is an environment where we can't reset prototypes; the best we can do + // is add the @@iterator property directly to %GeneratorPrototype%. Gp[iteratorSymbol] = function() { return this; }; From 21b9a251806b3539972f1ef834c0b2a01b7e1fdb Mon Sep 17 00:00:00 2001 From: Sasha Aickin Date: Wed, 7 Dec 2016 11:42:00 -0800 Subject: [PATCH 3/4] More compact and robust implementation using Object.create; result of a very helpful code review by @benjamn --- packages/regenerator-runtime/runtime.js | 64 +++++++------------------ 1 file changed, 17 insertions(+), 47 deletions(-) diff --git a/packages/regenerator-runtime/runtime.js b/packages/regenerator-runtime/runtime.js index 002ade8f3..6ea12a5e5 100644 --- a/packages/regenerator-runtime/runtime.js +++ b/packages/regenerator-runtime/runtime.js @@ -83,7 +83,23 @@ function GeneratorFunction() {} function GeneratorFunctionPrototype() {} - var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype; + // this is a polyfill for %IteratorPrototype% for environments that don't + // natively support it. + var IteratorPrototype = {}; + IteratorPrototype[iteratorSymbol] = function() {return this;}; + var getProto = Object.getPrototypeOf; + if (getProto) { + var NativeIteratorPrototype = getProto(getProto(values([]))); + if (NativeIteratorPrototype && + NativeIteratorPrototype !== Object.prototype && + NativeIteratorPrototype.hasOwnProperty(iteratorSymbol)) { + // this environment has a native %IteratorPrototype%; use it instead of + // a polyfill. + IteratorPrototype = NativeIteratorPrototype; + } + } + + var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; GeneratorFunctionPrototype.constructor = GeneratorFunction; GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction"; @@ -370,52 +386,6 @@ // unified ._invoke helper method. defineIteratorMethods(Gp); - // for browsers that do not support %IteratorPrototype%, this is a polyfilled - // %IteratorPrototype% - var IteratorPrototype = {}; - IteratorPrototype[iteratorSymbol] = function() { - return this; - }; - - // for browsers that support it, return the shared %IteratorPrototype%. for ones, - // that don't, return the polyfill. - function getIteratorPrototype() { - if (!([][iteratorSymbol])) { - // arrays don't have an @@iterator property, return the polyfill. - return IteratorPrototype; - } - var iteratorInstance = [][iteratorSymbol](); - if (!iteratorInstance) { - // for some reason, array iterators aren't callable. return the polyfill. - return IteratorPrototype; - } - var nativeIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(iteratorInstance)); - // some older browser engines don't support %IteratorPrototype% and have - // %ArrayIteratorPrototype% inherit directly from Object.prototype. these checks - // are to weed out those non-compliant browsers. - if (nativeIteratorPrototype - && nativeIteratorPrototype.hasOwnProperty(iteratorSymbol) - && nativeIteratorPrototype !== Object.prototype) { - // looks like the browser has an ES2015-compliant %IteratorPrototype%, so - // we can return it. - return nativeIteratorPrototype; - } - // otherwise, return the polyfill. - return IteratorPrototype; - } - - if (Object.setPrototypeOf) { - Object.setPrototypeOf(Gp, getIteratorPrototype()); - } else if ('__proto__' in Object.prototype){ - Gp.__proto__ = getIteratorPrototype(); - } else { - // this is an environment where we can't reset prototypes; the best we can do - // is add the @@iterator property directly to %GeneratorPrototype%. - Gp[iteratorSymbol] = function() { - return this; - }; - } - Gp[toStringTagSymbol] = "Generator"; Gp.toString = function() { From c455b1d43e8c93e64020dd441f63593db9bc7123 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 7 Dec 2016 17:55:37 -0500 Subject: [PATCH 4/4] Code golf and stylistic tweaks for #264. --- packages/regenerator-runtime/runtime.js | 34 ++++++++++++++----------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/regenerator-runtime/runtime.js b/packages/regenerator-runtime/runtime.js index 6ea12a5e5..15fdacce8 100644 --- a/packages/regenerator-runtime/runtime.js +++ b/packages/regenerator-runtime/runtime.js @@ -11,7 +11,8 @@ !(function(global) { "use strict"; - var hasOwn = Object.prototype.hasOwnProperty; + var Op = Object.prototype; + var hasOwn = Op.hasOwnProperty; var undefined; // More compressible than void 0. var $Symbol = typeof Symbol === "function" ? Symbol : {}; var iteratorSymbol = $Symbol.iterator || "@@iterator"; @@ -83,26 +84,29 @@ function GeneratorFunction() {} function GeneratorFunctionPrototype() {} - // this is a polyfill for %IteratorPrototype% for environments that don't - // natively support it. + // This is a polyfill for %IteratorPrototype% for environments that + // don't natively support it. var IteratorPrototype = {}; - IteratorPrototype[iteratorSymbol] = function() {return this;}; + IteratorPrototype[iteratorSymbol] = function () { + return this; + }; + var getProto = Object.getPrototypeOf; - if (getProto) { - var NativeIteratorPrototype = getProto(getProto(values([]))); - if (NativeIteratorPrototype && - NativeIteratorPrototype !== Object.prototype && - NativeIteratorPrototype.hasOwnProperty(iteratorSymbol)) { - // this environment has a native %IteratorPrototype%; use it instead of - // a polyfill. - IteratorPrototype = NativeIteratorPrototype; - } + var NativeIteratorPrototype = getProto && getProto(getProto(values([]))); + if (NativeIteratorPrototype && + NativeIteratorPrototype !== Op && + hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) { + // This environment has a native %IteratorPrototype%; use it instead + // of the polyfill. + IteratorPrototype = NativeIteratorPrototype; } - var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); + var Gp = GeneratorFunctionPrototype.prototype = + Generator.prototype = Object.create(IteratorPrototype); GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; GeneratorFunctionPrototype.constructor = GeneratorFunction; - GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction"; + GeneratorFunctionPrototype[toStringTagSymbol] = + GeneratorFunction.displayName = "GeneratorFunction"; // Helper for defining the .next, .throw, and .return methods of the // Iterator interface in terms of a single ._invoke method.