feat: track unused reflection data

This commit is contained in:
yjbanov 2015-08-19 14:07:30 -07:00 committed by Yegor Jbanov
parent b0d27ee896
commit 8336881a85
2 changed files with 57 additions and 4 deletions

View File

@ -4,6 +4,8 @@ import {
ListWrapper, ListWrapper,
Map, Map,
MapWrapper, MapWrapper,
Set,
SetWrapper,
StringMap, StringMap,
StringMapWrapper StringMapWrapper
} from 'angular2/src/facade/collection'; } from 'angular2/src/facade/collection';
@ -32,6 +34,7 @@ export class Reflector {
_getters: Map<string, GetterFn>; _getters: Map<string, GetterFn>;
_setters: Map<string, SetterFn>; _setters: Map<string, SetterFn>;
_methods: Map<string, MethodFn>; _methods: Map<string, MethodFn>;
_usedKeys: Set<any>;
reflectionCapabilities: PlatformReflectionCapabilities; reflectionCapabilities: PlatformReflectionCapabilities;
constructor(reflectionCapabilities: PlatformReflectionCapabilities) { constructor(reflectionCapabilities: PlatformReflectionCapabilities) {
@ -39,11 +42,31 @@ export class Reflector {
this._getters = new Map(); this._getters = new Map();
this._setters = new Map(); this._setters = new Map();
this._methods = new Map(); this._methods = new Map();
this._usedKeys = null;
this.reflectionCapabilities = reflectionCapabilities; this.reflectionCapabilities = reflectionCapabilities;
} }
isReflectionEnabled(): boolean { return this.reflectionCapabilities.isReflectionEnabled(); } isReflectionEnabled(): boolean { return this.reflectionCapabilities.isReflectionEnabled(); }
/**
* Causes `this` reflector to track keys used to access
* {@link ReflectionInfo} objects.
*/
trackUsage(): void { this._usedKeys = new Set(); }
/**
* Lists types for which reflection information was not requested since
* {@link #trackUsage} was called. This list could later be audited as
* potential dead code.
*/
listUnusedKeys(): List<any> {
if (this._usedKeys == null) {
throw new BaseException('Usage tracking is disabled');
}
var allTypes = MapWrapper.keys(this._injectableInfo);
return ListWrapper.filter(allTypes, (key) => { return !SetWrapper.has(this._usedKeys, key); });
}
registerFunction(func: Function, funcInfo: ReflectionInfo): void { registerFunction(func: Function, funcInfo: ReflectionInfo): void {
this._injectableInfo.set(func, funcInfo); this._injectableInfo.set(func, funcInfo);
} }
@ -66,7 +89,7 @@ export class Reflector {
factory(type: Type): Function { factory(type: Type): Function {
if (this._containsReflectionInfo(type)) { if (this._containsReflectionInfo(type)) {
var res = this._injectableInfo.get(type)._factory; var res = this._getReflectionInfo(type)._factory;
return isPresent(res) ? res : null; return isPresent(res) ? res : null;
} else { } else {
return this.reflectionCapabilities.factory(type); return this.reflectionCapabilities.factory(type);
@ -75,7 +98,7 @@ export class Reflector {
parameters(typeOrFunc: /*Type*/ any): List<any> { parameters(typeOrFunc: /*Type*/ any): List<any> {
if (this._injectableInfo.has(typeOrFunc)) { if (this._injectableInfo.has(typeOrFunc)) {
var res = this._injectableInfo.get(typeOrFunc)._parameters; var res = this._getReflectionInfo(typeOrFunc)._parameters;
return isPresent(res) ? res : []; return isPresent(res) ? res : [];
} else { } else {
return this.reflectionCapabilities.parameters(typeOrFunc); return this.reflectionCapabilities.parameters(typeOrFunc);
@ -84,7 +107,7 @@ export class Reflector {
annotations(typeOrFunc: /*Type*/ any): List<any> { annotations(typeOrFunc: /*Type*/ any): List<any> {
if (this._injectableInfo.has(typeOrFunc)) { if (this._injectableInfo.has(typeOrFunc)) {
var res = this._injectableInfo.get(typeOrFunc)._annotations; var res = this._getReflectionInfo(typeOrFunc)._annotations;
return isPresent(res) ? res : []; return isPresent(res) ? res : [];
} else { } else {
return this.reflectionCapabilities.annotations(typeOrFunc); return this.reflectionCapabilities.annotations(typeOrFunc);
@ -93,7 +116,7 @@ export class Reflector {
interfaces(type: Type): List<any> { interfaces(type: Type): List<any> {
if (this._injectableInfo.has(type)) { if (this._injectableInfo.has(type)) {
var res = this._injectableInfo.get(type)._interfaces; var res = this._getReflectionInfo(type)._interfaces;
return isPresent(res) ? res : []; return isPresent(res) ? res : [];
} else { } else {
return this.reflectionCapabilities.interfaces(type); return this.reflectionCapabilities.interfaces(type);
@ -124,6 +147,13 @@ export class Reflector {
} }
} }
_getReflectionInfo(typeOrFunc) {
if (isPresent(this._usedKeys)) {
this._usedKeys.add(typeOrFunc);
}
return this._injectableInfo.get(typeOrFunc);
}
_containsReflectionInfo(typeOrFunc) { return this._injectableInfo.has(typeOrFunc); } _containsReflectionInfo(typeOrFunc) { return this._injectableInfo.has(typeOrFunc); }
} }

View File

@ -48,6 +48,29 @@ export function main() {
beforeEach(() => { reflector = new Reflector(new ReflectionCapabilities()); }); beforeEach(() => { reflector = new Reflector(new ReflectionCapabilities()); });
describe("usage tracking", () => {
beforeEach(() => { reflector = new Reflector(null); });
it("should be disabled by default", () => {
expect(() => reflector.listUnusedKeys()).toThrowError('Usage tracking is disabled');
});
it("should report unused keys", () => {
reflector.trackUsage();
expect(reflector.listUnusedKeys()).toEqual([]);
reflector.registerType(AType, new ReflectionInfo(null, null, () => "AType"));
reflector.registerType(TestObj, new ReflectionInfo(null, null, () => "TestObj"));
expect(reflector.listUnusedKeys()).toEqual([AType, TestObj]);
reflector.factory(AType);
expect(reflector.listUnusedKeys()).toEqual([TestObj]);
reflector.factory(TestObj);
expect(reflector.listUnusedKeys()).toEqual([]);
});
});
describe("factory", () => { describe("factory", () => {
it("should create a factory for the given type", () => { it("should create a factory for the given type", () => {
var obj = reflector.factory(TestObj)(1, 2); var obj = reflector.factory(TestObj)(1, 2);