Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
fix(toString): fix #666, Zone patched method toString should like bef…
Browse files Browse the repository at this point in the history
…ore patched (#686)
  • Loading branch information
JiaLiPassion authored and mhevery committed Mar 22, 2017
1 parent e11d9ff commit 0d0ee53
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 2 deletions.
4 changes: 4 additions & 0 deletions lib/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {patchTimer} from '../common/timers';
import {patchFuncToString} from '../common/to-string';
import {findEventTask, patchClass, patchEventTargetMethods, patchMethod, patchPrototype, zoneSymbol} from '../common/utils';

import {propertyPatch} from './define-property';
Expand Down Expand Up @@ -151,6 +152,9 @@ if (_global['navigator'] && _global['navigator'].geolocation) {
patchPrototype(_global['navigator'].geolocation, ['getCurrentPosition', 'watchPosition']);
}

// patch Func.prototype.toString to let them look like native
patchFuncToString();

// handle unhandled promise rejection
function findPromiseRejectionHandler(evtName: string) {
return function(e: any) {
Expand Down
4 changes: 3 additions & 1 deletion lib/browser/register-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {isBrowser, isMix} from '../common/utils';
import {attachOriginToPatched, isBrowser, isMix} from '../common/utils';

import {_redefineProperty} from './define-property';

Expand Down Expand Up @@ -39,4 +39,6 @@ export function registerElementPatch(_global: any) {

return _registerElement.apply(document, [name, opts]);
};

attachOriginToPatched((<any>document).registerElement, _registerElement);
}
36 changes: 36 additions & 0 deletions lib/common/to-string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {zoneSymbol} from './utils';

// override Function.prototype.toString to make zone.js patched function
// look like native function
export function patchFuncToString() {
const originalFunctionToString = Function.prototype.toString;
const g: any =
typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global;
Function.prototype.toString = function() {
if (typeof this === 'function') {
if (this[zoneSymbol('OriginalDelegate')]) {
return originalFunctionToString.apply(this[zoneSymbol('OriginalDelegate')], arguments);
}
if (this === Promise) {
const nativePromise = g[zoneSymbol('Promise')];
if (nativePromise) {
return originalFunctionToString.apply(nativePromise, arguments);
}
}
if (this === Error) {
const nativeError = g[zoneSymbol('Error')];
if (nativeError) {
return originalFunctionToString.apply(nativeError, arguments);
}
}
}
return originalFunctionToString.apply(this, arguments);
};
}
18 changes: 17 additions & 1 deletion lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ export function patchPrototype(prototype: any, fnNames: string[]) {
const delegate = prototype[name];
if (delegate) {
prototype[name] = ((delegate: Function) => {
return function() {
const patched: any = function() {
return delegate.apply(this, bindArguments(<any>arguments, source + '.' + name));
};
attachOriginToPatched(patched, delegate);
return patched;
})(delegate);
}
}
Expand Down Expand Up @@ -401,6 +403,8 @@ const originalInstanceKey = zoneSymbol('originalInstance');
export function patchClass(className: string) {
const OriginalClass = _global[className];
if (!OriginalClass) return;
// keep original class in global
_global[zoneSymbol(className)] = OriginalClass;

_global[className] = function() {
const a = bindArguments(<any>arguments, className);
Expand All @@ -425,6 +429,9 @@ export function patchClass(className: string) {
}
};

// attach original delegate to patched function
attachOriginToPatched(_global[className], OriginalClass);

const instance = new OriginalClass(function() {});

let prop;
Expand All @@ -441,6 +448,10 @@ export function patchClass(className: string) {
set: function(fn) {
if (typeof fn === 'function') {
this[originalInstanceKey][prop] = Zone.current.wrap(fn, className + '.' + prop);
// keep callback in wrapped function so we can
// use it in Function.prototype.toString to return
// the native one.
attachOriginToPatched(this[originalInstanceKey][prop], fn);
} else {
this[originalInstanceKey][prop] = fn;
}
Expand Down Expand Up @@ -488,6 +499,7 @@ export function patchMethod(
if (proto && !(delegate = proto[delegateName])) {
delegate = proto[delegateName] = proto[name];
proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name));
attachOriginToPatched(proto[name], delegate);
}
return delegate;
}
Expand Down Expand Up @@ -575,5 +587,9 @@ export function findEventTask(target: any, evtName: string): Task[] {
return result;
}

export function attachOriginToPatched(patched: Function, original: any) {
(patched as any)[zoneSymbol('OriginalDelegate')] = original;
}

(Zone as any)[zoneSymbol('patchEventTargetMethods')] = patchEventTargetMethods;
(Zone as any)[zoneSymbol('patchOnProperties')] = patchOnProperties;
4 changes: 4 additions & 0 deletions lib/node/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import './events';
import './fs';

import {patchTimer} from '../common/timers';
import {patchFuncToString} from '../common/to-string';
import {findEventTask, patchMacroTask, patchMicroTask, zoneSymbol} from '../common/utils';

const set = 'set';
Expand All @@ -35,6 +36,9 @@ if (shouldPatchGlobalTimers) {
patchProcess();
handleUnhandledPromiseRejection();

// patch Function.prototyp.toString
patchFuncToString();

// Crypto
let crypto: any;
try {
Expand Down
32 changes: 32 additions & 0 deletions test/common/toString.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {zoneSymbol} from '../../lib/common/utils';
import {ifEnvSupports} from '../test-util';

const g: any =
typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global;
describe('global function patch', () => {
describe('isOriginal', () => {
it('setTimeout toString should be the same with non patched setTimeout', () => {
expect(Function.prototype.toString.call(setTimeout))
.toEqual(Function.prototype.toString.call(g[zoneSymbol('setTimeout')]));
});
});

describe('isNative', () => {
it('ZoneAwareError toString should look like native', () => {
expect(Function.prototype.toString.call(Error)).toContain('[native code]');
});

it('EventTarget addEventListener should look like native', ifEnvSupports('HTMLElement', () => {
expect(Function.prototype.toString.call(HTMLElement.prototype.addEventListener))
.toContain('[native code]');
}));
});
});
1 change: 1 addition & 0 deletions test/common_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import './common/Promise.spec';
import './common/Error.spec';
import './common/setInterval.spec';
import './common/setTimeout.spec';
import './common/toString.spec';
import './zone-spec/long-stack-trace-zone.spec';
import './zone-spec/async-test.spec';
import './zone-spec/sync-test.spec';
Expand Down

0 comments on commit 0d0ee53

Please sign in to comment.