From 9cad5486ce9379d41bae6606657e74567324c71d Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:00:00 +0200 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20Always=C2=A0use=20correct=C2=A0proto?= =?UTF-8?q?type=20for=C2=A0ordinary=C2=A0objects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proxy.min.js | 9 ++++---- src/proxy.js | 60 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/proxy.min.js b/proxy.min.js index 292e1d6..b2378ba 100644 --- a/proxy.min.js +++ b/proxy.min.js @@ -1,4 +1,5 @@ -(function(){function k(){function p(a){return a?"object"===typeof a||"function"===typeof a:!1}var l=null;var n=function(a,c){function g(){}if(!p(a)||!p(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");l=function(){a=null;g=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");}};setTimeout(function(){l=null},0);var f=c;c={get:null,set:null,apply:null,construct:null};for(var h in f){if(!(h in c))throw new TypeError("Proxy polyfill does not support trap '"+ -h+"'");c[h]=f[h]}"function"===typeof f&&(c.apply=f.apply.bind(f));var d=this,q=!1,r=!1;"function"===typeof a?(d=function(){var b=this&&this.constructor===d,e=Array.prototype.slice.call(arguments);g(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,e):!b&&c.apply?c.apply(a,this,e):b?(e.unshift(a),new (a.bind.apply(a,e))):a.apply(this,e)},q=!0):a instanceof Array&&(d=[],r=!0);var t=c.get?function(b){g("get");return c.get(this,b,d)}:function(b){g("get");return this[b]},w=c.set?function(b, -e){g("set");c.set(this,b,e,d)}:function(b,e){g("set");this[b]=e},u={};Object.getOwnPropertyNames(a).forEach(function(b){if(!((q||r)&&b in d)){var e={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable,get:t.bind(a,b),set:w.bind(a,b)};Object.defineProperty(d,b,e);u[b]=!0}});f=!0;Object.setPrototypeOf?Object.setPrototypeOf(d,Object.getPrototypeOf(a)):d.__proto__?d.__proto__=a.__proto__:f=!1;if(c.get||!f)for(var m in a)u[m]||Object.defineProperty(d,m,{get:t.bind(a,m)});Object.seal(a);Object.seal(d); -return d};n.revocable=function(a,c){return{proxy:new n(a,c),revoke:l}};return n};var v="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?global:self;v.Proxy||(v.Proxy=k(),v.Proxy.revocable=v.Proxy.revocable);})(); +(function(){function m(){function h(a){return a?"object"===typeof a||"function"===typeof a:!1}var n=null,p=Object,u=!0,z=p.create||((u=!({__proto__:null}instanceof p))?function(a){if(null!==a&&!h(a))throw new TypeError("Object prototype may only be an Object or null: "+a);return{__proto__:a}}:function(a){function c(){}if(null!==a&&!h(a))throw new TypeError("Object prototype may only be an Object or null: "+a);if(null===a)throw new SyntaxError("Native Object.create is required to create objects with null prototype"); +c.prototype=a;return new c}),v=p.getPrototypeOf||([].__proto__===Array.prototype?function(a){return a.__proto__}:null);var k=function(a,c){function g(){}if(void 0===(this&&this instanceof k?this.constructor:void 0))throw new TypeError("Constructor Proxy requires 'new'");if(!h(a)||!h(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");n=function(){a=null;g=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");}};setTimeout(function(){n= +null},0);var e=c;c={get:null,set:null,apply:null,construct:null};for(var l in e){if(!(l in c))throw new TypeError("Proxy polyfill does not support trap '"+l+"'");c[l]=e[l]}"function"===typeof e&&(c.apply=e.apply.bind(e));e=v?v(a):null;var d=null!==e||u?z(e):{},q=!1,r=!1;"function"===typeof a?(d=function(){var b=this&&this.constructor===d,f=Array.prototype.slice.call(arguments);g(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,f):!b&&c.apply?c.apply(a,this,f):b?(f.unshift(a),new (a.bind.apply(a, +f))):a.apply(this,f)},q=!0):a instanceof Array&&(d=[],r=!0);var w=c.get?function(b){g("get");return c.get(this,b,d)}:function(b){g("get");return this[b]},A=c.set?function(b,f){g("set");c.set(this,b,f,d)}:function(b,f){g("set");this[b]=f},x={};Object.getOwnPropertyNames(a).forEach(function(b){if(!((q||r)&&b in d)){var f={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable,get:w.bind(a,b),set:A.bind(a,b)};Object.defineProperty(d,b,f);x[b]=!0}});e=!0;if(q||r)Object.setPrototypeOf?Object.setPrototypeOf(d, +Object.getPrototypeOf(a)):d.__proto__?d.__proto__=a.__proto__:e=!1;if(c.get||!e)for(var t in a)x[t]||Object.defineProperty(d,t,{get:w.bind(a,t)});Object.seal(a);Object.seal(d);return d};k.revocable=function(a,c){return{proxy:new k(a,c),revoke:n}};return k};var y="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?global:self;y.Proxy||(y.Proxy=m(),y.Proxy.revocable=y.Proxy.revocable);})(); diff --git a/src/proxy.js b/src/proxy.js index 0316395..3277834 100644 --- a/src/proxy.js +++ b/src/proxy.js @@ -26,12 +26,53 @@ module.exports = function proxyPolyfill() { return o ? (typeof o === 'object' || typeof o === 'function') : false; } + const $Object = Object; + + let hasProto = true; + const objectCreate = + $Object.create || + ((hasProto = !({ __proto__: null } instanceof $Object)) + ? function create(proto) { + if (proto !== null && !isObject(proto)) { + throw new TypeError('Object prototype may only be an Object or null: ' + proto); + } + + return { __proto__: proto }; + } + : function create(proto) { + if (proto !== null && !isObject(proto)) { + throw new TypeError('Object prototype may only be an Object or null: ' + proto); + } + + if (proto === null) { + throw new SyntaxError('Native Object.create is required to create objects with null prototype'); + } + + // nb. cast to convince Closure compiler that this is a constructor + var T = /** @type {!Function} */ (function T() {}); + T.prototype = proto; + return new T(); + }); + + const getProto = + $Object.getPrototypeOf || + ([].__proto__ === Array.prototype + ? function getPrototypeOf(O) { + return O.__proto__; + } + : null); + /** * @constructor * @param {!Object} target * @param {{apply, construct, get, set}} handler */ ProxyPolyfill = function(target, handler) { + const newTarget = this && this instanceof ProxyPolyfill ? this.constructor : undefined; + if (newTarget === undefined) { + throw new TypeError("Constructor Proxy requires 'new'"); + } + if (!isObject(target) || !isObject(handler)) { throw new TypeError('Cannot create proxy with a non-object as target or handler'); } @@ -69,7 +110,8 @@ module.exports = function proxyPolyfill() { // Define proxy as this, or a Function (if either it's callable, or apply is set). // TODO(samthor): Closure compiler doesn't know about 'construct', attempts to rename it. - let proxy = this; + let proto = getProto ? getProto(target) : null; + let proxy = proto !== null || hasProto ? objectCreate(proto) : {}; let isMethod = false; let isArray = false; if (typeof target === 'function') { @@ -143,12 +185,14 @@ module.exports = function proxyPolyfill() { // TODO(samthor): We don't allow prototype methods to be set. It's (even more) awkward. // An alternative here would be to _just_ clone methods to keep behavior consistent. let prototypeOk = true; - if (Object.setPrototypeOf) { - Object.setPrototypeOf(proxy, Object.getPrototypeOf(target)); - } else if (proxy.__proto__) { - proxy.__proto__ = target.__proto__; - } else { - prototypeOk = false; + if (isMethod || isArray) { + if (Object.setPrototypeOf) { + Object.setPrototypeOf(proxy, Object.getPrototypeOf(target)); + } else if (proxy.__proto__) { + proxy.__proto__ = target.__proto__; + } else { + prototypeOk = false; + } } if (handler.get || !prototypeOk) { for (let k in target) { @@ -172,4 +216,4 @@ module.exports = function proxyPolyfill() { }; return ProxyPolyfill; -} \ No newline at end of file +} From 81771cfd7747749fc4c0c02caff855236c2bf365 Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Mon, 22 Jun 2020 12:30:00 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Address=C2=A0review=C2=A0comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proxy.min.js | 10 +++++----- src/proxy.js | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/proxy.min.js b/proxy.min.js index b2378ba..0c93791 100644 --- a/proxy.min.js +++ b/proxy.min.js @@ -1,5 +1,5 @@ -(function(){function m(){function h(a){return a?"object"===typeof a||"function"===typeof a:!1}var n=null,p=Object,u=!0,z=p.create||((u=!({__proto__:null}instanceof p))?function(a){if(null!==a&&!h(a))throw new TypeError("Object prototype may only be an Object or null: "+a);return{__proto__:a}}:function(a){function c(){}if(null!==a&&!h(a))throw new TypeError("Object prototype may only be an Object or null: "+a);if(null===a)throw new SyntaxError("Native Object.create is required to create objects with null prototype"); -c.prototype=a;return new c}),v=p.getPrototypeOf||([].__proto__===Array.prototype?function(a){return a.__proto__}:null);var k=function(a,c){function g(){}if(void 0===(this&&this instanceof k?this.constructor:void 0))throw new TypeError("Constructor Proxy requires 'new'");if(!h(a)||!h(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");n=function(){a=null;g=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");}};setTimeout(function(){n= -null},0);var e=c;c={get:null,set:null,apply:null,construct:null};for(var l in e){if(!(l in c))throw new TypeError("Proxy polyfill does not support trap '"+l+"'");c[l]=e[l]}"function"===typeof e&&(c.apply=e.apply.bind(e));e=v?v(a):null;var d=null!==e||u?z(e):{},q=!1,r=!1;"function"===typeof a?(d=function(){var b=this&&this.constructor===d,f=Array.prototype.slice.call(arguments);g(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,f):!b&&c.apply?c.apply(a,this,f):b?(f.unshift(a),new (a.bind.apply(a, -f))):a.apply(this,f)},q=!0):a instanceof Array&&(d=[],r=!0);var w=c.get?function(b){g("get");return c.get(this,b,d)}:function(b){g("get");return this[b]},A=c.set?function(b,f){g("set");c.set(this,b,f,d)}:function(b,f){g("set");this[b]=f},x={};Object.getOwnPropertyNames(a).forEach(function(b){if(!((q||r)&&b in d)){var f={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable,get:w.bind(a,b),set:A.bind(a,b)};Object.defineProperty(d,b,f);x[b]=!0}});e=!0;if(q||r)Object.setPrototypeOf?Object.setPrototypeOf(d, -Object.getPrototypeOf(a)):d.__proto__?d.__proto__=a.__proto__:e=!1;if(c.get||!e)for(var t in a)x[t]||Object.defineProperty(d,t,{get:w.bind(a,t)});Object.seal(a);Object.seal(d);return d};k.revocable=function(a,c){return{proxy:new k(a,c),revoke:n}};return k};var y="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?global:self;y.Proxy||(y.Proxy=m(),y.Proxy.revocable=y.Proxy.revocable);})(); +(function(){function m(){function k(a){return a?"object"===typeof a||"function"===typeof a:!1}var n=null,p=Object,u=!0,z=p.create||((u=!({__proto__:null}instanceof p))?function(a){if(null!==a&&!k(a))throw new TypeError("Object prototype may only be an Object or null: "+a);return{__proto__:a}}:function(a){function c(){}if(null!==a&&!k(a))throw new TypeError("Object prototype may only be an Object or null: "+a);if(null===a)throw new SyntaxError("Native Object.create is required to create objects with null prototype"); +c.prototype=a;return new c}),v=p.getPrototypeOf||([].__proto__===Array.prototype?function(a){return a.__proto__}:null);var l=function(a,c){function h(){}if(void 0===(this&&this instanceof l?this.constructor:void 0))throw new TypeError("Constructor Proxy requires 'new'");if(!k(a)||!k(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");n=function(){a=null;h=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");}};setTimeout(function(){n= +null},0);var f=c;c={get:null,set:null,apply:null,construct:null};for(var g in f){if(!(g in c))throw new TypeError("Proxy polyfill does not support trap '"+g+"'");c[g]=f[g]}"function"===typeof f&&(c.apply=f.apply.bind(f));f=v?v(a):null;var q=!1,r=!1;if("function"===typeof a){var d=function(){var b=this&&this.constructor===d,e=Array.prototype.slice.call(arguments);h(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,e):!b&&c.apply?c.apply(a,this,e):b?(e.unshift(a),new (a.bind.apply(a, +e))):a.apply(this,e)};q=!0}else a instanceof Array?(d=[],r=!0):d=null!==f||u?z(f):{};var w=c.get?function(b){h("get");return c.get(this,b,d)}:function(b){h("get");return this[b]},A=c.set?function(b,e){h("set");c.set(this,b,e,d)}:function(b,e){h("set");this[b]=e},x={};Object.getOwnPropertyNames(a).forEach(function(b){if(!((q||r)&&b in d)){var e={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable,get:w.bind(a,b),set:A.bind(a,b)};Object.defineProperty(d,b,e);x[b]=!0}});g=!0;if(q||r)Object.setPrototypeOf? +Object.setPrototypeOf(d,f):d.__proto__?d.__proto__=f:g=!1;if(c.get||!g)for(var t in a)x[t]||Object.defineProperty(d,t,{get:w.bind(a,t)});Object.seal(a);Object.seal(d);return d};l.revocable=function(a,c){return{proxy:new l(a,c),revoke:n}};return l};var y="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?global:self;y.Proxy||(y.Proxy=m(),y.Proxy.revocable=y.Proxy.revocable);})(); diff --git a/src/proxy.js b/src/proxy.js index 3277834..c26b90e 100644 --- a/src/proxy.js +++ b/src/proxy.js @@ -28,10 +28,10 @@ module.exports = function proxyPolyfill() { const $Object = Object; - let hasProto = true; + let canCreateNullProtoObjects = true; const objectCreate = $Object.create || - ((hasProto = !({ __proto__: null } instanceof $Object)) + ((canCreateNullProtoObjects = !({ __proto__: null } instanceof $Object)) ? function create(proto) { if (proto !== null && !isObject(proto)) { throw new TypeError('Object prototype may only be an Object or null: ' + proto); @@ -108,10 +108,10 @@ module.exports = function proxyPolyfill() { handler.apply = unsafeHandler.apply.bind(unsafeHandler); } - // Define proxy as this, or a Function (if either it's callable, or apply is set). - // TODO(samthor): Closure compiler doesn't know about 'construct', attempts to rename it. - let proto = getProto ? getProto(target) : null; - let proxy = proto !== null || hasProto ? objectCreate(proto) : {}; + // Define proxy as an object that extends target.[[Prototype]], + // or a Function (if either it's callable, or apply is set). + const proto = getProto ? getProto(target) : null; + let proxy; let isMethod = false; let isArray = false; if (typeof target === 'function') { @@ -120,6 +120,7 @@ module.exports = function proxyPolyfill() { const args = Array.prototype.slice.call(arguments); throwRevoked(usingNew ? 'construct' : 'apply'); + // TODO(samthor): Closure compiler doesn't know about 'construct', attempts to rename it. if (usingNew && handler['construct']) { return handler['construct'].call(this, target, args); } else if (!usingNew && handler.apply) { @@ -140,6 +141,8 @@ module.exports = function proxyPolyfill() { } else if (target instanceof Array) { proxy = []; isArray = true; + } else { + proxy = proto !== null || canCreateNullProtoObjects ? objectCreate(proto) : {}; } // Create default getters/setters. Create different code paths as handler.get/handler.set can't @@ -187,9 +190,9 @@ module.exports = function proxyPolyfill() { let prototypeOk = true; if (isMethod || isArray) { if (Object.setPrototypeOf) { - Object.setPrototypeOf(proxy, Object.getPrototypeOf(target)); + Object.setPrototypeOf(proxy, proto); } else if (proxy.__proto__) { - proxy.__proto__ = target.__proto__; + proxy.__proto__ = proto; } else { prototypeOk = false; } From f0a6cbbfe594be2d1d62117cd02d8fde18db76f9 Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Tue, 30 Jun 2020 17:30:00 +0200 Subject: [PATCH 3/3] =?UTF-8?q?Address=C2=A0review=C2=A0comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sam Thorogood --- proxy.min.js | 10 +++---- src/proxy.js | 74 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/proxy.min.js b/proxy.min.js index 0c93791..e1c3656 100644 --- a/proxy.min.js +++ b/proxy.min.js @@ -1,5 +1,5 @@ -(function(){function m(){function k(a){return a?"object"===typeof a||"function"===typeof a:!1}var n=null,p=Object,u=!0,z=p.create||((u=!({__proto__:null}instanceof p))?function(a){if(null!==a&&!k(a))throw new TypeError("Object prototype may only be an Object or null: "+a);return{__proto__:a}}:function(a){function c(){}if(null!==a&&!k(a))throw new TypeError("Object prototype may only be an Object or null: "+a);if(null===a)throw new SyntaxError("Native Object.create is required to create objects with null prototype"); -c.prototype=a;return new c}),v=p.getPrototypeOf||([].__proto__===Array.prototype?function(a){return a.__proto__}:null);var l=function(a,c){function h(){}if(void 0===(this&&this instanceof l?this.constructor:void 0))throw new TypeError("Constructor Proxy requires 'new'");if(!k(a)||!k(c))throw new TypeError("Cannot create proxy with a non-object as target or handler");n=function(){a=null;h=function(b){throw new TypeError("Cannot perform '"+b+"' on a proxy that has been revoked");}};setTimeout(function(){n= -null},0);var f=c;c={get:null,set:null,apply:null,construct:null};for(var g in f){if(!(g in c))throw new TypeError("Proxy polyfill does not support trap '"+g+"'");c[g]=f[g]}"function"===typeof f&&(c.apply=f.apply.bind(f));f=v?v(a):null;var q=!1,r=!1;if("function"===typeof a){var d=function(){var b=this&&this.constructor===d,e=Array.prototype.slice.call(arguments);h(b?"construct":"apply");return b&&c.construct?c.construct.call(this,a,e):!b&&c.apply?c.apply(a,this,e):b?(e.unshift(a),new (a.bind.apply(a, -e))):a.apply(this,e)};q=!0}else a instanceof Array?(d=[],r=!0):d=null!==f||u?z(f):{};var w=c.get?function(b){h("get");return c.get(this,b,d)}:function(b){h("get");return this[b]},A=c.set?function(b,e){h("set");c.set(this,b,e,d)}:function(b,e){h("set");this[b]=e},x={};Object.getOwnPropertyNames(a).forEach(function(b){if(!((q||r)&&b in d)){var e={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable,get:w.bind(a,b),set:A.bind(a,b)};Object.defineProperty(d,b,e);x[b]=!0}});g=!0;if(q||r)Object.setPrototypeOf? -Object.setPrototypeOf(d,f):d.__proto__?d.__proto__=f:g=!1;if(c.get||!g)for(var t in a)x[t]||Object.defineProperty(d,t,{get:w.bind(a,t)});Object.seal(a);Object.seal(d);return d};l.revocable=function(a,c){return{proxy:new l(a,c),revoke:n}};return l};var y="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?global:self;y.Proxy||(y.Proxy=m(),y.Proxy.revocable=y.Proxy.revocable);})(); +(function(){function n(){function l(a){return a?"object"===typeof a||"function"===typeof a:!1}function p(a){if(null!==a&&!l(a))throw new TypeError("Object prototype may only be an Object or null: "+a);}var q=null,d=Object,v=!!d.create||!({__proto__:null}instanceof d),B=d.create||(v?function(a){p(a);return{__proto__:a}}:function(a){function b(){}p(a);if(null===a)throw new SyntaxError("Native Object.create is required to create objects with null prototype");b.prototype=a;return new b}),w=d.getPrototypeOf||([].__proto__=== +Array.prototype?function(a){a=a.__proto__;return l(a)?a:null}:null),x=d.setPrototypeOf||([].__proto__===Array.prototype?function(a,b){p(b);a.__proto__=b;return a}:null);var m=function(a,b){function k(){}if(void 0===(this&&this instanceof m?this.constructor:void 0))throw new TypeError("Constructor Proxy requires 'new'");if(!l(a)||!l(b))throw new TypeError("Cannot create proxy with a non-object as target or handler");q=function(){a=null;k=function(c){throw new TypeError("Cannot perform '"+c+"' on a proxy that has been revoked"); +}};setTimeout(function(){q=null},0);var g=b;b={get:null,set:null,apply:null,construct:null};for(var h in g){if(!(h in b))throw new TypeError("Proxy polyfill does not support trap '"+h+"'");b[h]=g[h]}"function"===typeof g&&(b.apply=g.apply.bind(g));g=w?w(a):null;var r=!1,t=!1;if("function"===typeof a){var e=function(){var c=this&&this.constructor===e,f=Array.prototype.slice.call(arguments);k(c?"construct":"apply");return c&&b.construct?b.construct.call(this,a,f):!c&&b.apply?b.apply(a,this,f):c?(f.unshift(a), +new (a.bind.apply(a,f))):a.apply(this,f)};r=!0}else a instanceof Array?(e=[],t=!0):e=v||null!==g?B(g):{};var y=b.get?function(c){k("get");return b.get(this,c,e)}:function(c){k("get");return this[c]},C=b.set?function(c,f){k("set");b.set(this,c,f,e)}:function(c,f){k("set");this[c]=f},z={};d.getOwnPropertyNames(a).forEach(function(c){if(!((r||t)&&c in e)){var f={enumerable:!!d.getOwnPropertyDescriptor(a,c).enumerable,get:y.bind(a,c),set:C.bind(a,c)};d.defineProperty(e,c,f);z[c]=!0}});h=!0;if(r||t)x&& +void 0!==g?x(e,g):h=!1;if(b.get||!h)for(var u in a)z[u]||d.defineProperty(e,u,{get:y.bind(a,u)});d.seal(a);d.seal(e);return e};m.revocable=function(a,b){return{proxy:new m(a,b),revoke:q}};return m};var A="undefined"!==typeof process&&"[object process]"==={}.toString.call(process)||"undefined"!==typeof navigator&&"ReactNative"===navigator.product?global:self;A.Proxy||(A.Proxy=n(),A.Proxy.revocable=A.Proxy.revocable);})(); diff --git a/src/proxy.js b/src/proxy.js index c26b90e..819fe4e 100644 --- a/src/proxy.js +++ b/src/proxy.js @@ -26,39 +26,55 @@ module.exports = function proxyPolyfill() { return o ? (typeof o === 'object' || typeof o === 'function') : false; } + function validateProto(proto) { + if (proto !== null && !isObject(proto)) { + throw new TypeError('Object prototype may only be an Object or null: ' + proto); + } + } + const $Object = Object; - let canCreateNullProtoObjects = true; + // Closure assumes that `{__proto__: null} instanceof Object` is always true, hence why we check against a different name. + const canCreateNullProtoObjects = !!$Object.create || !({ __proto__: null } instanceof $Object); const objectCreate = $Object.create || - ((canCreateNullProtoObjects = !({ __proto__: null } instanceof $Object)) + (canCreateNullProtoObjects ? function create(proto) { - if (proto !== null && !isObject(proto)) { - throw new TypeError('Object prototype may only be an Object or null: ' + proto); - } - + validateProto(proto); return { __proto__: proto }; } : function create(proto) { - if (proto !== null && !isObject(proto)) { - throw new TypeError('Object prototype may only be an Object or null: ' + proto); - } - - if (proto === null) { - throw new SyntaxError('Native Object.create is required to create objects with null prototype'); - } + validateProto(proto); + if (proto === null) { + throw new SyntaxError('Native Object.create is required to create objects with null prototype'); + } - // nb. cast to convince Closure compiler that this is a constructor - var T = /** @type {!Function} */ (function T() {}); - T.prototype = proto; - return new T(); - }); + // nb. cast to convince Closure compiler that this is a constructor + var T = /** @type {!Function} */ (function T() {}); + T.prototype = proto; + return new T(); + }); const getProto = $Object.getPrototypeOf || ([].__proto__ === Array.prototype ? function getPrototypeOf(O) { - return O.__proto__; + // If O.[[Prototype]] === null, then the __proto__ accessor won't exist, + // as it's inherited from `Object.prototype` + const proto = O.__proto__; + return isObject(proto) ? proto : null; + } + : null); + + // Some old engines support Object.getPrototypeOf but not Object.setPrototypeOf, + // because Object.setPrototypeOf was standardized later. + const setProto = + $Object.setPrototypeOf || + ([].__proto__ === Array.prototype + ? function setPrototypeOf(O, proto) { + validateProto(proto); + O.__proto__ = proto; + return O; } : null); @@ -142,7 +158,7 @@ module.exports = function proxyPolyfill() { proxy = []; isArray = true; } else { - proxy = proto !== null || canCreateNullProtoObjects ? objectCreate(proto) : {}; + proxy = canCreateNullProtoObjects || proto !== null ? objectCreate(proto) : {}; } // Create default getters/setters. Create different code paths as handler.get/handler.set can't @@ -168,19 +184,19 @@ module.exports = function proxyPolyfill() { }; // Clone direct properties (i.e., not part of a prototype). - const propertyNames = Object.getOwnPropertyNames(target); + const propertyNames = $Object.getOwnPropertyNames(target); const propertyMap = {}; propertyNames.forEach(function(prop) { if ((isMethod || isArray) && prop in proxy) { return; // ignore properties already here, e.g. 'bind', 'prototype' etc } - const real = Object.getOwnPropertyDescriptor(target, prop); + const real = $Object.getOwnPropertyDescriptor(target, prop); const desc = { enumerable: !!real.enumerable, get: getter.bind(target, prop), set: setter.bind(target, prop), }; - Object.defineProperty(proxy, prop, desc); + $Object.defineProperty(proxy, prop, desc); propertyMap[prop] = true; }); @@ -189,10 +205,8 @@ module.exports = function proxyPolyfill() { // An alternative here would be to _just_ clone methods to keep behavior consistent. let prototypeOk = true; if (isMethod || isArray) { - if (Object.setPrototypeOf) { - Object.setPrototypeOf(proxy, proto); - } else if (proxy.__proto__) { - proxy.__proto__ = proto; + if (setProto && proto !== undefined) { + setProto(proxy, proto); } else { prototypeOk = false; } @@ -202,13 +216,13 @@ module.exports = function proxyPolyfill() { if (propertyMap[k]) { continue; } - Object.defineProperty(proxy, k, { get: getter.bind(target, k) }); + $Object.defineProperty(proxy, k, { get: getter.bind(target, k) }); } } // The Proxy polyfill cannot handle adding new properties. Seal the target and proxy. - Object.seal(target); - Object.seal(proxy); + $Object.seal(target); + $Object.seal(proxy); return proxy; // nb. if isMethod is true, proxy != this };