Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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