Skip to content

Commit ef88e0f

Browse files
Tim Blasivicb
authored andcommitted
refactor(transformers): extract lifecycle hooks from the interfaces
1 parent 8302aff commit ef88e0f

File tree

7 files changed

+160
-102
lines changed

7 files changed

+160
-102
lines changed

modules_dart/analyzer_plugin/lib/src/tasks.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class BuildUnitDirectivesTask extends SourceBasedAnalysisTask {
3434
List<RenderDirectiveMetadata> metaList = <RenderDirectiveMetadata>[];
3535
for (CompilationUnitMember unitMember in unit.declarations) {
3636
if (unitMember is ClassDeclaration) {
37-
RenderDirectiveMetadata meta = readDirectiveMetadata(unitMember.metadata);
37+
RenderDirectiveMetadata meta = readDirectiveMetadata(unitMember);
3838
if (meta != null) {
3939
metaList.add(meta);
4040
}

modules_dart/transform/lib/src/transform/common/directive_metadata_reader.dart

Lines changed: 103 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ import 'package:angular2/src/core/render/api.dart';
66
import 'package:angular2/src/core/change_detection/change_detection.dart';
77

88
/// Reads [RenderDirectiveMetadata] from the `node`. `node` is expected to be an
9-
/// instance of [Annotation], [NodeList<Annotation>], ListLiteral, or
10-
/// [InstanceCreationExpression].
11-
RenderDirectiveMetadata readDirectiveMetadata(dynamic node) {
12-
assert(node is Annotation ||
13-
node is NodeList ||
14-
node is InstanceCreationExpression ||
15-
node is ListLiteral);
16-
var visitor = new _DirectiveMetadataVisitor();
9+
/// instance of [ClassDeclaration] (a class which may have a [Directive] or
10+
/// [Component] annotation) or an [InstanceCreationExpression] (an instantiation
11+
/// of [ReflectionInfo]).
12+
RenderDirectiveMetadata readDirectiveMetadata(AstNode node) {
13+
var visitor;
14+
if (node is ClassDeclaration) {
15+
visitor = new _DeclarationVisitor();
16+
} else if (node is InstanceCreationExpression) {
17+
visitor = new _ReflectionInfoVisitor();
18+
} else {
19+
throw new ArgumentError('Incorrect value passed to readDirectiveMetadata. '
20+
'Expected types are ClassDeclaration and InstanceCreationExpression '
21+
'Provided type was ${node.runtimeType}');
22+
}
1723
node.accept(visitor);
1824
return visitor.meta;
1925
}
@@ -45,8 +51,79 @@ num _getDirectiveType(String annotationName, Element element) {
4551
return byNameMatch;
4652
}
4753

48-
/// Visitor responsible for processing the `annotations` property of a
49-
/// [RegisterType] object and pulling out [RenderDirectiveMetadata].
54+
class _ReflectionInfoVisitor extends _DirectiveMetadataVisitor {
55+
// TODO(kegluneq): Create a more robust check that this is a real
56+
// `ReflectionInfo` instantiation.
57+
static bool _isReflectionInfo(InstanceCreationExpression node) {
58+
var name = node.constructorName.type.name;
59+
name = name is PrefixedIdentifier ? name.identifier : name;
60+
return '$name' == 'ReflectionInfo';
61+
}
62+
63+
@override
64+
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
65+
if (_isReflectionInfo(node)) {
66+
// NOTE(kegluneq): This is very tightly coupled with the `Reflector`
67+
// implementation. Clean this up with a better intermediate representation
68+
// for .ng_deps.dart files.
69+
var reflectionInfoArgs = node.argumentList.arguments;
70+
if (reflectionInfoArgs.length > 0) {
71+
// Process annotations to determine information specified via
72+
// `Component` and `Directive` parameters.
73+
reflectionInfoArgs[0].accept(this);
74+
if (_hasMeta && reflectionInfoArgs.length > 3) {
75+
// Process interfaces to determine which lifecycle events we need to
76+
// react to for this `Directive`.
77+
_processInterfaces(reflectionInfoArgs[3]);
78+
}
79+
}
80+
return null;
81+
}
82+
var directiveType = _getDirectiveType(
83+
'${node.constructorName.type.name}', node.staticElement);
84+
if (directiveType >= 0) {
85+
if (_hasMeta) {
86+
throw new FormatException(
87+
'Only one Directive is allowed per class. '
88+
'Found "$node" but already processed "$meta".',
89+
'$node' /* source */);
90+
}
91+
_initializeMetadata(directiveType);
92+
super.visitInstanceCreationExpression(node);
93+
}
94+
// Annotation we do not recognize - no need to visit.
95+
return null;
96+
}
97+
98+
void _processInterfaces(Expression lifecycleValue) {
99+
_checkMeta();
100+
if (lifecycleValue is! ListLiteral) {
101+
throw new FormatException(
102+
'Angular 2 expects a List but could not understand the value for interfaces. '
103+
'$lifecycleValue');
104+
}
105+
ListLiteral l = lifecycleValue;
106+
_populateLifecycle(l.elements.map((s) => s.toSource().split('.').last));
107+
}
108+
}
109+
110+
class _DeclarationVisitor extends _DirectiveMetadataVisitor {
111+
@override
112+
Object visitClassDeclaration(ClassDeclaration node) {
113+
node.metadata.accept(this);
114+
if (this._hasMeta) {
115+
if (node.implementsClause != null &&
116+
node.implementsClause.interfaces != null) {
117+
_populateLifecycle(node.implementsClause.interfaces
118+
.map((s) => s.toSource().split('.').last));
119+
}
120+
}
121+
return null;
122+
}
123+
}
124+
125+
/// Visitor responsible for processing [Directive] code into a
126+
/// [RenderDirectiveMetadata] object.
50127
class _DirectiveMetadataVisitor extends Object
51128
with RecursiveAstVisitor<Object> {
52129
bool get _hasMeta => _type != null;
@@ -130,24 +207,6 @@ class _DirectiveMetadataVisitor extends Object
130207
return null;
131208
}
132209

133-
@override
134-
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
135-
var directiveType = _getDirectiveType(
136-
'${node.constructorName.type.name}', node.staticElement);
137-
if (directiveType >= 0) {
138-
if (_hasMeta) {
139-
throw new FormatException(
140-
'Only one Directive is allowed per class. '
141-
'Found "$node" but already processed "$meta".',
142-
'$node' /* source */);
143-
}
144-
_initializeMetadata(directiveType);
145-
super.visitInstanceCreationExpression(node);
146-
}
147-
// Annotation we do not recognize - no need to visit.
148-
return null;
149-
}
150-
151210
@override
152211
Object visitNamedExpression(NamedExpression node) {
153212
// TODO(kegluneq): Remove this limitation.
@@ -170,10 +229,6 @@ class _DirectiveMetadataVisitor extends Object
170229
case 'host':
171230
_populateHost(node.expression);
172231
break;
173-
// TODO(vicb) should come from the interfaces on the class
174-
case 'lifecycle':
175-
_populateLifecycle(node.expression);
176-
break;
177232
case 'exportAs':
178233
_populateExportAs(node.expression);
179234
break;
@@ -207,8 +262,7 @@ class _DirectiveMetadataVisitor extends Object
207262
if (!_hasMeta) {
208263
throw new ArgumentError(
209264
'Incorrect value passed to readDirectiveMetadata. '
210-
'Expected types are Annotation, InstanceCreationExpression, '
211-
'NodeList or ListLiteral');
265+
'Expected types are ClassDeclaration and InstanceCreationExpression');
212266
}
213267
}
214268

@@ -271,23 +325,19 @@ class _DirectiveMetadataVisitor extends Object
271325
_exportAs = _expressionToString(exportAsValue, 'Directive#exportAs');
272326
}
273327

