From 8336881a852fb16afaa48fd67ae4f8f48cec9769 Mon Sep 17 00:00:00 2001 From: yjbanov Date: Wed, 19 Aug 2015 14:07:30 -0700 Subject: [PATCH] feat: track unused reflection data --- modules/angular2/src/reflection/reflector.ts | 38 +++++++++++++++++-- .../test/reflection/reflector_spec.ts | 23 +++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/modules/angular2/src/reflection/reflector.ts b/modules/angular2/src/reflection/reflector.ts index eb95e9298b..3fc12036ac 100644 --- a/modules/angular2/src/reflection/reflector.ts +++ b/modules/angular2/src/reflection/reflector.ts @@ -4,6 +4,8 @@ import { ListWrapper, Map, MapWrapper, + Set, + SetWrapper, StringMap, StringMapWrapper } from 'angular2/src/facade/collection'; @@ -32,6 +34,7 @@ export class Reflector { _getters: Map; _setters: Map; _methods: Map; + _usedKeys: Set; reflectionCapabilities: PlatformReflectionCapabilities; constructor(reflectionCapabilities: PlatformReflectionCapabilities) { @@ -39,11 +42,31 @@ export class Reflector { this._getters = new Map(); this._setters = new Map(); this._methods = new Map(); + this._usedKeys = null; this.reflectionCapabilities = reflectionCapabilities; } 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 { + 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 { this._injectableInfo.set(func, funcInfo); } @@ -66,7 +89,7 @@ export class Reflector { factory(type: Type): Function { if (this._containsReflectionInfo(type)) { - var res = this._injectableInfo.get(type)._factory; + var res = this._getReflectionInfo(type)._factory; return isPresent(res) ? res : null; } else { return this.reflectionCapabilities.factory(type); @@ -75,7 +98,7 @@ export class Reflector { parameters(typeOrFunc: /*Type*/ any): List { if (this._injectableInfo.has(typeOrFunc)) { - var res = this._injectableInfo.get(typeOrFunc)._parameters; + var res = this._getReflectionInfo(typeOrFunc)._parameters; return isPresent(res) ? res : []; } else { return this.reflectionCapabilities.parameters(typeOrFunc); @@ -84,7 +107,7 @@ export class Reflector { annotations(typeOrFunc: /*Type*/ any): List { if (this._injectableInfo.has(typeOrFunc)) { - var res = this._injectableInfo.get(typeOrFunc)._annotations; + var res = this._getReflectionInfo(typeOrFunc)._annotations; return isPresent(res) ? res : []; } else { return this.reflectionCapabilities.annotations(typeOrFunc); @@ -93,7 +116,7 @@ export class Reflector { interfaces(type: Type): List { if (this._injectableInfo.has(type)) { - var res = this._injectableInfo.get(type)._interfaces; + var res = this._getReflectionInfo(type)._interfaces; return isPresent(res) ? res : []; } else { 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); } } diff --git a/modules/angular2/test/reflection/reflector_spec.ts b/modules/angular2/test/reflection/reflector_spec.ts index c2839088f1..4649987f9b 100644 --- a/modules/angular2/test/reflection/reflector_spec.ts +++ b/modules/angular2/test/reflection/reflector_spec.ts @@ -48,6 +48,29 @@ export function main() { 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", () => { it("should create a factory for the given type", () => { var obj = reflector.factory(TestObj)(1, 2);