Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement mangle friendly MemberPrecondition #51

Merged
merged 1 commit into from
Dec 20, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ The library offers the following combinations of advices and joint points:
export interface MethodSelector {
classNamePattern?: RegExp;
methodNamePattern?: RegExp;
classes?: any[];
methods?: any[];
classes?: Function[];
methods?: Function[];
}
```

Expand All @@ -130,6 +130,8 @@ export interface MethodSelector {
export interface MemberSelector {
classNamePattern: RegExp;
fieldNamePattern: RegExp;
classes?: Function[];
methods?: PropertyDescriptor[];
}
```

Expand Down
3 changes: 1 addition & 2 deletions lib/src/joint_points/accessor_use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@ export class AccessorJointPoint extends JointPoint {
}

public match(target: Function): any[] {
const name = target.name;
const keys = Object.getOwnPropertyNames(target.prototype);
const res = keys
.map(key => {
const descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);
if (
this.precondition.assert({ className: name, fieldName: key }) &&
this.precondition.assert({ classDefinition: target, fieldName: key }) &&
(this.type === 'get' || (this.type === 'set' && typeof descriptor[this.type] === 'function'))
) {
return key;
Expand Down
4 changes: 2 additions & 2 deletions lib/src/joint_points/method_call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export class MethodCallJointPoint extends JointPoint {
let descriptor = Object.getOwnPropertyDescriptor(target.prototype, key);
if (
this.precondition.assert({
classInstance: target,
methodName: key,
classDefinition: target,
methodName: key
}) &&
typeof descriptor.value === 'function'
) {
Expand Down
53 changes: 37 additions & 16 deletions lib/src/joint_points/preconditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,56 @@ export class MethodPrecondition implements Precondition {
}
}

assert({ classInstance, methodName }: { classInstance: any; methodName: string }): boolean {
assert({ classDefinition, methodName }: { classDefinition: any; methodName: string }): boolean {
const s = this.selector;
const className = classInstance.name;

const matchAnyMethod = (methods: any[], target: any, methodName: string) => {
let keys = Object.getOwnPropertyNames(target.prototype);
return methods.some(f => {
return target.prototype[methodName] === f;
});
};
const className = classDefinition.name;

const matchClass =
(!s.classNamePattern && !s.classes) ||
(s.classNamePattern && s.classNamePattern.test(className)) ||
(s.classes && s.classes.some(c => c === classInstance));
(s.classes && s.classes.some(c => c === classDefinition));

if (!matchClass) {
return false;
}

const matchMember =
return !!(
(!s.methodNamePattern && !s.methods) ||
(s.methodNamePattern && s.methodNamePattern.test(methodName)) ||
(s.methods && matchAnyMethod(s.methods, classInstance, methodName));

return matchClass && matchMember;
(s.methods && s.methods.some(m => classDefinition.prototype[methodName] === m))
);
}
}

export class MemberPrecondition implements Precondition {
constructor(private selector: MemberSelector) {}

assert({ className, fieldName }: { fieldName: string; className: string }): boolean {
return this.selector.classNamePattern.test(className) && this.selector.fieldNamePattern.test(fieldName);
assert({ classDefinition, fieldName }: { classDefinition: any; fieldName: string }): boolean {
const s = this.selector;
const className = classDefinition.name;

const matchClass =
(!s.classNamePattern && !s.classes) ||
(s.classNamePattern && s.classNamePattern.test(className)) ||
(s.classes && s.classes.some(c => c === classDefinition));

if (!matchClass) {
return false;
}

const d = Object.getOwnPropertyDescriptor(classDefinition.prototype, fieldName);
return !!(
(!s.fieldNamePattern && !s.fields) ||
(s.fieldNamePattern && s.fieldNamePattern.test(fieldName)) ||
(s.fields &&
s.fields.some(f => {
if (!f) {
throw new Error(
'Got invalid property descriptor for a member selector. Use Object.getOwnPropertyDescriptor(fn.prototype, name) if you are using field selectors.'
);
}
return d && (d.get === f.get && d.set === f.set);
}))
);
}
}
10 changes: 6 additions & 4 deletions lib/src/joint_points/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export interface MethodSelector {
classNamePattern?: RegExp;
methodNamePattern?: RegExp;
classes?: any[];
methods?: any[];
classes?: Function[];
methods?: Function[];
}

export interface MemberSelector {
classNamePattern: RegExp;
fieldNamePattern: RegExp;
classNamePattern?: RegExp;
fieldNamePattern?: RegExp;
classes?: Function[];
fields?: PropertyDescriptor[];
}
4 changes: 2 additions & 2 deletions lib/src/joint_points/static_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export class StaticMethodJointPoint extends JointPoint {
const descriptor = Object.getOwnPropertyDescriptor(target, key);
return (
this.precondition.assert({
classInstance: target,
methodName: key,
classDefinition: target,
methodName: key
}) && typeof descriptor.value === 'function'
);
});
Expand Down
104 changes: 104 additions & 0 deletions test/core/preconditions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { MemberSelector } from './../../lib/src/joint_points/selectors';
import { MemberPrecondition } from './../../lib/src/joint_points/preconditions';

import { expect } from 'chai';

describe('Preconditions', () => {
describe('MemberPrecondition', () => {
it('should match by regex', () => {
class Foo {
get bar(): any {
return null;
}
get baz(): any {
return null;
}
set foobar(v: any) {
// empty
}
}

const selector: MemberSelector = {
classNamePattern: /Foo/,
fieldNamePattern: /bar/
};
const p = new MemberPrecondition(selector);
expect(p.assert({ classDefinition: Foo, fieldName: 'bar' })).equal(true);
expect(p.assert({ classDefinition: Foo, fieldName: 'baz' })).equal(false);
});

it('should match by value', () => {
class Foo {
get bar(): any {
return undefined;
}
get baz(): any {
return null;
}
set foobar(v: any) {
// empty
}
}

const selector: MemberSelector = {
classes: [Foo],
fields: [Object.getOwnPropertyDescriptor(Foo.prototype, 'baz')]
};
const p = new MemberPrecondition(selector);
// expect(p.assert({ classDefinition: Foo, fieldName: 'baz' })).equal(true);
expect(p.assert({ classDefinition: Foo, fieldName: 'bar' })).equal(false);
});

it('should throw with invalid selector', () => {
class Foo {
get bar(): any {
return null;
}
get baz(): any {
return null;
}
set foobar(v: any) {
// empty
}
}

const selector: MemberSelector = {
classes: [Foo],
fields: [Foo.prototype.foobar]
};
const p = new MemberPrecondition(selector);
expect(() => {
p.assert({ classDefinition: Foo, fieldName: 'baz' });
}).to.throw();
});

it('should match by value & regex', () => {
class Foo {
get bar(): any {
return null;
}
get baz(): any {
return null;
}
set foobar(v: any) {
// empty
}
}

const p1 = new MemberPrecondition({
classNamePattern: /Foo/,
fields: [Object.getOwnPropertyDescriptor(Foo.prototype, 'baz')]
});
expect(p1.assert({ classDefinition: Foo, fieldName: 'bar' })).equal(false);
expect(p1.assert({ classDefinition: Foo, fieldName: 'baz' })).equal(true);

const p2 = new MemberPrecondition({
classes: [Foo],
fieldNamePattern: /bar/
});
expect(p2.assert({ classDefinition: Foo, fieldName: 'bar' })).equal(true);
expect(p2.assert({ classDefinition: Foo, fieldName: 'foobar' })).equal(true);
expect(p2.assert({ classDefinition: Foo, fieldName: 'baz' })).equal(false);
});
});
});
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"test/advices/sync_advices.spec.ts",
"test/advices/async_advices.spec.ts",
"test/core/pointcut.spec.ts",
"test/core/preconditions.spec.ts",
"demo/index.ts"
],
"angularCompilerOptions": {
Expand Down