274-
void _populateLifecycle(Expression lifecycleValue) {
328+
void _populateLifecycle(Iterable<String> lifecycleInterfaceNames) {
275329
_checkMeta();
276-
if (lifecycleValue is! ListLiteral) {
277-
throw new FormatException(
278-
'Angular 2 expects a List but could not understand the value for lifecycle. '
279-
'$lifecycleValue');
280-
}
281-
ListLiteral l = lifecycleValue;
282-
var lifecycleEvents = l.elements.map((s) => s.toSource().split('.').last);
283-
_callOnDestroy = lifecycleEvents.contains("OnDestroy");
284-
_callOnChange = lifecycleEvents.contains("OnChanges");
285-
_callDoCheck = lifecycleEvents.contains("DoCheck");
286-
_callOnInit = lifecycleEvents.contains("OnInit");
287-
_callAfterContentInit = lifecycleEvents.contains("AfterContentInit");
288-
_callAfterContentChecked = lifecycleEvents.contains("AfterContentChecked");
289-
_callAfterViewInit = lifecycleEvents.contains("AfterViewInit");
290-
_callAfterViewChecked = lifecycleEvents.contains("AfterViewChecked");
330+
_callOnDestroy = lifecycleInterfaceNames.contains("OnDestroy");
331+
_callOnChange = lifecycleInterfaceNames.contains("OnChanges");
332+
_callDoCheck = lifecycleInterfaceNames.contains("DoCheck");
333+
_callOnInit = lifecycleInterfaceNames.contains("OnInit");
334+
_callAfterContentInit =
335+
lifecycleInterfaceNames.contains("AfterContentInit");
336+
_callAfterContentChecked =
337+
lifecycleInterfaceNames.contains("AfterContentChecked");
338+
_callAfterViewInit = lifecycleInterfaceNames.contains("AfterViewInit");
339+
_callAfterViewChecked =
340+
lifecycleInterfaceNames.contains("AfterViewChecked");
291341
}
292342

293343
void _populateEvents(Expression eventsValue) {
@@ -301,5 +351,6 @@ class _DirectiveMetadataVisitor extends Object
301351
}
302352
}
303353

