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

Commit

Permalink
fix: readonly property should not be patched (#860)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion authored and mhevery committed Aug 4, 2017
1 parent deae751 commit 7fbd655
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 1 deletion.
20 changes: 20 additions & 0 deletions lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export function patchPrototype(prototype: any, fnNames: string[]) {
const name = fnNames[i];
const delegate = prototype[name];
if (delegate) {
const prototypeDesc = Object.getOwnPropertyDescriptor(prototype, name);
if (!isPropertyWritable(prototypeDesc)) {
continue;
}
prototype[name] = ((delegate: Function) => {
const patched: any = function() {
return delegate.apply(this, bindArguments(<any>arguments, source + '.' + name));
Expand All @@ -44,6 +48,22 @@ export function patchPrototype(prototype: any, fnNames: string[]) {
}
}

export function isPropertyWritable(propertyDesc: any) {
if (!propertyDesc) {
return true;
}

if (propertyDesc.writable === false) {
return false;
}

if (typeof propertyDesc.get === 'function' && typeof propertyDesc.set === 'undefined') {
return false;
}

return true;
}

export const isWebWorker: boolean =
(typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope);

Expand Down
165 changes: 164 additions & 1 deletion test/common/util.spec.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 {patchMethod, patchProperty, zoneSymbol} from '../../lib/common/utils';
import {patchMethod, patchProperty, patchPrototype, zoneSymbol} from '../../lib/common/utils';

describe('utils', function() {

Expand Down Expand Up @@ -79,4 +79,167 @@ describe('utils', function() {
expect(!desc.get).toBeTruthy();
});
});

describe('patchPrototype', () => {
it('non configurable property desc should be patched', () => {
'use strict';
const TestFunction: any = function() {};
const log: string[] = [];
Object.defineProperties(TestFunction.prototype, {
'property1': {
value: function Property1(callback: Function) {
Zone.root.run(callback);
},
writable: true,
configurable: true,
enumerable: true
},
'property2': {
value: function Property2(callback: Function) {
Zone.root.run(callback);
},
writable: true,
configurable: false,
enumerable: true
}
});

const zone = Zone.current.fork({name: 'patch'});

zone.run(() => {
const instance = new TestFunction();
instance.property1(() => {
log.push('property1' + Zone.current.name);
});
instance.property2(() => {
log.push('property2' + Zone.current.name);
});
});
expect(log).toEqual(['property1<root>', 'property2<root>']);
log.length = 0;

patchPrototype(TestFunction.prototype, ['property1', 'property2']);

zone.run(() => {
const instance = new TestFunction();
instance.property1(() => {
log.push('property1' + Zone.current.name);
});
instance.property2(() => {
log.push('property2' + Zone.current.name);
});
});
expect(log).toEqual(['property1patch', 'property2patch']);
});

it('non writable property desc should not be patched', () => {
'use strict';
const TestFunction: any = function() {};
const log: string[] = [];
Object.defineProperties(TestFunction.prototype, {
'property1': {
value: function Property1(callback: Function) {
Zone.root.run(callback);
},
writable: true,
configurable: true,
enumerable: true
},
'property2': {
value: function Property2(callback: Function) {
Zone.root.run(callback);
},
writable: false,
configurable: true,
enumerable: true
}
});

const zone = Zone.current.fork({name: 'patch'});

zone.run(() => {
const instance = new TestFunction();
instance.property1(() => {
log.push('property1' + Zone.current.name);
});
instance.property2(() => {
log.push('property2' + Zone.current.name);
});
});
expect(log).toEqual(['property1<root>', 'property2<root>']);
log.length = 0;

patchPrototype(TestFunction.prototype, ['property1', 'property2']);

zone.run(() => {
const instance = new TestFunction();
instance.property1(() => {
log.push('property1' + Zone.current.name);
});
instance.property2(() => {
log.push('property2' + Zone.current.name);
});
});
expect(log).toEqual(['property1patch', 'property2<root>']);
});

it('readonly property desc should not be patched', () => {
'use strict';
const TestFunction: any = function() {};
const log: string[] = [];
Object.defineProperties(TestFunction.prototype, {
'property1': {
get: function() {
if (!this._property1) {
this._property1 = function Property2(callback: Function) {
Zone.root.run(callback);
};
}
return this._property1;
},
set: function(func: Function) {
this._property1 = func;
},
configurable: true,
enumerable: true
},
'property2': {
get: function() {
return function Property2(callback: Function) {
Zone.root.run(callback);
};
},
configurable: true,
enumerable: true
}
});

const zone = Zone.current.fork({name: 'patch'});

zone.run(() => {
const instance = new TestFunction();
instance.property1(() => {
log.push('property1' + Zone.current.name);
});
instance.property2(() => {
log.push('property2' + Zone.current.name);
});
});
expect(log).toEqual(['property1<root>', 'property2<root>']);
log.length = 0;

patchPrototype(TestFunction.prototype, ['property1', 'property2']);

zone.run(() => {
const instance = new TestFunction();
instance.property1(() => {
log.push('property1' + Zone.current.name);
});
instance.property2(() => {
log.push('property2' + Zone.current.name);
});
});
expect(log).toEqual(['property1patch', 'property2<root>']);
});
});
});

0 comments on commit 7fbd655

Please sign in to comment.