feat(Reflection): extract reflection capabilities into a separate module

This commit is contained in:
vsavkin
2014-11-20 12:07:48 -08:00
parent 044625a098
commit 6e8175a816
46 changed files with 637 additions and 416 deletions

View File

@ -1,7 +1,9 @@
import {FIELD, Type, isBlank} from 'facade/lang';
import {FIELD, Type, isBlank, isPresent} from 'facade/lang';
import {List, MapWrapper, ListWrapper} from 'facade/collection';
import {reflector} from './reflector';
import {reflector} from 'reflection/reflection';
import {Key} from './key';
import {Inject, InjectLazy, InjectPromise, DependencyAnnotation} from './annotations';
import {NoAnnotationError} from './exceptions';
export class Dependency {
key:Key;
@ -43,8 +45,8 @@ export class BindingBuilder {
toClass(type:Type):Binding {
return new Binding(
Key.get(this.token),
reflector.factoryFor(type),
reflector.dependencies(type),
reflector.factory(type),
_dependenciesFor(type),
false
);
}
@ -78,7 +80,49 @@ export class BindingBuilder {
_constructDependencies(factoryFunction:Function, dependencies:List) {
return isBlank(dependencies) ?
reflector.dependencies(factoryFunction) :
_dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => new Dependency(Key.get(t), false, false, []));
}
}
function _dependenciesFor(typeOrFunc):List {
var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return [];
if (ListWrapper.any(params, (p) => isBlank(p))) throw new NoAnnotationError(typeOrFunc);
return ListWrapper.map(params, (p) => _extractToken(typeOrFunc, p));
}
function _extractToken(typeOrFunc, annotations) {
var type;
var depProps = [];
for (var i = 0; i < annotations.length; ++i) {
var paramAnnotation = annotations[i];
if (paramAnnotation instanceof Type) {
type = paramAnnotation;
} else if (paramAnnotation instanceof Inject) {
return _createDependency(paramAnnotation.token, false, false, []);
} else if (paramAnnotation instanceof InjectPromise) {
return _createDependency(paramAnnotation.token, true, false, []);
} else if (paramAnnotation instanceof InjectLazy) {
return _createDependency(paramAnnotation.token, false, true, []);
} else if (paramAnnotation instanceof DependencyAnnotation) {
ListWrapper.push(depProps, paramAnnotation);
}
}
if (isPresent(type)) {
return _createDependency(type, false, false, depProps);
} else {
throw new NoAnnotationError(typeOrFunc);
}
}
function _createDependency(token, asPromise, lazy, depProps):Dependency {
return new Dependency(Key.get(token), asPromise, lazy, depProps);
}

View File

@ -2,10 +2,9 @@ import {Map, List, MapWrapper, ListWrapper} from 'facade/collection';
import {Binding, BindingBuilder, bind} from './binding';
import {ProviderError, NoProviderError, InvalidBindingError,
AsyncBindingError, CyclicDependencyError, InstantiationError} from './exceptions';
import {Type, isPresent, isBlank} from 'facade/lang';
import {FunctionWrapper, Type, isPresent, isBlank} from 'facade/lang';
import {Promise, PromiseWrapper} from 'facade/async';
import {Key} from './key';
import {reflector} from './reflector';
var _constructing = new Object();
@ -159,7 +158,7 @@ class _SyncInjectorStrategy {
_createInstance(key:Key, binding:Binding, deps:List) {
try {
var instance = reflector.invoke(binding.factory, deps);
var instance = FunctionWrapper.apply(binding.factory, deps);
this.injector._setInstance(key, instance);
return instance;
} catch (e) {
@ -221,7 +220,7 @@ class _AsyncInjectorStrategy {
try {
var instance = this.injector._getInstance(key);
if (!_isWaiting(instance)) return instance;
return reflector.invoke(binding.factory, deps);
return FunctionWrapper.apply(binding.factory, deps);
} catch (e) {
this.injector._clear(key);
throw new InstantiationError(e, key);

View File

@ -1,94 +0,0 @@
library facade.di.reflector;
import 'dart:mirrors';
import 'annotations.dart' show Inject, InjectPromise, InjectLazy, DependencyAnnotation;
import 'key.dart' show Key;
import 'binding.dart' show Dependency;
import 'exceptions.dart' show NoAnnotationError;
class Reflector {
Function factoryFor(Type type) {
ClassMirror classMirror = reflectType(type);
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
Function create = classMirror.newInstance;
Symbol name = ctor.constructorName;
int length = ctor.parameters.length;
switch (length) {
case 0: return () =>
create(name, []).reflectee;
case 1: return (a1) =>
create(name, [a1]).reflectee;
case 2: return (a1, a2) =>
create(name, [a1, a2]).reflectee;
case 3: return (a1, a2, a3) =>
create(name, [a1, a2, a3]).reflectee;
case 4: return (a1, a2, a3, a4) =>
create(name, [a1, a2, a3, a4]).reflectee;
case 5: return (a1, a2, a3, a4, a5) =>
create(name, [a1, a2, a3, a4, a5]).reflectee;
case 6: return (a1, a2, a3, a4, a5, a6) =>
create(name, [a1, a2, a3, a4, a5, a6]).reflectee;
case 7: return (a1, a2, a3, a4, a5, a6, a7) =>
create(name, [a1, a2, a3, a4, a5, a6, a7]).reflectee;
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) =>
create(name, [a1, a2, a3, a4, a5, a6, a7, a8]).reflectee;
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9]).reflectee;
case 10: return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) =>
create(name, [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]).reflectee;
};
throw "Factory cannot take more than 10 arguments";
}
invoke(Function factory, List args) {
return Function.apply(factory, args);
}
List<Dependency> dependencies(typeOrFunc) {
final parameters = typeOrFunc is Type ?
_constructorParameters(typeOrFunc) :
_functionParameters(typeOrFunc);
return new List.generate(parameters.length, (int pos) {
ParameterMirror p = parameters[pos];
final metadata = p.metadata.map((m) => m.reflectee);
var inject = metadata.firstWhere((m) => m is Inject, orElse: () => null);
var injectPromise = metadata.firstWhere((m) => m is InjectPromise, orElse: () => null);
var injectLazy = metadata.firstWhere((m) => m is InjectLazy, orElse: () => null);
if (inject != null) {
return new Dependency(Key.get(inject.token), false, false, []);
} else if (injectPromise != null) {
return new Dependency(Key.get(injectPromise.token), true, false, []);
} else if (injectLazy != null) {
return new Dependency(Key.get(injectLazy.token), false, true, []);
} else if (p.type.qualifiedName != #dynamic) {
var depProps = metadata.where((m) => m is DependencyAnnotation).toList();
return new Dependency(Key.get(p.type.reflectedType), false, false, depProps);
} else {
throw new NoAnnotationError(typeOrFunc);
}
}, growable:false);
}
List<ParameterMirror> _functionParameters(Function func) {
var closureMirror = reflect(func);
return closureMirror.function.parameters;
}
List<ParameterMirror> _constructorParameters(Type type) {
ClassMirror classMirror = reflectType(type);
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
return ctor.parameters;
}
}
final Reflector reflector = new Reflector();

View File

@ -1,84 +0,0 @@
import {Type, isPresent} from 'facade/lang';
import {List} from 'facade/collection';
import {Inject, InjectPromise, InjectLazy, DependencyAnnotation} from './annotations';
import {Key} from './key';
import {Dependency} from './binding';
import {NoAnnotationError} from './exceptions';
class Reflector {
factoryFor(type:Type):Function {
var length = type.parameters ? type.parameters.length : 0;
switch (length) {
case 0: return () =>
new type();
case 1: return (a1) =>
new type(a1);
case 2: return (a1, a2) =>
new type(a1, a2);
case 3: return (a1, a2, a3) =>
new type(a1, a2, a3);
case 4: return (a1, a2, a3, a4) =>
new type(a1, a2, a3, a4);
case 5: return (a1, a2, a3, a4, a5) =>
new type(a1, a2, a3, a4, a5);
case 6: return (a1, a2, a3, a4, a5, a6) =>
new type(a1, a2, a3, a4, a5, a6);
case 7: return (a1, a2, a3, a4, a5, a6, a7) =>
new type(a1, a2, a3, a4, a5, a6, a7);
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) =>
new type(a1, a2, a3, a4, a5, a6, a7, a8);
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
new type(a1, a2, a3, a4, a5, a6, a7, a8, a9);
case 10: return (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) =>
new type(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
};
throw "Factory cannot take more than 10 arguments";
}
invoke(factory:Function, args:List) {
return factory(...args);
}
dependencies(typeOrFunc):List {
var p = typeOrFunc.parameters;
if (p == undefined && typeOrFunc.length == 0) return [];
if (p == undefined) throw new NoAnnotationError(typeOrFunc);
return typeOrFunc.parameters.map((p) => this._extractToken(typeOrFunc, p));
}
_extractToken(typeOrFunc, annotations) {
var type;
var depProps = [];
for (var paramAnnotation of annotations) {
if (paramAnnotation instanceof Type) {
type = paramAnnotation;
} else if (paramAnnotation instanceof Inject) {
return this._createDependency(paramAnnotation.token, false, false, []);
} else if (paramAnnotation instanceof InjectPromise) {
return this._createDependency(paramAnnotation.token, true, false, []);
} else if (paramAnnotation instanceof InjectLazy) {
return this._createDependency(paramAnnotation.token, false, true, []);
} else if (paramAnnotation instanceof DependencyAnnotation) {
depProps.push(paramAnnotation);
}
}
if (isPresent(type)) {
return this._createDependency(type, false, false, depProps);
} else {
throw new NoAnnotationError(typeOrFunc);
}
}
_createDependency(token, asPromise, lazy, depProps):Dependency {
return new Dependency(Key.get(token), asPromise, lazy, depProps);
}
}
export var reflector:Reflector = new Reflector();

View File

@ -1,23 +0,0 @@
import {ddescribe, describe, it, iit, expect} from 'test_lib/test_lib';
import {Key, Inject, DependencyAnnotation} from 'di/di';
import {CONST} from 'facade/lang';
import {reflector, Token} from 'di/reflector';
class Parent extends DependencyAnnotation {
@CONST()
constructor() {
}
}
export function main() {
describe("reflector", function () {
describe("dependencies", function () {
it('should collect annotations implementing DependencyAnnotation as properties', function () {
function f(@Parent() arg:Function) {}
var dep = reflector.dependencies(f)[0];
expect(dep.properties[0]).toBeAnInstanceOf(Parent);
});
});
});
}