Skip to content

Commit 3b6aaf9

Browse files
Tim Blasimhevery
authored andcommitted
feat(dart/transform) Remove import of dart:mirrors
+ Precede the call to `new ReflectionCapabilities()` with our generated code which populates the reflection map statically. + Add the import of our generated code. + Once we are generating all necessary code, we will remove the import of reflection_capabilities.dart and the instantiation of `ReflectionCapabilities`, cutting the dependency on dart:mirrors. Closes angular#761
1 parent fad25c2 commit 3b6aaf9

File tree

25 files changed

+339
-187
lines changed

25 files changed

+339
-187
lines changed

modules/angular2/src/transform/annotation_processor.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class AnnotationMatcher {
5454
}
5555
}
5656

57-
/// [ConstructorElement] / [ElementAnnotation] pair, where the Constructor
57+
/// [ClassElement] / [ElementAnnotation] pair.
5858
class AnnotationMatch {
5959
/// The resolved element corresponding to [node].
6060
final ClassElement element;

modules/angular2/src/transform/codegen.dart

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ abstract class DirectiveRegistry {
6060
void register(AnnotationMatch entry);
6161
}
6262

63+
const setupReflectionMethodName = 'setupReflection';
64+
65+
const _libraryDeclaration = '''
66+
library angular2.src.transform.generated;
67+
''';
68+
6369
const _reflectorImport = '''
6470
import 'package:angular2/src/reflection/reflection.dart' show reflector;
6571
''';
@@ -70,18 +76,17 @@ AssetId _assetIdFromLibraryElement(LibraryElement el) {
7076
return (el.source as dynamic).assetId;
7177
}
7278

73-
String codegenEntryPoint(Context context,
74-
{LibraryElement entryPoint, AssetId newEntryPoint}) {
75-
// This must be called prior to [codegenImports] or the entry point
76-
// library will not be imported.
77-
var entryPointPrefix = context._getPrefixDot(entryPoint);
79+
String codegenEntryPoint(Context context, {AssetId newEntryPoint}) {
80+
if (newEntryPoint == null) {
81+
throw new ArgumentError.notNull('newEntryPoint');
82+
}
7883
// TODO(jakemac): copyright and library declaration
79-
var outBuffer = new StringBuffer(_reflectorImport);
84+
var outBuffer = new StringBuffer()
85+
..write(_libraryDeclaration)
86+
..write(_reflectorImport);
8087
_codegenImports(context, newEntryPoint, outBuffer);
8188
outBuffer
82-
..write('main() {')
83-
..write(context.directiveRegistry.toString())
84-
..write('${entryPointPrefix}main();}');
89+
.write('${setupReflectionMethodName}() {${context.directiveRegistry}}');
8590

8691
return new DartFormatter().format(outBuffer.toString());
8792
}

modules/angular2/src/transform/find_bootstrap.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
library angular2.src.transform;
2+
13
import 'package:analyzer/src/generated/ast.dart';
24
import 'package:analyzer/src/generated/element.dart';
35
import 'package:code_transformers/resolver.dart';
@@ -60,8 +62,8 @@ class _ParseBootstrapTypeVisitor extends SimpleAstVisitor<Object> {
6062
// TODO(kegluneq): Allow non-SimpleIdentifier expressions.
6163

6264
@override
63-
Object visitSimpleIdentifier(SimpleIdentifier e) {
64-
bootstrapType = (e.bestElement as ClassElement);
65+
Object visitSimpleIdentifier(SimpleIdentifier node) {
66+
bootstrapType = (node.bestElement as ClassElement);
6567
if (!_types.isComponent(bootstrapType)) {
6668
throw new ArgumentError('Class passed to `${bootstrapMethodName}` must '
6769
'be a @${_types.componentAnnotation.name}');
@@ -71,7 +73,7 @@ class _ParseBootstrapTypeVisitor extends SimpleAstVisitor<Object> {
7173

7274
/// Recursively visits all nodes in an Ast structure, recording all encountered
7375
/// calls to the provided [FunctionElement].
74-
class _FindFunctionVisitor extends UnifyingAstVisitor<Object> {
76+
class _FindFunctionVisitor extends RecursiveAstVisitor<Object> {
7577
final FunctionElement _target;
7678
_FindFunctionVisitor(this._target);
7779

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
library angular2.src.transform;
2+
3+
import 'package:analyzer/src/generated/ast.dart';
4+
import 'package:analyzer/src/generated/element.dart';
5+
import 'package:analyzer/src/generated/java_core.dart';
6+
import 'package:barback/barback.dart';
7+
import 'package:code_transformers/resolver.dart';
8+
import 'package:dart_style/dart_style.dart';
9+
import 'package:path/path.dart' as path;
10+
11+
import 'codegen.dart';
12+
import 'logging.dart';
13+
import 'resolvers.dart';
14+
15+
/// Finds all calls to the Angular2 [ReflectionCapabilities] constructor
16+
/// defined in [library].
17+
/// This only searches the code defined in the file
18+
// represented by [library], not `part`s, `import`s, `export`s, etc.
19+
String findReflectionCapabilities(
20+
Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) {
21+
var types = new Angular2Types(resolver);
22+
if (types.reflectionCapabilities == null) {
23+
throw new ArgumentError(
24+
'Could not find class for ${reflectionCapabilitiesTypeName}.');
25+
}
26+
27+
var codegen = new _SetupReflectionCodegen(
28+
resolver, reflectionEntryPoint, newEntryPoint);
29+
30+
var writer = new PrintStringWriter();
31+
var visitor = new _RewriteReflectionEntryPointVisitor(
32+
writer, types.reflectionCapabilities, codegen);
33+
34+
// TODO(kegluneq): Determine how to get nodes without querying Element#node.
35+
// Root of file defining that library (main part).
36+
resolver.getLibrary(reflectionEntryPoint).definingCompilationUnit.node
37+
.accept(visitor);
38+
39+
return new DartFormatter().format(writer.toString());
40+
}
41+
42+
class _SetupReflectionCodegen {
43+
static const _prefixBase = 'ngStaticInit';
44+
45+
final String prefix;
46+
final String importUri;
47+
48+
_SetupReflectionCodegen._internal(this.prefix, this.importUri);
49+
50+
factory _SetupReflectionCodegen(
51+
Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) {
52+
var lib = resolver.getLibrary(reflectionEntryPoint);
53+
var prefix = _prefixBase;
54+
var idx = 0;
55+
while (lib.imports.any((import) {
56+
return import.prefix != null && import.prefix == prefix;
57+
})) {
58+
prefix = '${_prefixBase}${idx++}';
59+
}
60+
61+
var importPath = path.relative(newEntryPoint.path,
62+
from: path.dirname(reflectionEntryPoint.path));
63+
return new _SetupReflectionCodegen._internal(prefix, importPath);
64+
}
65+
66+
/// Generates code to import the library containing the method which sets up
67+
/// Angular2 reflection statically.
68+
///
69+
/// The code generated here should follow the example of code generated for
70+
/// an [ImportDirective] node.
71+
String codegenImport() {
72+
return 'import \'${importUri}\' as ${prefix};';
73+
}
74+
75+
/// Generates code to call the method which sets up Angular2 reflection
76+
/// statically.
77+
///
78+
/// The code generated here should follow the example of code generated for
79+
/// a [MethodInvocation] node, that is, it should be prefixed as necessary
80+
/// and not be followed by a ';'.
81+
String codegenSetupReflectionCall() {
82+
return '${prefix}.${setupReflectionMethodName}()';
83+
}
84+
}
85+
86+
class _RewriteReflectionEntryPointVisitor extends ToSourceVisitor {
87+
final PrintWriter _writer;
88+
final ClassElement _forbiddenClass;
89+
final _SetupReflectionCodegen _codegen;
90+
91+
_RewriteReflectionEntryPointVisitor(
92+
PrintWriter writer, this._forbiddenClass, this._codegen)
93+
: _writer = writer,
94+
super(writer);
95+
96+
bool _isNewReflectionCapabilities(InstanceCreationExpression node) {
97+
var typeElement = node.constructorName.type.name.bestElement;
98+
return typeElement != null && typeElement == _forbiddenClass;
99+
}
100+
101+
bool _isReflectionCapabilitiesImport(ImportDirective node) {
102+
return node.uriElement == _forbiddenClass.library;
103+
}
104+
105+
@override
106+
Object visitImportDirective(ImportDirective node) {
107+
if (_isReflectionCapabilitiesImport(node)) {
108+
// TODO(kegluneq): Remove newlines once dart_style bug is fixed.
109+
// https://github.com/dart-lang/dart_style/issues/178
110+
// _writer.print('\n/* ReflectionCapabilities import removed */\n');
111+
_writer.print(_codegen.codegenImport());
112+
// TODO(kegluneq): Remove once we generate all needed code.
113+
{
114+
super.visitImportDirective(node);
115+
}
116+
return null;
117+
}
118+
return super.visitImportDirective(node);
119+
}
120+
121+
@override
122+
Object visitAssignmentExpression(AssignmentExpression node) {
123+
if (node.rightHandSide is InstanceCreationExpression &&
124+
_isNewReflectionCapabilities(node.rightHandSide)) {
125+
// TODO(kegluneq): Remove newlines once dart_style bug is fixed.
126+
// https://github.com/dart-lang/dart_style/issues/178
127+
// _writer.print('/* Creation of ReflectionCapabilities removed */\n');
128+
_writer.print(_codegen.codegenSetupReflectionCall());
129+
130+
// TODO(kegluneq): Remove once we generate all needed code.
131+
{
132+
_writer.print(';');
133+
node.leftHandSide.accept(this);
134+
_writer.print(' ${node.operator.lexeme} ');
135+
super.visitInstanceCreationExpression(node.rightHandSide);
136+
}
137+
return null;
138+
}
139+
return super.visitAssignmentExpression(node);
140+
}
141+
142+
@override
143+
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
144+
if (_isNewReflectionCapabilities(node)) {
145+
logger.error('Unexpected format in creation of '
146+
'${reflectionCapabilitiesTypeName}');
147+
} else {
148+
return super.visitInstanceCreationExpression(node);
149+
}
150+
return null;
151+
}
152+
}

modules/angular2/src/transform/html_transform.dart

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,41 @@
11
library angular2.src.transform;
22

3-
import 'package:path/path.dart' as path;
3+
const entryPointParam = 'entry_point';
4+
const reflectionEntryPointParam = 'reflection_entry_point';
5+
const newEntryPointParam = 'new_entry_point';
46

57
/// Provides information necessary to transform an Angular2 app.
68
class TransformerOptions {
79
/// The file where the application's call to [bootstrap] is.
8-
// TODO(kegluenq): Allow multiple bootstrap entry points.
9-
final String bootstrapEntryPoint;
10-
11-
/// The Dart entry point, that is, where the initial call to [main] occurs.
10+
// TODO(kegluneq): Allow multiple entry points.
1211
final String entryPoint;
1312

13+
/// The reflection entry point, that is, where the
14+
/// application's [ReflectionCapabilities] are set.
15+
final String reflectionEntryPoint;
16+
1417
/// The path where we should generate code.
1518
final String newEntryPoint;
1619

17-
/// The html file that includes [entryPoint].
18-
final String htmlEntryPoint;
19-
20-
TransformerOptions(this.bootstrapEntryPoint, this.entryPoint,
21-
this.newEntryPoint, this.htmlEntryPoint);
20+
TransformerOptions._internal(
21+
this.entryPoint, this.reflectionEntryPoint, this.newEntryPoint);
2222

23-
bool inSameTopLevelDir() {
24-
var expectedDir = path.split(htmlEntryPoint)[0];
25-
return (expectedDir == path.split(entryPoint)[0] &&
26-
expectedDir == path.split(newEntryPoint)[0]);
23+
factory TransformerOptions(String entryPoint,
24+
{String reflectionEntryPoint, String newEntryPoint}) {
25+
if (entryPoint == null || entryPoint.isEmpty) {
26+
throw new ArgumentError.notNull(entryPointParam);
27+
}
28+
if (reflectionEntryPoint == null || entryPoint.isEmpty) {
29+
reflectionEntryPoint = entryPoint;
30+
}
31+
if (newEntryPoint == null || newEntryPoint.isEmpty) {
32+
newEntryPoint =
33+
reflectionEntryPoint.replaceFirst('.dart', '.bootstrap.dart');
34+
if (newEntryPoint == reflectionEntryPoint) {
35+
newEntryPoint = 'bootstrap.${newEntryPoint}';
36+
}
37+
}
38+
return new TransformerOptions._internal(
39+
entryPoint, reflectionEntryPoint, newEntryPoint);
2740
}
2841
}

modules/angular2/src/transform/resolvers.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Resolvers createResolvers() {
4747
}
4848

4949
const bootstrapMethodName = 'bootstrap';
50+
const reflectionCapabilitiesTypeName = 'ReflectionCapabilities';
5051

5152
/// Provides resolved [Elements] for well-known Angular2 symbols.
5253
class Angular2Types {
@@ -57,6 +58,8 @@ class Angular2Types {
5758
new AssetId('angular2', 'lib/src/core/application.dart');
5859
static final _templateLibAssetId =
5960
new AssetId('angular2', 'lib/src/core/annotations/template.dart');
61+
static final _reflectionCapabilitiesLibAssetId = new AssetId(
62+
'angular2', 'lib/src/reflection/reflection_capabilities.dart');
6063

6164
final Resolver _resolver;
6265
FunctionElement _bootstrapMethod;
@@ -84,6 +87,12 @@ class Angular2Types {
8487

8588
ClassElement get templateAnnotation => _getTypeSafe(templateLib, 'Template');
8689

90+
LibraryElement get reflectionCapabilitiesLib =>
91+
_resolver.getLibrary(_reflectionCapabilitiesLibAssetId);
92+
93+
ClassElement get reflectionCapabilities =>
94+
_getTypeSafe(reflectionCapabilitiesLib, reflectionCapabilitiesTypeName);
95+
8796
LibraryElement get applicationLib =>
8897
_resolver.getLibrary(_applicationLibAssetId);
8998

0 commit comments

Comments
 (0)