304-
final Map<String, ChangeDetectionStrategy> changeDetectionStrategies
305-
= new Map.fromIterable(ChangeDetectionStrategy.values, key: (v) => v.toString());
354+
final Map<String, ChangeDetectionStrategy> changeDetectionStrategies =
355+
new Map.fromIterable(ChangeDetectionStrategy.values,
356+
key: (v) => v.toString());

modules_dart/transform/lib/src/transform/common/registered_type.dart

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class RegisteredType {
1414
/// The actual call to `Reflector#registerType`.
1515
final MethodInvocation registerMethod;
1616

17+
/// The `ReflectionInfo` [InstanceCreationExpression]
18+
final InstanceCreationExpression reflectionInfoCreate;
19+
1720
/// The factory method registered.
1821
final Expression factoryFn;
1922

@@ -25,22 +28,28 @@ class RegisteredType {
2528

2629
RenderDirectiveMetadata _directiveMetadata = null;
2730

28-
RegisteredType._(this.typeName, this.registerMethod, this.factoryFn,
29-
this.parameters, this.annotations);
31+
RegisteredType._(
32+
this.typeName,
33+
this.registerMethod,
34+
this.reflectionInfoCreate,
35+
this.factoryFn,
36+
this.parameters,
37+
this.annotations);
3038

3139
/// Creates a {@link RegisteredType} given a {@link MethodInvocation} node representing
3240
/// a call to `registerType`.
3341
factory RegisteredType.fromMethodInvocation(MethodInvocation registerMethod) {
3442
var visitor = new _ParseRegisterTypeVisitor();
3543
registerMethod.accept(visitor);
36-
return new RegisteredType._(visitor.typeName, registerMethod,
44+
return new RegisteredType._(visitor.typeName, registerMethod, visitor.info,
3745
visitor.factoryFn, visitor.parameters, visitor.annotations);
3846
}
3947

4048
RenderDirectiveMetadata get directiveMetadata {
4149
if (_directiveMetadata == null) {
4250
try {
43-
_directiveMetadata = readDirectiveMetadata(annotations);
51+
/// TODO(kegluneq): Allow passing a lifecycle interface matcher.
52+
_directiveMetadata = readDirectiveMetadata(reflectionInfoCreate);
4453
if (_directiveMetadata != null) {
4554
_directiveMetadata.id = '$typeName';
4655
}
@@ -55,6 +64,7 @@ class RegisteredType {
5564
class _ParseRegisterTypeVisitor extends Object
5665
with RecursiveAstVisitor<Object> {
5766
Identifier typeName;
67+
InstanceCreationExpression info;
5868
Expression factoryFn;
5969
Expression parameters;
6070
Expression annotations;
@@ -68,9 +78,9 @@ class _ParseRegisterTypeVisitor extends Object
6878
? node.argumentList.arguments[0]
6979
: null;
7080

71-
// The second argument to a `registerType` call is the RegistrationInfo
81+
// The second argument to a `registerType` call is the `ReflectionInfo`
7282
// object creation.
73-
var info = node.argumentList.arguments[1] as InstanceCreationExpression;
83+
info = node.argumentList.arguments[1] as InstanceCreationExpression;
7484
var args = info.argumentList.arguments;
7585
for (int i = 0; i < args.length; i++) {
7686
var arg = args[i];

modules_dart/transform/lib/src/transform/directive_processor/rewriter.dart

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'package:angular2/src/core/render/xhr.dart' show XHR;
88
import 'package:angular2/src/transform/common/annotation_matcher.dart';
99
import 'package:angular2/src/transform/common/asset_reader.dart';
1010
import 'package:angular2/src/transform/common/async_string_writer.dart';
11-
import 'package:angular2/src/transform/common/interface_matcher.dart';
1211
import 'package:angular2/src/transform/common/logging.dart';
1312
import 'package:angular2/src/transform/common/names.dart';
1413
import 'package:angular2/src/transform/common/xhr_impl.dart';
@@ -54,12 +53,7 @@ Future<String> createNgDeps(AssetReader reader, AssetId assetId,
5453
var declarationsCode =
5554
await _getAllDeclarations(reader, assetId, code, directivesVisitor);
5655
var declarationsVisitor = new _NgDepsDeclarationsVisitor(
57-
assetId,
58-
writer,
59-
new XhrImpl(reader, assetId),
60-
annotationMatcher,
61-
_interfaceMatcher,
62-
ngMeta,
56+
assetId, writer, new XhrImpl(reader, assetId), annotationMatcher, ngMeta,
6357
inlineViews: inlineViews);
6458
parseCompilationUnit(declarationsCode, name: '${assetId.path} and parts')
6559
.declarations
@@ -75,8 +69,6 @@ Future<String> createNgDeps(AssetReader reader, AssetId assetId,
7569
return writer.asyncToString();
7670
}
7771

78-
InterfaceMatcher _interfaceMatcher = new InterfaceMatcher();
79-
8072
/// Processes `visitor.parts`, reading and appending their contents to the
8173
/// original `code`.
8274
/// Order of `part`s is preserved. That is, if in the main library we have
@@ -278,29 +270,20 @@ class _NgDepsDeclarationsVisitor extends Object with SimpleAstVisitor<Object> {
278270
/// Angular 2, for example `@Component`.
279271
final AnnotationMatcher _annotationMatcher;
280272

281-
/// Responsible for testing whether interfaces are recognized by Angular2,
282-
/// for example `OnChanges`.
283-
final InterfaceMatcher _interfaceMatcher;
284-
285273
/// Used to fetch linked files.
286274
final XHR _xhr;
287275

288-
_NgDepsDeclarationsVisitor(
289-
AssetId assetId,
290-
AsyncStringWriter writer,
291-
XHR xhr,
292-
AnnotationMatcher annotationMatcher,
293-
InterfaceMatcher interfaceMatcher,
294-
this.ngMeta,
276+
_NgDepsDeclarationsVisitor(AssetId assetId, AsyncStringWriter writer, XHR xhr,
277+
AnnotationMatcher annotationMatcher, this.ngMeta,
295278
{bool inlineViews})
296279
: writer = writer,
297280
_copyVisitor = new ToSourceVisitor(writer),
298281
_factoryVisitor = new FactoryTransformVisitor(writer),
299282
_paramsVisitor = new ParameterTransformVisitor(writer),
300283
_metaVisitor = new AnnotationsTransformVisitor(
301-
writer, xhr, annotationMatcher, assetId, inlineViews: inlineViews),
284+
writer, xhr, annotationMatcher, assetId,
285+
inlineViews: inlineViews),
302286
_annotationMatcher = annotationMatcher,
303-
_interfaceMatcher = interfaceMatcher,
304287
this.assetId = assetId,
305288
_xhr = xhr;
306289

@@ -445,4 +428,5 @@ class _NgDepsDeclarationsVisitor extends Object with SimpleAstVisitor<Object> {
445428
}
446429

447430
const _REF_PREFIX = '_ngRef';
448-
const _REFLECTOR_IMPORT = 'package:angular2/src/core/reflection/reflection.dart';
431+
const _REFLECTOR_IMPORT =
432+
'package:angular2/src/core/reflection/reflection.dart';

modules_dart/transform/test/transform/directive_metadata_extractor/directive_metadata_files/changeDetection.ng_deps.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ void initReflector(reflector) {
1111
reflector
1212
..registerType(
1313
HelloCmp,
14-
new ReflectionInfo(
15-
const [const Component(changeDetection: ChangeDetectionStrategy.CheckOnce)],
16-
const [const []],
17-
() => new HelloCmp()));
14+
new ReflectionInfo(const [
15+
const Component(changeDetection: ChangeDetectionStrategy.CheckOnce)
16+
], const [
17+
const []
18+
], () => new HelloCmp()));
1819
}

modules_dart/transform/test/transform/directive_metadata_extractor/directive_metadata_files/events.ng_deps.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ void initReflector(reflector) {
1212
..registerType(
1313
HelloCmp,
1414
new ReflectionInfo(const [
15-
const Component(events: ['onFoo', 'onBar'])
15+
const Component(events: const ['onFoo', 'onBar'])
1616
], const [
1717
const []
1818
], () => new HelloCmp()));

0 commit comments

Comments
 (0)