feat(Reflection): extract reflection capabilities into a separate module
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
name: reflection
|
||||
environment:
|
||||
sdk: '>=1.4.0'
|
||||
dependencies:
|
||||
facade:
|
||||
path: ../facade
|
||||
dev_dependencies:
|
||||
test_lib:
|
||||
path: ../test_lib
|
||||
guinness: ">=0.1.16 <0.2.0"
|
||||
@@ -0,0 +1,34 @@
|
||||
library reflection.reflection;
|
||||
|
||||
import 'reflector.dart';
|
||||
import 'types.dart';
|
||||
export 'reflector.dart';
|
||||
import 'package:facade/lang.dart';
|
||||
|
||||
class NoReflectionCapabilities {
|
||||
Function factory(Type type){
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
List parameters(Type type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
List annotations(Type type) {
|
||||
throw "Cannot find reflection information on ${stringify(type)}";
|
||||
}
|
||||
|
||||
GetterFn getter(String name) {
|
||||
throw "Cannot find getter ${name}";
|
||||
}
|
||||
|
||||
SetterFn setter(String name) {
|
||||
throw "Cannot find getter ${name}";
|
||||
}
|
||||
|
||||
MethodFn method(String name) {
|
||||
throw "Cannot find method ${name}";
|
||||
}
|
||||
}
|
||||
|
||||
final Reflector reflector = new Reflector(new NoReflectionCapabilities());
|
||||
@@ -0,0 +1,7 @@
|
||||
import {Type, isPresent} from 'facade/lang';
|
||||
import {List, ListWrapper} from 'facade/collection';
|
||||
import {Reflector} from './reflector';
|
||||
export {Reflector} from './reflector';;
|
||||
import {ReflectionCapabilities} from './reflection_capabilities';;
|
||||
|
||||
export var reflector = new Reflector(new ReflectionCapabilities());
|
||||
@@ -0,0 +1,89 @@
|
||||
library reflection.reflection_capabilities;
|
||||
|
||||
import 'reflection.dart';
|
||||
import 'types.dart';
|
||||
import 'dart:mirrors';
|
||||
|
||||
class ReflectionCapabilities {
|
||||
Function factory(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";
|
||||
}
|
||||
|
||||
List<List> parameters(typeOrFunc) {
|
||||
final parameters = typeOrFunc is Type ?
|
||||
_constructorParameters(typeOrFunc) :
|
||||
_functionParameters(typeOrFunc);
|
||||
return parameters.map(_convertParameter).toList();
|
||||
}
|
||||
|
||||
List _convertParameter(ParameterMirror p) {
|
||||
var t = p.type.reflectedType;
|
||||
var res = t == dynamic ? [] : [t];
|
||||
res.addAll(p.metadata.map((m) => m.reflectee));
|
||||
return res;
|
||||
}
|
||||
|
||||
List annotations(typeOrFunc) {
|
||||
final meta = typeOrFunc is Type ?
|
||||
_constructorMetadata(typeOrFunc) :
|
||||
_functionMetadata(typeOrFunc);
|
||||
|
||||
return meta.map((m) => m.reflectee).toList();
|
||||
}
|
||||
|
||||
GetterFn getter(String name) {
|
||||
var symbol = new Symbol(name);
|
||||
return (receiver) => reflect(receiver).getField(symbol).reflectee;
|
||||
}
|
||||
|
||||
SetterFn setter(String name) {
|
||||
var symbol = new Symbol(name);
|
||||
return (receiver, value) => reflect(receiver).setField(symbol, value).reflectee;
|
||||
}
|
||||
|
||||
MethodFn method(String name) {
|
||||
var symbol = new Symbol(name);
|
||||
return (receiver, posArgs) => reflect(receiver).invoke(symbol, posArgs).reflectee;
|
||||
}
|
||||
|
||||
List _functionParameters(Function func) {
|
||||
var closureMirror = reflect(func);
|
||||
return closureMirror.function.parameters;
|
||||
}
|
||||
|
||||
List _constructorParameters(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];
|
||||
return ctor.parameters;
|
||||
}
|
||||
|
||||
List _functionMetadata(Function func) {
|
||||
var closureMirror = reflect(func);
|
||||
return closureMirror.function.metadata;
|
||||
}
|
||||
|
||||
List _constructorMetadata(Type type) {
|
||||
ClassMirror classMirror = reflectType(type);
|
||||
return classMirror.metadata;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import {Type, isPresent} from 'facade/lang';
|
||||
import {List, ListWrapper} from 'facade/collection';
|
||||
import {GetterFn, SetterFn, MethodFn} from './types';
|
||||
|
||||
export class ReflectionCapabilities {
|
||||
factory(type:Type):Function {
|
||||
switch (type.length) {
|
||||
case 0:
|
||||
return function(){return new type();};
|
||||
case 1:
|
||||
return function(a1){return new type(a1);};
|
||||
case 2:
|
||||
return function(a1, a2){return new type(a1, a2);};
|
||||
case 3:
|
||||
return function(a1, a2, a3){return new type(a1, a2, a3);};
|
||||
case 4:
|
||||
return function(a1, a2, a3, a4){return new type(a1, a2, a3, a4);};
|
||||
case 5:
|
||||
return function(a1, a2, a3, a4, a5){return new type(a1, a2, a3, a4, a5);};
|
||||
case 6:
|
||||
return function(a1, a2, a3, a4, a5, a6){return new type(a1, a2, a3, a4, a5, a6);};
|
||||
case 7:
|
||||
return function(a1, a2, a3, a4, a5, a6, a7){return new type(a1, a2, a3, a4, a5, a6, a7);};
|
||||
case 8:
|
||||
return function(a1, a2, a3, a4, a5, a6, a7, a8){return new type(a1, a2, a3, a4, a5, a6, a7, a8);};
|
||||
case 9:
|
||||
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9){return new type(a1, a2, a3, a4, a5, a6, a7, a8, a9);};
|
||||
case 10:
|
||||
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10){return new type(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);};
|
||||
};
|
||||
|
||||
throw new Error("Factory cannot take more than 10 arguments");
|
||||
}
|
||||
|
||||
parameters(typeOfFunc):List<List> {
|
||||
return isPresent(typeOfFunc.parameters) ?
|
||||
typeOfFunc.parameters :
|
||||
ListWrapper.createFixedSize(typeOfFunc.length);
|
||||
}
|
||||
|
||||
annotations(typeOfFunc):List {
|
||||
return isPresent(typeOfFunc.annotations) ? typeOfFunc.annotations : [];
|
||||
}
|
||||
|
||||
getter(name:string):GetterFn {
|
||||
return new Function('o', 'return o.' + name + ';');
|
||||
}
|
||||
|
||||
setter(name:string):SetterFn {
|
||||
return new Function('o', 'v', 'return o.' + name + ' = v;');
|
||||
}
|
||||
|
||||
method(name:string):MethodFn {
|
||||
var method = `o.${name}`;
|
||||
return new Function('o', 'args',
|
||||
`if (!${method}) throw new Error('"${name}" is undefined');` +
|
||||
`return ${method}.apply(o, args);`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import {Type, isPresent, stringify, BaseException} from 'facade/lang';
|
||||
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'facade/collection';
|
||||
import {SetterFn, GetterFn, MethodFn} from './types';
|
||||
export {SetterFn, GetterFn, MethodFn} from './types';
|
||||
|
||||
export class Reflector {
|
||||
_typeInfo:Map;
|
||||
_getters:Map;
|
||||
_setters:Map;
|
||||
_methods:Map;
|
||||
reflectionCapabilities:any;
|
||||
|
||||
constructor(reflectionCapabilities) {
|
||||
this._typeInfo = MapWrapper.create();
|
||||
this._getters = MapWrapper.create();
|
||||
this._setters = MapWrapper.create();
|
||||
this._methods = MapWrapper.create();
|
||||
this.reflectionCapabilities = reflectionCapabilities;
|
||||
}
|
||||
|
||||
registerType(type, typeInfo){
|
||||
MapWrapper.set(this._typeInfo, type, typeInfo);
|
||||
}
|
||||
|
||||
registerGetters(getters){
|
||||
_mergeMaps(this._getters, getters);
|
||||
}
|
||||
|
||||
registerSetters(setters){
|
||||
_mergeMaps(this._setters, setters);
|
||||
}
|
||||
|
||||
registerMethods(methods){
|
||||
_mergeMaps(this._methods, methods);
|
||||
}
|
||||
|
||||
factory(type:Type):Function {
|
||||
if(MapWrapper.contains(this._typeInfo, type)) {
|
||||
return MapWrapper.get(this._typeInfo, type)["factory"];
|
||||
} else {
|
||||
return this.reflectionCapabilities.factory(type);
|
||||
}
|
||||
}
|
||||
|
||||
parameters(typeOfFunc):List {
|
||||
if(MapWrapper.contains(this._typeInfo, typeOfFunc)) {
|
||||
return MapWrapper.get(this._typeInfo, typeOfFunc)["parameters"];
|
||||
} else {
|
||||
return this.reflectionCapabilities.parameters(typeOfFunc);
|
||||
}
|
||||
}
|
||||
|
||||
annotations(typeOfFunc):List {
|
||||
if(MapWrapper.contains(this._typeInfo, typeOfFunc)) {
|
||||
return MapWrapper.get(this._typeInfo, typeOfFunc)["annotations"];
|
||||
} else {
|
||||
return this.reflectionCapabilities.annotations(typeOfFunc);
|
||||
}
|
||||
}
|
||||
|
||||
getter(name:string):GetterFn {
|
||||
if(MapWrapper.contains(this._getters, name)) {
|
||||
return MapWrapper.get(this._getters, name);
|
||||
} else {
|
||||
return this.reflectionCapabilities.getter(name);
|
||||
}
|
||||
}
|
||||
|
||||
setter(name:string):SetterFn {
|
||||
if(MapWrapper.contains(this._setters, name)) {
|
||||
return MapWrapper.get(this._setters, name);
|
||||
} else {
|
||||
return this.reflectionCapabilities.setter(name);
|
||||
}
|
||||
}
|
||||
|
||||
method(name:string):MethodFn {
|
||||
if(MapWrapper.contains(this._methods, name)) {
|
||||
return MapWrapper.get(this._methods, name);
|
||||
} else {
|
||||
return this.reflectionCapabilities.method(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _mergeMaps(target:Map, config) {
|
||||
StringMapWrapper.forEach(config, (v, k) => MapWrapper.set(target, k, v));
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
library reflection.types;
|
||||
|
||||
typedef SetterFn(Object obj, value);
|
||||
typedef GetterFn(Object obj);
|
||||
typedef MethodFn(Object obj, List args);
|
||||
@@ -0,0 +1,3 @@
|
||||
export var SetterFn = Function;
|
||||
export var GetterFn = Function;
|
||||
export var MethodFn = Function;
|
||||
@@ -0,0 +1,168 @@
|
||||
import {describe, it, iit, ddescribe, expect, beforeEach} from 'test_lib/test_lib';
|
||||
import {Reflector} from 'reflection/reflection';
|
||||
import {ReflectionCapabilities} from 'reflection/reflection_capabilities';
|
||||
import {CONST} from 'facade/lang';
|
||||
|
||||
class Annotation {
|
||||
value;
|
||||
|
||||
@CONST()
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
class AType {
|
||||
value;
|
||||
|
||||
constructor(value){
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Annotation('class')
|
||||
class ClassWithAnnotations {
|
||||
a;
|
||||
b;
|
||||
|
||||
constructor(@Annotation("a") a:AType, @Annotation("b") b:AType) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithoutAnnotations {
|
||||
constructor(a,b){}
|
||||
}
|
||||
|
||||
class TestObjWith11Args {
|
||||
constructor(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11) {
|
||||
}
|
||||
}
|
||||
|
||||
@Annotation('func')
|
||||
function testFunc(@Annotation("a") a:AType, @Annotation("b") b:AType) {
|
||||
}
|
||||
|
||||
class TestObj {
|
||||
a;
|
||||
b;
|
||||
|
||||
constructor(a, b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
identity(arg) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('Reflector', () => {
|
||||
var reflector;
|
||||
|
||||
beforeEach(() => {
|
||||
reflector = new Reflector(new ReflectionCapabilities());
|
||||
});
|
||||
|
||||
describe("factory", () => {
|
||||
it("should create a factory for the given type", () => {
|
||||
var obj = reflector.factory(TestObj)(1, 2);
|
||||
|
||||
expect(obj.a).toEqual(1);
|
||||
expect(obj.b).toEqual(2);
|
||||
});
|
||||
|
||||
it("should throw when more than 10 arguments", () => {
|
||||
expect(() => reflector.factory(TestObjWith11Args)(1,2,3,4,5,6,7,8,9,10,11)).toThrowError();
|
||||
});
|
||||
|
||||
it("should return a registered factory if available", () => {
|
||||
reflector.registerType(TestObj, {"factory" : () => "fake"});
|
||||
expect(reflector.factory(TestObj)()).toEqual("fake");
|
||||
});
|
||||
});
|
||||
|
||||
describe("parameters", () => {
|
||||
it("should return an array of parameters for a type", () => {
|
||||
var p = reflector.parameters(ClassWithAnnotations);
|
||||
expect(p).toEqual([[AType, new Annotation('a')], [AType, new Annotation('b')]]);
|
||||
});
|
||||
|
||||
it("should return an array of parameters for a function", () => {
|
||||
var p = reflector.parameters(testFunc);
|
||||
expect(p).toEqual([[AType, new Annotation('a')], [AType, new Annotation('b')]]);
|
||||
});
|
||||
|
||||
it("should work for a class without annotations", () => {
|
||||
var p = reflector.parameters(ClassWithoutAnnotations);
|
||||
expect(p.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("should return registered parameters if available", () => {
|
||||
reflector.registerType(TestObj, {"parameters" : [1,2]});
|
||||
expect(reflector.parameters(TestObj)).toEqual([1,2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("annotations", () => {
|
||||
it("should return an array of annotations for a type", () => {
|
||||
var p = reflector.annotations(ClassWithAnnotations);
|
||||
expect(p).toEqual([new Annotation('class')]);
|
||||
});
|
||||
|
||||
it("should return an array of annotations for a function", () => {
|
||||
var p = reflector.annotations(testFunc);
|
||||
expect(p).toEqual([new Annotation('func')]);
|
||||
});
|
||||
|
||||
it("should return registered annotations if available", () => {
|
||||
reflector.registerType(TestObj, {"annotations" : [1,2]});
|
||||
expect(reflector.annotations(TestObj)).toEqual([1,2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getter", () => {
|
||||
it("returns a function reading a property", () => {
|
||||
var getA = reflector.getter('a');
|
||||
expect(getA(new TestObj(1, 2))).toEqual(1);
|
||||
});
|
||||
|
||||
it("should return a registered getter if available", () => {
|
||||
reflector.registerGetters({"abc" : (obj) => "fake"});
|
||||
expect(reflector.getter("abc")("anything")).toEqual("fake");
|
||||
});
|
||||
});
|
||||
|
||||
describe("setter", () => {
|
||||
it("returns a function setting a property", () => {
|
||||
var setA = reflector.setter('a');
|
||||
var obj = new TestObj(1, 2);
|
||||
setA(obj, 100);
|
||||
expect(obj.a).toEqual(100);
|
||||
});
|
||||
|
||||
it("should return a registered setter if available", () => {
|
||||
var updateMe;
|
||||
reflector.registerSetters({"abc" : (obj, value) => {updateMe = value;}});
|
||||
reflector.setter("abc")("anything", "fake");
|
||||
|
||||
expect(updateMe).toEqual("fake");
|
||||
});
|
||||
});
|
||||
|
||||
describe("method", () => {
|
||||
it("returns a function invoking a method", () => {
|
||||
var func = reflector.method('identity');
|
||||
var obj = new TestObj(1, 2);
|
||||
expect(func(obj, ['value'])).toEqual('value');
|
||||
});
|
||||
|
||||
it("should return a registered method if available", () => {
|
||||
reflector.registerMethods({"abc" : (obj, args) => args});
|
||||
expect(reflector.method("abc")("anything", ["fake"])).toEqual(['fake']);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user