diff --git a/.gitignore b/angel_container/.gitignore similarity index 100% rename from .gitignore rename to angel_container/.gitignore diff --git a/angel_container/LICENSE b/angel_container/LICENSE new file mode 100644 index 00000000..f8e6088a --- /dev/null +++ b/angel_container/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 The Angel Framework + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/angel_container/README.md b/angel_container/README.md new file mode 100644 index 00000000..11f96719 --- /dev/null +++ b/angel_container/README.md @@ -0,0 +1,2 @@ +# container +A better IoC container for Angel, ultimately allowing Angel to be used without dart:mirrors. diff --git a/angel_container/lib/angel_container.dart b/angel_container/lib/angel_container.dart new file mode 100644 index 00000000..9025b02e --- /dev/null +++ b/angel_container/lib/angel_container.dart @@ -0,0 +1,3 @@ +export 'src/container.dart'; +export 'src/exception.dart'; +export 'src/reflector.dart'; \ No newline at end of file diff --git a/angel_container/lib/mirrors.dart b/angel_container/lib/mirrors.dart new file mode 100644 index 00000000..7c4452c0 --- /dev/null +++ b/angel_container/lib/mirrors.dart @@ -0,0 +1 @@ +export 'src/mirrors/mirrors.dart'; \ No newline at end of file diff --git a/angel_container/lib/src/container.dart b/angel_container/lib/src/container.dart new file mode 100644 index 00000000..c08b3205 --- /dev/null +++ b/angel_container/lib/src/container.dart @@ -0,0 +1,51 @@ +import 'exception.dart'; +import 'reflector.dart'; + +class Container { + final Reflector reflector; + final Map _singletons = {}; + + Container(this.reflector); + + T make(Type type) { + if (_singletons.containsKey(type)) { + return _singletons[type] as T; + } else { + var reflectedType = reflector.reflectType(type); + var positional = []; + var named = {}; + + if (reflectedType is ReflectedClass) { + var constructor = reflectedType.constructors.firstWhere( + (c) => c.name.isEmpty, + orElse: () => throw new ReflectionException('${reflectedType + .name} has no default constructor, and therefore cannot be instantiated.')); + + for (var param in constructor.parameters) { + var value = make(param.type); + + if (param.isNamed) { + named[param.name] = value; + } else { + positional.add(value); + } + } + + return reflectedType + .newInstance(constructor.name, positional, named, []); + } else { + throw new ReflectionException( + '$type is not a class, and therefore cannot be instantiated.'); + } + } + } + + void singleton(Object object, {Type as}) { + if (_singletons.containsKey(as ?? object.runtimeType)) { + throw new StateError('This container already has a singleton for ${as ?? + object.runtimeType}.'); + } + + _singletons[as ?? object.runtimeType] = object; + } +} diff --git a/angel_container/lib/src/exception.dart b/angel_container/lib/src/exception.dart new file mode 100644 index 00000000..7cedc82a --- /dev/null +++ b/angel_container/lib/src/exception.dart @@ -0,0 +1,8 @@ +class ReflectionException implements Exception { + final String message; + + ReflectionException(this.message); + + @override + String toString() => message; +} diff --git a/angel_container/lib/src/mirrors/mirrors.dart b/angel_container/lib/src/mirrors/mirrors.dart new file mode 100644 index 00000000..69be7235 --- /dev/null +++ b/angel_container/lib/src/mirrors/mirrors.dart @@ -0,0 +1 @@ +export 'reflector.dart'; \ No newline at end of file diff --git a/angel_container/lib/src/mirrors/reflector.dart b/angel_container/lib/src/mirrors/reflector.dart new file mode 100644 index 00000000..4dbba446 --- /dev/null +++ b/angel_container/lib/src/mirrors/reflector.dart @@ -0,0 +1,127 @@ +import 'dart:mirrors' as dart; +import 'package:angel_container/angel_container.dart'; +import 'package:angel_container/src/reflector.dart'; +import 'package:angel_container/src/reflector.dart'; + +class MirrorsReflector implements Reflector { + @override + String getName(Symbol symbol) => dart.MirrorSystem.getName(symbol); + + @override + ReflectedClass reflectClass(Type clazz) { + var mirror = dart.reflectType(clazz); + + if (mirror is dart.ClassMirror) { + return new _ReflectedClassMirror(mirror); + } else { + throw new ArgumentError('$clazz is not a class.'); + } + } + + @override + ReflectedFunction reflectFunction(Function function) { + // TODO: implement reflectFunction + } + + @override + ReflectedType reflectType(Type type) { + var mirror = dart.reflectType(type); + + if (mirror is dart.ClassMirror) { + return new _ReflectedClassMirror(mirror); + } else { + return new _ReflectedTypeMirror(mirror); + } + } +} + +class _ReflectedTypeParameter extends ReflectedTypeParameter { + final dart.TypeVariableMirror mirror; + + _ReflectedTypeParameter(this.mirror) + : super( + dart.MirrorSystem.getName(mirror.simpleName), mirror.reflectedType); +} + +class _ReflectedTypeMirror extends ReflectedType { + final dart.TypeMirror mirror; + + _ReflectedTypeMirror(this.mirror) + : super( + dart.MirrorSystem.getName(mirror.simpleName), + mirror.typeVariables + .map((m) => new _ReflectedTypeParameter(m)) + .toList(), + ); + + @override + bool isAssignableTo(ReflectedType other) { + return other is _ReflectedTypeMirror && mirror.isAssignableTo(other.mirror); + } + + @override + T newInstance(String constructorName, List positionalArguments, + Map namedArguments, List typeArguments) { + throw new ReflectionException( + '$name is not a class, and therefore cannot be instantiated.'); + } +} + +class _ReflectedClassMirror extends ReflectedClass { + final dart.ClassMirror mirror; + + _ReflectedClassMirror(this.mirror) + : super( + dart.MirrorSystem.getName(mirror.simpleName), + mirror.typeVariables + .map((m) => new _ReflectedTypeParameter(m)) + .toList(), + mirror.metadata.map((m) => new _ReflectedInstanceMirror(m)).toList(), + _constructorsOf(mirror), + _declarationsOf(mirror), + ); + + static List _constructorsOf(dart.ClassMirror mirror) { + var out = []; + + for (var key in mirror.declarations.keys) { + var value = mirror.declarations[key]; + } + + return out; + } + + static List _declarationsOf(dart.ClassMirror mirror) { + var out = []; + + for (var key in mirror.declarations.keys) { + var value = mirror.declarations[key]; + } + + return out; + } + + @override + bool isAssignableTo(ReflectedType other) { + return other is _ReflectedTypeMirror && mirror.isAssignableTo(other.mirror); + } + + @override + T newInstance(String constructorName, List positionalArguments, + Map namedArguments, List typeArguments) { + // TODO: implement newInstance + } +} + +class _ReflectedInstanceMirror extends ReflectedInstance { + final dart.InstanceMirror mirror; + + _ReflectedInstanceMirror(this.mirror) + : super(new _ReflectedClassMirror(mirror.type), + new _ReflectedClassMirror(mirror.type)); + + @override + T invoke(Invocation invocation) { + return mirror.delegate(invocation) as T; + } +} diff --git a/angel_container/lib/src/reflector.dart b/angel_container/lib/src/reflector.dart new file mode 100644 index 00000000..45eeaffa --- /dev/null +++ b/angel_container/lib/src/reflector.dart @@ -0,0 +1,171 @@ +import 'package:collection/collection.dart'; +import 'package:quiver_hashcode/hashcode.dart'; + +abstract class Reflector { + String getName(Symbol symbol); + + ReflectedClass reflectClass(Type clazz); + + ReflectedFunction reflectFunction(Function function); + + ReflectedType reflectType(Type type); +} + +abstract class ReflectedInstance { + final ReflectedType type; + final ReflectedClass clazz; + + const ReflectedInstance(this.type, this.clazz); + + @override + int get hashCode => hash2(type, clazz); + + @override + bool operator ==(other) => + other is ReflectedInstance && other.type == type && other.clazz == clazz; + + T invoke(Invocation invocation); +} + +abstract class ReflectedType { + final String name; + final List typeParameters; + + const ReflectedType(this.name, this.typeParameters); + + @override + int get hashCode => hash2(name, typeParameters); + + @override + bool operator ==(other) => + other is ReflectedType && + other.name == name && + const ListEquality() + .equals(other.typeParameters, typeParameters); + + T newInstance(String constructorName, List positionalArguments, + Map namedArguments, List typeArguments); + + bool isAssignableTo(ReflectedType other); +} + +abstract class ReflectedClass extends ReflectedType { + final List annotations; + final List constructors; + final List declarations; + + const ReflectedClass(String name, List typeParameters, + this.annotations, this.constructors, this.declarations) + : super(name, typeParameters); + + @override + int get hashCode => + hash4(super.hashCode, annotations, constructors, declarations); + + @override + bool operator ==(other) => + other is ReflectedClass && + super == other && + const ListEquality() + .equals(other.annotations, annotations) && + const ListEquality() + .equals(other.constructors, constructors) && + const ListEquality() + .equals(other.declarations, declarations); +} + +class ReflectedDeclaration { + final String name; + final bool isStatic; + final ReflectedFunction function; + + const ReflectedDeclaration(this.name, this.isStatic, this.function); + + @override + int get hashCode => hash3(name, isStatic, function); + + @override + bool operator ==(other) => + other is ReflectedDeclaration && + other.name == name && + other.isStatic == isStatic && + other.function == function; +} + +class ReflectedFunction { + final String name; + final List typeParameters; + final List annotations; + final Type returnType; + final List parameters; + final bool isGetter, isSetter; + + const ReflectedFunction(this.name, this.typeParameters, this.annotations, + this.returnType, this.parameters, this.isGetter, this.isSetter); + + @override + int get hashCode => hashObjects([ + name, + typeParameters, + annotations, + returnType, + parameters, + isGetter, + isSetter + ]); + + @override + bool operator ==(other) => + other is ReflectedFunction && + other.name == name && + const ListEquality() + .equals(other.typeParameters, typeParameters) && + const ListEquality() + .equals(other.annotations, annotations) && + other.returnType == returnType && + const ListEquality() + .equals(other.parameters, other.parameters) && + other.isGetter == isGetter && + other.isSetter == isSetter; +} + +class ReflectedParameter { + final String name; + final List annotations; + final Type type; + final bool isRequired; + final bool isNamed; + + const ReflectedParameter( + this.name, this.annotations, this.type, this.isRequired, this.isNamed); + + @override + int get hashCode => + hashObjects([name, annotations, type, isRequired, isNamed]); + + @override + bool operator ==(other) => + other is ReflectedParameter && + other.name == name && + const ListEquality() + .equals(other.annotations, annotations) && + other.type == type && + other.isRequired == isRequired && + other.isNamed == isNamed; +} + +class ReflectedTypeParameter { + final String name; + final Type type; + + const ReflectedTypeParameter(this.name, this.type); + + @override + int get hashCode => hash2(name, type); + + @override + bool operator ==(other) => + other is ReflectedTypeParameter && + other.name == name && + other.type == type; +} diff --git a/angel_container/pubspec.yaml b/angel_container/pubspec.yaml new file mode 100644 index 00000000..6e3292a5 --- /dev/null +++ b/angel_container/pubspec.yaml @@ -0,0 +1,4 @@ +name: angel_container +dependencies: + collection: ^1.0.0 + quiver_hashcode: ^1.0.0 \ No newline at end of file diff --git a/angel_container_generator/.gitignore b/angel_container_generator/.gitignore new file mode 100644 index 00000000..7bf00e82 --- /dev/null +++ b/angel_container_generator/.gitignore @@ -0,0 +1,13 @@ +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +.pub/ +build/ +# If you're building an application, you may want to check-in your pubspec.lock +pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ diff --git a/angel_container_generator/LICENSE b/angel_container_generator/LICENSE new file mode 100644 index 00000000..f8e6088a --- /dev/null +++ b/angel_container_generator/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 The Angel Framework + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/angel_container_generator/README.md b/angel_container_generator/README.md new file mode 100644 index 00000000..11f96719 --- /dev/null +++ b/angel_container_generator/README.md @@ -0,0 +1,2 @@ +# container +A better IoC container for Angel, ultimately allowing Angel to be used without dart:mirrors.