Skip to content
This repository has been archived by the owner on Sep 16, 2022. It is now read-only.

Commit

Permalink
perf(ReflectiveProviders): Phase1, speed up resolveReflectiveProviders
Browse files Browse the repository at this point in the history
Reduces resolveReflectiveProviders AWSM 22ms->12ms, CM 63ms->24ms for startup.
ReflectiveInjection hashmap lookups reduced for runtime perf.
useProperty: removed from provider api.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=153207714
  • Loading branch information
ferhatb committed Apr 14, 2017
1 parent e71c364 commit 9eb1ef8
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 114 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

### Breaking changes

* Injecting null no longer supported.
* Remove unused `useProperty` argument in DI `Provider` api.
* `ReflectionCapabilities.isReflectionEnabled` renamed to `reflectionEnabled`.
* Malformed CSS warnings are errors now.

* Removed forms async validators. Alternative:

```dart
Expand Down
4 changes: 0 additions & 4 deletions lib/src/compiler/compile_metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ class CompileProviderMetadata {
dynamic useValue;
CompileTokenMetadata useExisting;
CompileFactoryMetadata useFactory;
String useProperty;
List<CompileDiDependencyMetadata> deps;
bool multi;
CompileProviderMetadata(
Expand All @@ -162,7 +161,6 @@ class CompileProviderMetadata {
this.useValue,
this.useExisting,
this.useFactory,
this.useProperty,
this.deps,
bool multi})
: this.multi = multi == true;
Expand All @@ -177,7 +175,6 @@ class CompileProviderMetadata {
_objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson),
useFactory:
_objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson),
useProperty: data['useProperty'],
multi: data['multi'],
deps: _arrayFromJson(data['deps'], CompileDiDependencyMetadata.fromJson)
as List<CompileDiDependencyMetadata>);
Expand All @@ -192,7 +189,6 @@ class CompileProviderMetadata {
'useExisting': _objToJson(useExisting),
'useValue': _objToJson(useValue),
'useFactory': _objToJson(useFactory),
'useProperty': useProperty,
'multi': multi,
'deps': _arrayToJson(deps)
};
Expand Down
1 change: 0 additions & 1 deletion lib/src/compiler/provider_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,6 @@ CompileProviderMetadata _transformProvider(CompileProviderMetadata provider,
useExisting: useExisting,
useFactory: provider.useFactory,
useValue: useValue,
useProperty: provider.useProperty,
deps: deps,
multi: provider.multi);
}
Expand Down
1 change: 0 additions & 1 deletion lib/src/compiler/runtime_metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ class RuntimeMetadataResolver {
useExisting: provider.useExisting != null
? this.getTokenMetadata(provider.useExisting)
: null,
useProperty: provider.useProperty,
deps: compileDeps,
multi: provider.multi);
}
Expand Down
3 changes: 0 additions & 3 deletions lib/src/compiler/view_compiler/compile_element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,6 @@ class CompileElement extends CompileNode {
} else {
providerValue = convertValueToOutputAst(provider.useValue);
}
if (provider.useProperty != null) {
providerValue = providerValue.prop(provider.useProperty);
}
providerValueExpressions.add(providerValue);
}
if (isLocalAlias) {
Expand Down
6 changes: 0 additions & 6 deletions lib/src/core/di/provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,6 @@ class Provider {
/// Used in conjunction with dependencies.
final Function useFactory;

/// Specifies the property of the configuration class to use as value.
///
/// Only used in conjunction with the @Injector class.
final String useProperty;

/// Specifies a set of dependencies
/// (as `token`s) which should be injected into the factory function.
///
Expand All @@ -126,7 +121,6 @@ class Provider {
this.useValue: noValueProvided,
this.useExisting,
this.useFactory,
this.useProperty,
List<Object> deps,
bool multi})
: dependencies = deps,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/core/di/reflective_injector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ class ReflectiveInjectorImpl implements ReflectiveInjector {
} catch (e, e_stack) {
throw new InstantiationError(this, e, e_stack, provider.key);
}
return resolvedFactory.postProcess(obj);
return obj;
}

dynamic _getByReflectiveDependency(
Expand Down
5 changes: 2 additions & 3 deletions lib/src/core/di/reflective_key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ class KeyRegistry {
var _allKeys = <Object, ReflectiveKey>{};
ReflectiveKey get(Object token) {
if (token is ReflectiveKey) return token;
if (_allKeys.containsKey(token)) {
return _allKeys[token];
}
var res = _allKeys[token];
if (res != null) return res;
var newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
_allKeys[token] = newKey;
return newKey;
Expand Down
173 changes: 112 additions & 61 deletions lib/src/core/di/reflective_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:angular2/src/facade/lang.dart' show assertionsEnabled;

import '../metadata.dart';
import 'decorators.dart';
import 'provider.dart' show Provider, provide, noValueProvided;
import 'provider.dart' show Provider, noValueProvided;
import 'reflective_exceptions.dart'
show
NoAnnotationError,
Expand All @@ -15,22 +15,18 @@ import 'reflective_key.dart';
/// [Dependency] is used by the framework to extend DI.
/// This is internal to Angular and should not be used directly.
class ReflectiveDependency {
ReflectiveKey key;
bool optional;
dynamic lowerBoundVisibility;
dynamic upperBoundVisibility;
List<dynamic> properties;
final ReflectiveKey key;
final bool optional;
final dynamic lowerBoundVisibility;
final dynamic upperBoundVisibility;
final List properties;
ReflectiveDependency(this.key, this.optional, this.lowerBoundVisibility,
this.upperBoundVisibility, this.properties);
static ReflectiveDependency fromKey(ReflectiveKey key) {
return new ReflectiveDependency(key, false, null, null, []);
return new ReflectiveDependency(key, false, null, null, const []);
}
}

dynamic _identityPostProcess(obj) {
return obj;
}

/// An internal resolved representation of a [Provider] used by the [Injector].
///
/// It is usually created automatically by `Injector.resolveAndCreate`.
Expand Down Expand Up @@ -62,29 +58,26 @@ class ResolvedReflectiveProviderImpl implements ResolvedReflectiveBinding {
ReflectiveKey key;
List<ResolvedReflectiveFactory> resolvedFactories;
bool multiProvider;

ResolvedReflectiveProviderImpl(
this.key, this.resolvedFactories, this.multiProvider);
ResolvedReflectiveFactory get resolvedFactory {
return this.resolvedFactories[0];
}

ResolvedReflectiveFactory get resolvedFactory => resolvedFactories.first;
}

/// An internal resolved representation of a factory function created by
/// resolving [Provider].
class ResolvedReflectiveFactory {
Function factory;
List<ReflectiveDependency> dependencies;
Function postProcess;
final Function factory;
final List<ReflectiveDependency> dependencies;

/// Constructs a resolved factory.
///
/// [factory] returns an instance of an object represented by a key.
///
/// [dependencies] is a list of dependencies passed to [factory] as
/// parameters.
///
/// [postProcess] function is applied to the value constructed by [factory].
ResolvedReflectiveFactory(this.factory, this.dependencies, this.postProcess);
ResolvedReflectiveFactory(this.factory, this.dependencies);
}

/// Resolve a single provider.
Expand Down Expand Up @@ -125,26 +118,27 @@ ResolvedReflectiveFactory resolveReflectiveFactory(Provider provider) {
provider.dependencies,
);
}
} else if (provider.useClass != null) {
var useClass = provider.useClass;
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
} else if (provider.useValue != noValueProvided) {
factoryFn = () => provider.useValue;
resolvedDeps = const <ReflectiveDependency>[];
} else if (provider.token is Type) {
var useClass = provider.token;
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
} else {
throw new InvalidProviderError.withCustomMessage(
provider, 'token is not a Type and no factory was specified');
var useClass = provider.useClass;
if (useClass != null) {
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
} else {
var useValue = provider.useValue;
if (useValue != noValueProvided) {
factoryFn = () => useValue;
resolvedDeps = const <ReflectiveDependency>[];
} else if (provider.token is Type) {
var useClass = provider.token;
factoryFn = reflector.factory(useClass);
resolvedDeps = _dependenciesFor(useClass);
} else {
throw new InvalidProviderError.withCustomMessage(
provider, 'token is not a Type and no factory was specified');
}
}
}

var postProcess = provider.useProperty != null
? reflector.getter(provider.useProperty)
: _identityPostProcess;
return new ResolvedReflectiveFactory(factoryFn, resolvedDeps, postProcess);
return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
}

/// Converts the [Provider] into [ResolvedProvider].
Expand All @@ -160,33 +154,35 @@ ResolvedReflectiveProvider resolveReflectiveProvider(Provider provider) {
List<ResolvedReflectiveProvider> resolveReflectiveProviders(
List<dynamic /* Type | Provider | List < dynamic > */ > providers) {
var normalized = _normalizeProviders(providers, []);
var resolved = normalized.map(resolveReflectiveProvider).toList();
return mergeResolvedReflectiveProviders(
resolved, new Map<num, ResolvedReflectiveProvider>())
.values
.toList();
var resolved = <ResolvedReflectiveProvider>[];
for (int i = 0, len = normalized.length; i < len; i++) {
resolved.add(resolveReflectiveProvider(normalized[i]));
}
return mergeResolvedReflectiveProviders(resolved);
}

/// Merges a list of ResolvedProviders into a list where
/// each key is contained exactly once and multi providers
/// have been merged.
Map<num, ResolvedReflectiveProvider> mergeResolvedReflectiveProviders(
List<ResolvedReflectiveProvider> providers,
Map<num, ResolvedReflectiveProvider> normalizedProvidersMap) {
for (var i = 0; i < providers.length; i++) {
List<ResolvedReflectiveProvider> mergeResolvedReflectiveProviders(
List<ResolvedReflectiveProvider> providers) {
// Map used to dedup by provider key id.
var idToProvider = <num, ResolvedReflectiveProvider>{};
for (var i = 0, len = providers.length; i < len; i++) {
var provider = providers[i];
var existing = normalizedProvidersMap[provider.key.id];
var existing = idToProvider[provider.key.id];
if (existing != null) {
if (!identical(provider.multiProvider, existing.multiProvider)) {
throw new MixingMultiProvidersWithRegularProvidersError(
existing, provider);
}
if (provider.multiProvider) {
for (var j = 0; j < provider.resolvedFactories.length; j++) {
var factories = provider.resolvedFactories;
for (var j = 0, len = factories.length; j < len; j++) {
existing.resolvedFactories.add(provider.resolvedFactories[j]);
}
} else {
normalizedProvidersMap[provider.key.id] = provider;
idToProvider[provider.key.id] = provider;
}
} else {
var resolvedProvider;
Expand All @@ -196,28 +192,31 @@ Map<num, ResolvedReflectiveProvider> mergeResolvedReflectiveProviders(
} else {
resolvedProvider = provider;
}
normalizedProvidersMap[provider.key.id] = resolvedProvider;
idToProvider[provider.key.id] = resolvedProvider;
}
}
return normalizedProvidersMap;
return idToProvider.values.toList();
}

// Flattens list of lists of providers into a flat list. If any entry is a
// Type it converts it to a useClass provider.
List<Provider> _normalizeProviders(
List<dynamic /* Type | Provider | List < dynamic > */ > providers,
List<Provider> res) {
providers.forEach((b) {
for (int i = 0, len = providers.length; i < len; i++) {
var b = providers[i];
if (b is Type) {
res.add(provide(b, useClass: b));
_normalizeProviders(const [], res);
// If user listed a Type in provider list create Provide useClass: for it.
// This is a shortcut to make provider lists easier to create.
res.add(new Provider(b, useClass: b));
} else if (b is Provider) {
_normalizeProviders(const [], res);
res.add(b);
} else if (b is List) {
_normalizeProviders(b, res);
} else {
throw new InvalidProviderError(b);
}
});
}
return res;
}

Expand All @@ -226,10 +225,12 @@ List<ReflectiveDependency> constructDependencies(
if (dependencies == null) {
return _dependenciesFor(typeOrFunc);
} else {
List<List<dynamic>> params = dependencies.map((t) => [t]).toList();
return dependencies
.map((t) => _extractToken(typeOrFunc, t, params))
.toList();
var deps = <ReflectiveDependency>[];
for (int i = 0, len = dependencies.length; i < len; i++) {
deps.add(_extractTokenUnwrappedParameters(
typeOrFunc, dependencies[i], dependencies));
}
return deps;
}
}

Expand Down Expand Up @@ -288,6 +289,56 @@ ReflectiveDependency _extractToken(
token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
}

// Same as _extractToken but doesn't expect list wrapped parameters. This is
// to reduce GC by eliminating list allocations for each parameter.
ReflectiveDependency _extractTokenUnwrappedParameters(
typeOrFunc, metadata, List<dynamic> params) {
var depProps = [];
var token;
var optional = false;
if (metadata is! List) {
if (metadata is Inject) {
return _createDependency(metadata.token, optional, null, null, depProps);
} else {
return _createDependency(metadata, optional, null, null, depProps);
}
}
var lowerBoundVisibility;
var upperBoundVisibility;
for (var i = 0; i < metadata.length; ++i) {
var paramMetadata = metadata[i];
if (paramMetadata is Type) {
token = paramMetadata;
} else if (paramMetadata is Inject) {
token = paramMetadata.token;
} else if (paramMetadata is Optional) {
optional = true;
} else if (paramMetadata is Self) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata is Host) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata is SkipSelf) {
lowerBoundVisibility = paramMetadata;
} else if (paramMetadata is DependencyMetadata) {
if (paramMetadata.token != null) {
token = paramMetadata.token;
}
depProps.add(paramMetadata);
}
}
if (token == null) {
// Since we have rare failure, wrap parameter types into a format that
// NoAnnotationError expects in reflective mode.
var paramList = <List<dynamic>>[];
for (var param in params) {
paramList.add([param]);
}
throw new NoAnnotationError(typeOrFunc, params);
}
return _createDependency(
token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
}

ReflectiveDependency _createDependency(
token, optional, lowerBoundVisibility, upperBoundVisibility, depProps) {
return new ReflectiveDependency(ReflectiveKey.get(token), optional,
Expand Down
Loading

0 comments on commit 9eb1ef8

Please sign in to comment.