Stack overflow
This commit is contained in:
parent
24b03ce71c
commit
a570537bac
6 changed files with 165 additions and 24 deletions
|
@ -16,13 +16,17 @@ class Container {
|
||||||
var named = <String, dynamic>{};
|
var named = <String, dynamic>{};
|
||||||
|
|
||||||
if (reflectedType is ReflectedClass) {
|
if (reflectedType is ReflectedClass) {
|
||||||
|
bool isDefault(String name) {
|
||||||
|
return name.isEmpty || name == reflectedType.name;
|
||||||
|
}
|
||||||
|
|
||||||
var constructor = reflectedType.constructors.firstWhere(
|
var constructor = reflectedType.constructors.firstWhere(
|
||||||
(c) => c.name.isEmpty,
|
(c) => isDefault(c.name),
|
||||||
orElse: () => throw new ReflectionException('${reflectedType
|
orElse: () => throw new ReflectionException(
|
||||||
.name} has no default constructor, and therefore cannot be instantiated.'));
|
'${reflectedType.name} has no default constructor, and therefore cannot be instantiated.'));
|
||||||
|
|
||||||
for (var param in constructor.parameters) {
|
for (var param in constructor.parameters) {
|
||||||
var value = make(param.type);
|
var value = make(param.type.reflectedType);
|
||||||
|
|
||||||
if (param.isNamed) {
|
if (param.isNamed) {
|
||||||
named[param.name] = value;
|
named[param.name] = value;
|
||||||
|
@ -31,8 +35,10 @@ class Container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflectedType
|
return reflectedType.newInstance(
|
||||||
.newInstance(constructor.name, positional, named, []);
|
isDefault(constructor.name) ? '' : constructor.name,
|
||||||
|
positional,
|
||||||
|
named, []);
|
||||||
} else {
|
} else {
|
||||||
throw new ReflectionException(
|
throw new ReflectionException(
|
||||||
'$type is not a class, and therefore cannot be instantiated.');
|
'$type is not a class, and therefore cannot be instantiated.');
|
||||||
|
@ -42,8 +48,8 @@ class Container {
|
||||||
|
|
||||||
void singleton(Object object, {Type as}) {
|
void singleton(Object object, {Type as}) {
|
||||||
if (_singletons.containsKey(as ?? object.runtimeType)) {
|
if (_singletons.containsKey(as ?? object.runtimeType)) {
|
||||||
throw new StateError('This container already has a singleton for ${as ??
|
throw new StateError(
|
||||||
object.runtimeType}.');
|
'This container already has a singleton for ${as ?? object.runtimeType}.');
|
||||||
}
|
}
|
||||||
|
|
||||||
_singletons[as ?? object.runtimeType] = object;
|
_singletons[as ?? object.runtimeType] = object;
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import 'dart:mirrors' as dart;
|
import 'dart:mirrors' as dart;
|
||||||
|
|
||||||
import 'package:angel_container/angel_container.dart';
|
import 'package:angel_container/angel_container.dart';
|
||||||
import 'package:angel_container/src/reflector.dart';
|
import 'package:angel_container/src/reflector.dart';
|
||||||
import 'package:angel_container/src/reflector.dart';
|
|
||||||
|
|
||||||
|
/// A [Reflector] implementation that forwards to `dart:mirrors`.
|
||||||
|
///
|
||||||
|
/// Useful on the server, where reflection is supported.
|
||||||
class MirrorsReflector implements Reflector {
|
class MirrorsReflector implements Reflector {
|
||||||
|
const MirrorsReflector();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getName(Symbol symbol) => dart.MirrorSystem.getName(symbol);
|
String getName(Symbol symbol) => dart.MirrorSystem.getName(symbol);
|
||||||
|
|
||||||
|
@ -20,7 +25,8 @@ class MirrorsReflector implements Reflector {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ReflectedFunction reflectFunction(Function function) {
|
ReflectedFunction reflectFunction(Function function) {
|
||||||
// TODO: implement reflectFunction
|
var closure = dart.reflect(function) as dart.ClosureMirror;
|
||||||
|
return new _ReflectedMethodMirror(closure.function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -33,6 +39,11 @@ class MirrorsReflector implements Reflector {
|
||||||
return new _ReflectedTypeMirror(mirror);
|
return new _ReflectedTypeMirror(mirror);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ReflectedInstance reflectInstance(Object object) {
|
||||||
|
return new _ReflectedInstanceMirror(object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ReflectedTypeParameter extends ReflectedTypeParameter {
|
class _ReflectedTypeParameter extends ReflectedTypeParameter {
|
||||||
|
@ -52,6 +63,7 @@ class _ReflectedTypeMirror extends ReflectedType {
|
||||||
mirror.typeVariables
|
mirror.typeVariables
|
||||||
.map((m) => new _ReflectedTypeParameter(m))
|
.map((m) => new _ReflectedTypeParameter(m))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
mirror.reflectedType,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -61,7 +73,7 @@ class _ReflectedTypeMirror extends ReflectedType {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
T newInstance<T>(String constructorName, List positionalArguments,
|
T newInstance<T>(String constructorName, List positionalArguments,
|
||||||
Map<String, dynamic> namedArguments, List<Type> typeArguments) {
|
[Map<String, dynamic> namedArguments, List<Type> typeArguments]) {
|
||||||
throw new ReflectionException(
|
throw new ReflectionException(
|
||||||
'$name is not a class, and therefore cannot be instantiated.');
|
'$name is not a class, and therefore cannot be instantiated.');
|
||||||
}
|
}
|
||||||
|
@ -79,6 +91,7 @@ class _ReflectedClassMirror extends ReflectedClass {
|
||||||
mirror.metadata.map((m) => new _ReflectedInstanceMirror(m)).toList(),
|
mirror.metadata.map((m) => new _ReflectedInstanceMirror(m)).toList(),
|
||||||
_constructorsOf(mirror),
|
_constructorsOf(mirror),
|
||||||
_declarationsOf(mirror),
|
_declarationsOf(mirror),
|
||||||
|
mirror.reflectedType,
|
||||||
);
|
);
|
||||||
|
|
||||||
static List<ReflectedFunction> _constructorsOf(dart.ClassMirror mirror) {
|
static List<ReflectedFunction> _constructorsOf(dart.ClassMirror mirror) {
|
||||||
|
@ -86,6 +99,10 @@ class _ReflectedClassMirror extends ReflectedClass {
|
||||||
|
|
||||||
for (var key in mirror.declarations.keys) {
|
for (var key in mirror.declarations.keys) {
|
||||||
var value = mirror.declarations[key];
|
var value = mirror.declarations[key];
|
||||||
|
|
||||||
|
if (value is dart.MethodMirror && value.isConstructor) {
|
||||||
|
out.add(new _ReflectedMethodMirror(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -96,6 +113,11 @@ class _ReflectedClassMirror extends ReflectedClass {
|
||||||
|
|
||||||
for (var key in mirror.declarations.keys) {
|
for (var key in mirror.declarations.keys) {
|
||||||
var value = mirror.declarations[key];
|
var value = mirror.declarations[key];
|
||||||
|
|
||||||
|
if (value is dart.MethodMirror && !value.isConstructor) {
|
||||||
|
out.add(new ReflectedDeclaration(dart.MirrorSystem.getName(key),
|
||||||
|
value.isStatic, new _ReflectedMethodMirror(value)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -108,8 +130,10 @@ class _ReflectedClassMirror extends ReflectedClass {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
T newInstance<T>(String constructorName, List positionalArguments,
|
T newInstance<T>(String constructorName, List positionalArguments,
|
||||||
Map<String, dynamic> namedArguments, List<Type> typeArguments) {
|
[Map<String, dynamic> namedArguments, List<Type> typeArguments]) {
|
||||||
// TODO: implement newInstance
|
return mirror
|
||||||
|
.newInstance(new Symbol(constructorName), positionalArguments)
|
||||||
|
.reflectee as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,3 +149,31 @@ class _ReflectedInstanceMirror extends ReflectedInstance {
|
||||||
return mirror.delegate(invocation) as T;
|
return mirror.delegate(invocation) as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ReflectedMethodMirror extends ReflectedFunction {
|
||||||
|
final dart.MethodMirror mirror;
|
||||||
|
|
||||||
|
_ReflectedMethodMirror(this.mirror)
|
||||||
|
: super(
|
||||||
|
dart.MirrorSystem.getName(mirror.simpleName),
|
||||||
|
<ReflectedTypeParameter>[],
|
||||||
|
mirror.metadata
|
||||||
|
.map((mirror) => new _ReflectedInstanceMirror(mirror))
|
||||||
|
.toList(),
|
||||||
|
const MirrorsReflector()
|
||||||
|
.reflectType(mirror.returnType.reflectedType),
|
||||||
|
mirror.parameters.map(_reflectParameter).toList(),
|
||||||
|
mirror.isGetter,
|
||||||
|
mirror.isSetter);
|
||||||
|
|
||||||
|
static ReflectedParameter _reflectParameter(dart.ParameterMirror mirror) {
|
||||||
|
return new ReflectedParameter(
|
||||||
|
dart.MirrorSystem.getName(mirror.simpleName),
|
||||||
|
mirror.metadata
|
||||||
|
.map((mirror) => new _ReflectedInstanceMirror(mirror))
|
||||||
|
.toList(),
|
||||||
|
const MirrorsReflector().reflectType(mirror.type.reflectedType),
|
||||||
|
!mirror.isOptional,
|
||||||
|
mirror.isNamed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:quiver_hashcode/hashcode.dart';
|
import 'package:quiver/core.dart';
|
||||||
|
|
||||||
abstract class Reflector {
|
abstract class Reflector {
|
||||||
String getName(Symbol symbol);
|
String getName(Symbol symbol);
|
||||||
|
@ -9,6 +9,8 @@ abstract class Reflector {
|
||||||
ReflectedFunction reflectFunction(Function function);
|
ReflectedFunction reflectFunction(Function function);
|
||||||
|
|
||||||
ReflectedType reflectType(Type type);
|
ReflectedType reflectType(Type type);
|
||||||
|
|
||||||
|
ReflectedInstance reflectInstance(Object object);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ReflectedInstance {
|
abstract class ReflectedInstance {
|
||||||
|
@ -30,21 +32,23 @@ abstract class ReflectedInstance {
|
||||||
abstract class ReflectedType {
|
abstract class ReflectedType {
|
||||||
final String name;
|
final String name;
|
||||||
final List<ReflectedTypeParameter> typeParameters;
|
final List<ReflectedTypeParameter> typeParameters;
|
||||||
|
final Type reflectedType;
|
||||||
|
|
||||||
const ReflectedType(this.name, this.typeParameters);
|
const ReflectedType(this.name, this.typeParameters, this.reflectedType);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hash2(name, typeParameters);
|
int get hashCode => hash3(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) =>
|
bool operator ==(other) =>
|
||||||
other is ReflectedType &&
|
other is ReflectedType &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
const ListEquality<ReflectedTypeParameter>()
|
const ListEquality<ReflectedTypeParameter>()
|
||||||
.equals(other.typeParameters, typeParameters);
|
.equals(other.typeParameters, typeParameters) &&
|
||||||
|
other.reflectedType == reflectedType;
|
||||||
|
|
||||||
T newInstance<T>(String constructorName, List positionalArguments,
|
T newInstance<T>(String constructorName, List positionalArguments,
|
||||||
Map<String, dynamic> namedArguments, List<Type> typeArguments);
|
[Map<String, dynamic> namedArguments, List<Type> typeArguments]);
|
||||||
|
|
||||||
bool isAssignableTo(ReflectedType other);
|
bool isAssignableTo(ReflectedType other);
|
||||||
}
|
}
|
||||||
|
@ -54,9 +58,14 @@ abstract class ReflectedClass extends ReflectedType {
|
||||||
final List<ReflectedFunction> constructors;
|
final List<ReflectedFunction> constructors;
|
||||||
final List<ReflectedDeclaration> declarations;
|
final List<ReflectedDeclaration> declarations;
|
||||||
|
|
||||||
const ReflectedClass(String name, List<ReflectedTypeParameter> typeParameters,
|
const ReflectedClass(
|
||||||
this.annotations, this.constructors, this.declarations)
|
String name,
|
||||||
: super(name, typeParameters);
|
List<ReflectedTypeParameter> typeParameters,
|
||||||
|
this.annotations,
|
||||||
|
this.constructors,
|
||||||
|
this.declarations,
|
||||||
|
Type reflectedType)
|
||||||
|
: super(name, typeParameters, reflectedType);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
|
@ -96,7 +105,7 @@ class ReflectedFunction {
|
||||||
final String name;
|
final String name;
|
||||||
final List<ReflectedTypeParameter> typeParameters;
|
final List<ReflectedTypeParameter> typeParameters;
|
||||||
final List<ReflectedInstance> annotations;
|
final List<ReflectedInstance> annotations;
|
||||||
final Type returnType;
|
final ReflectedType returnType;
|
||||||
final List<ReflectedParameter> parameters;
|
final List<ReflectedParameter> parameters;
|
||||||
final bool isGetter, isSetter;
|
final bool isGetter, isSetter;
|
||||||
|
|
||||||
|
@ -132,7 +141,7 @@ class ReflectedFunction {
|
||||||
class ReflectedParameter {
|
class ReflectedParameter {
|
||||||
final String name;
|
final String name;
|
||||||
final List<ReflectedInstance> annotations;
|
final List<ReflectedInstance> annotations;
|
||||||
final Type type;
|
final ReflectedType type;
|
||||||
final bool isRequired;
|
final bool isRequired;
|
||||||
final bool isNamed;
|
final bool isNamed;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
name: angel_container
|
name: angel_container
|
||||||
|
version: 1.0.0-alpha
|
||||||
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
|
description: "A better IoC container for Angel, ultimately allowing Angel to be used without dart:mirrors."
|
||||||
|
homepage: https://github.com/angel-dart/container.git
|
||||||
|
environment:
|
||||||
|
sdk: ">=1.8.0 <3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
collection: ^1.0.0
|
collection: ^1.0.0
|
||||||
quiver_hashcode: ^1.0.0
|
quiver: ^2.0.0
|
||||||
|
dev_dependencies:
|
||||||
|
test:
|
59
angel_container/test/common.dart
Normal file
59
angel_container/test/common.dart
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import 'package:angel_container/angel_container.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void testReflector(Reflector reflector) {
|
||||||
|
var blaziken = new Pokemon('Blaziken', PokemonType.fire);
|
||||||
|
Container container;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
container = new Container(reflector);
|
||||||
|
container.singleton(blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make on singleton type returns singleton', () {
|
||||||
|
expect(container.make(Pokemon), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('make on aliased singleton returns singleton', () {
|
||||||
|
container.singleton(blaziken, as: StateError);
|
||||||
|
expect(container.make(StateError), blaziken);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('constructor injects singleton', () {
|
||||||
|
var lower = container.make(LowerPokemon) as LowerPokemon;
|
||||||
|
expect(lower.lowercaseName, blaziken.name.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('newInstance works', () {
|
||||||
|
var type = container.reflector.reflectType(Pokemon);
|
||||||
|
var instance =
|
||||||
|
type.newInstance('changeName', [blaziken, 'Charizard']) as Pokemon;
|
||||||
|
print(instance);
|
||||||
|
expect(instance.name, 'Charizard');
|
||||||
|
expect(instance.type, PokemonType.fire);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LowerPokemon {
|
||||||
|
final Pokemon pokemon;
|
||||||
|
|
||||||
|
LowerPokemon(this.pokemon);
|
||||||
|
|
||||||
|
String get lowercaseName => pokemon.name.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pokemon {
|
||||||
|
final String name;
|
||||||
|
final PokemonType type;
|
||||||
|
|
||||||
|
Pokemon(this.name, this.type);
|
||||||
|
|
||||||
|
factory Pokemon.changeName(Pokemon other, String name) {
|
||||||
|
return new Pokemon(name, other.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'NAME: $name, TYPE: $type';
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PokemonType { water, fire, grass, ice, poison, flying }
|
7
angel_container/test/mirrors_test.dart
Normal file
7
angel_container/test/mirrors_test.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import 'package:angel_container/mirrors.dart';
|
||||||
|
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testReflector(const MirrorsReflector());
|
||||||
|
}
|
Loading…
Reference in a new issue