Skip to content
This repository has been archived by the owner on Dec 6, 2017. It is now read-only.

Commit

Permalink
refactor(injector): introduced ResolutionContext to simplify circular…
Browse files Browse the repository at this point in the history
… dependency detection
  • Loading branch information
pavelgj committed Apr 22, 2014
1 parent c50c153 commit cf0b922
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 27 deletions.
52 changes: 34 additions & 18 deletions lib/src/base_injector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ List<Key> _PRIMITIVE_TYPES = new UnmodifiableListView(<Key>[

bool _defaultVisibility(_, __) => true;

const ResolutionContext _ZERO_DEPTH_RESOLVING =
const ResolutionContext(0, null, null);

abstract class BaseInjector implements Injector, ObjectFactory {

@override
Expand Down Expand Up @@ -79,15 +82,11 @@ abstract class BaseInjector implements Injector, ObjectFactory {
return types;
}

// 'resolving' is a tuple of (depth, Key, cdr), I implemented it
// as an array, but there may be a better solution.
static const ZERO_DEPTH_RESOLVING = const [0];

Object getInstanceByKey(Key key, Injector requester, List resolving) {
Object getInstanceByKey(Key key, Injector requester, ResolutionContext resolving) {
assert(_checkKeyConditions(key, resolving));

// Do not bother checking the array until we are fairly deep.
if (resolving[0] > 30 && resolvedTypes(resolving).contains(key)) {
if (resolving.depth > 30 && resolvedTypes(resolving).contains(key)) {
throw new CircularDependencyError(
error(resolving, 'Cannot resolve a circular dependency!', key));
}
Expand All @@ -107,13 +106,13 @@ abstract class BaseInjector implements Injector, ObjectFactory {
throw new NoProviderError(
error(resolving, 'No provider found for ${key}!', key));
}
injector =
injector.parent._getProviderWithInjectorForKey(key, resolving).injector;
injector = injector.parent
._getProviderWithInjectorForKey(key, resolving).injector;
}
return injector.getInstanceByKey(key, requester, resolving);
}

resolving = [resolving[0] + 1, key, resolving];
resolving = new ResolutionContext(resolving.depth + 1, key, resolving);
var value = provider.get(this, requester, this, resolving);

// cache the value.
Expand All @@ -123,7 +122,7 @@ abstract class BaseInjector implements Injector, ObjectFactory {

/// Returns a pair for provider and the injector where it's defined.
_ProviderWithDefiningInjector _getProviderWithInjectorForKey(
Key key, List resolving) {
Key key, ResolutionContext resolving) {
if (key.id < _providersLen) {
var provider = _providers[key.id];
if (provider != null) {
Expand All @@ -140,28 +139,31 @@ abstract class BaseInjector implements Injector, ObjectFactory {
new TypeProvider(key.type), this);
}

throw new NoProviderError(error(resolving, 'No provider found for ${key}!', key));
throw new NoProviderError(
error(resolving, 'No provider found for ${key}!', key));
}

bool _checkKeyConditions(Key key, List resolving) {
bool _checkKeyConditions(Key key, ResolutionContext resolving) {
if (_PRIMITIVE_TYPES.contains(key)) {
throw new NoProviderError(error(resolving, 'Cannot inject a primitive type '
'of ${key.type}!', key));
throw new NoProviderError(
error(resolving,
'Cannot inject a primitive type of ${key.type}!', key));
}
return true;
}

@override
dynamic get(Type type, [Type annotation]) =>
getInstanceByKey(new Key(type, annotation), this, BaseInjector.ZERO_DEPTH_RESOLVING);
getInstanceByKey(new Key(type, annotation), this, _ZERO_DEPTH_RESOLVING);

@override
dynamic getByKey(Key key) => getInstanceByKey(key, this, BaseInjector.ZERO_DEPTH_RESOLVING);
dynamic getByKey(Key key) =>
getInstanceByKey(key, this, _ZERO_DEPTH_RESOLVING);

@override
Injector createChild(List<Module> modules,
{List forceNewInstances, String name}) =>
createChildWithResolvingHistory(modules, BaseInjector.ZERO_DEPTH_RESOLVING,
createChildWithResolvingHistory(modules, _ZERO_DEPTH_RESOLVING,
forceNewInstances: forceNewInstances,
name: name);

Expand All @@ -178,7 +180,8 @@ abstract class BaseInjector implements Injector, ObjectFactory {
throw 'forceNewInstances must be List<Key|Type>';
}
assert(key is Key);
var providerWithInjector = _getProviderWithInjectorForKey(key, resolving);
var providerWithInjector =
_getProviderWithInjectorForKey(key, resolving);
var provider = providerWithInjector.provider;
forceNew.factoryByKey(key, (Injector inj) => provider.get(this,
inj, inj as ObjectFactory, resolving),
Expand All @@ -203,3 +206,16 @@ class _ProviderWithDefiningInjector {
final BaseInjector injector;
_ProviderWithDefiningInjector(this.provider, this.injector);
}

/**
* Information about the context in which the [key] is being resolved, including
* dependency tree [depth] at which the key is being resolved, as well as
* [parent] context (used to determine circular dependencies).
*/
class ResolutionContext {
final int depth;
final Key key;
final ResolutionContext parent;

const ResolutionContext(this.depth, this.key, this.parent);
}
13 changes: 7 additions & 6 deletions lib/src/error_helper.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
library di.error_helper;

import 'package:di/di.dart';
import 'package:di/src/base_injector.dart';

String error(List resolving, message, [appendDependency]) {
String error(ResolutionContext resolving, message, [Key appendDependency]) {
if (appendDependency != null) {
resolving = [resolving[0] + 1, appendDependency, resolving];
resolving = new ResolutionContext(resolving.depth + 1, appendDependency, resolving);
}

String graph = resolvedTypes(resolving).reversed.join(' -> ');

return '$message (resolving $graph)';
}

List<Key> resolvedTypes(resolving) {
List<Key> resolvedTypes(ResolutionContext resolving) {
List resolved = [];
while (resolving[0] != 0) {
resolved.add(resolving[1]);
resolving = resolving[2];
while (resolving.depth != 0) {
resolved.add(resolving.key);
resolving = resolving.parent;
}
return resolved;
}
4 changes: 2 additions & 2 deletions lib/src/injector_delagate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:di/di.dart';

class InjectorDelagate implements BaseInjector, ObjectFactory {
BaseInjector _injector;
List<Key> _resolving;
ResolutionContext _resolving;

InjectorDelagate(this._injector, this._resolving);

Expand All @@ -30,7 +30,7 @@ class InjectorDelagate implements BaseInjector, ObjectFactory {
_injector.getInstanceByKey(new Key(type, annotation), this, _resolving);

@override
dynamic getInstanceByKey(Key key, Injector requester, List<Key> resolving) =>
dynamic getInstanceByKey(Key key, Injector requester, ResolutionContext resolving) =>
_injector.getInstanceByKey(key, requester, resolving);

@override
Expand Down
2 changes: 1 addition & 1 deletion lib/src/provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'base_injector.dart';
import 'package:di/di.dart';

abstract class ObjectFactory {
Object getInstanceByKey(Key key, BaseInjector requester, List resolving);
Object getInstanceByKey(Key key, BaseInjector requester, ResolutionContext resolving);
}

abstract class Provider {
Expand Down

0 comments on commit cf0b922

Please sign in to comment.