Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: modules/angular2/src/transform/codegen.dart

Issue 927373004: Initial commit of Dart transformer to generate constructor stubs, see https://github.com/angular/an… (Closed) Base URL: https://github.com/kegluneq/angular.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 library angular2.transformer;
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' show AssetId, TransformLogger;
7 import 'package:dart_style/dart_style.dart';
8 import 'package:path/path.dart' as path;
9
10 import 'annotation_processor.dart';
11
12 /// Base class that maintains codegen state.
13 class Context {
14 final TransformLogger _logger;
15 /// Maps libraries to the import prefixes we will use in the newly
16 /// generated code.
17 final Map<LibraryElement, String> _libraryPrefixes;
18
19 DirectiveRegistry _directiveRegistry;
jakemac 2015/02/18 16:18:50 might be nice to have a comment here
tjblasi 2015/02/18 21:18:25 Done.
20 DirectiveRegistry get directiveRegistry => _directiveRegistry;
21
22 Context({TransformLogger logger})
23 : _logger = logger,
24 _libraryPrefixes = {} {
25 _directiveRegistry = new _DirectiveRegistryImpl(this);
26 }
27
28 void error(String errorString) {
29 if (_logger != null) {
jakemac 2015/02/17 23:46:45 nit, you could rewrite this: if (_logger == null)
tjblasi 2015/02/18 21:18:25 Done.
30 _logger.error(errorString);
31 } else {
32 throw new Error(errorString);
33 }
34 }
35
36 /// If elements in [lib] should be prefixed in our generated code, returns
37 /// the appropriate prefix followed by a `.`. Future items from the same
38 /// library will use the same prefix.
39 /// If [lib] does not need a prefix, returns the empty string.
40 String _getPrefixDot(LibraryElement lib) {
41 var prefix = lib != null && !lib.isInSdk
jakemac 2015/02/18 16:18:50 I might reorganize this a little bit to return ear
tjblasi 2015/02/18 21:18:25 Done.
42 ? _libraryPrefixes.putIfAbsent(lib, () => 'i${_libraryPrefixes.length}')
43 : null;
44 return prefix == null ? '' : '${prefix}.';
45 }
46 }
47
48 abstract class DirectiveRegistry {
jakemac 2015/02/18 16:18:50 A comment here would be good
tjblasi 2015/02/18 21:18:25 Done.
49 // Adds [entry] to the `registerType` calls which will be generated.
50 void register(AnnotationMatch entry);
51 }
52
53 const _reflectorImport =
jakemac 2015/02/18 16:18:50 You might just want to use ''' here
tjblasi 2015/02/18 21:18:25 Done.
54 'import \'package:angular2/src/reflection/reflection.dart\' '
55 'show reflector;';
56
57 /// Default implementation to map from [LibraryElement] to [AssetId]. This
58 /// assumes that [el.source] has a getter called [assetId].
59 AssetId _assetIdFromLibraryElement(LibraryElement el) {
60 return (el.source as dynamic).assetId;
61 }
62
63 String codegenEntryPoint(Context context,
64 {LibraryElement entryPoint, AssetId newEntryPoint}) {
65 // This must be called prior to [codegenImports] or the entry point
66 // library will not be imported.
67 var entryPointPrefix = context._getPrefixDot(entryPoint);
68 // TODO(jakemac): copyright and library declaration
69 var outBuffer = new StringBuffer(_reflectorImport);
70 _codegenImports(context, newEntryPoint, outBuffer);
71 outBuffer
72 ..write('main() {')
73 ..write(context.directiveRegistry.toString())
74 ..write('${entryPointPrefix}main();}');
75
76 return new DartFormatter().format(outBuffer.toString());
77 }
78
79 String _codegenImports(
80 Context context, AssetId newEntryPoint, StringBuffer buffer) {
81 context._libraryPrefixes.forEach((lib, prefix) {
82 buffer
83 ..write(_codegenImport(
84 context, _assetIdFromLibraryElement(lib), newEntryPoint))
85 ..writeln('as ${prefix};');
86 });
87 }
88
89 _codegenImport(Context context, AssetId libraryId, AssetId entryPoint) {
90 if (libraryId.path.startsWith('lib/')) {
91 var packagePath = libraryId.path.replaceFirst('lib/', '');
92 return "import 'package:${libraryId.package}/${packagePath}'";
93 } else if (libraryId.package != entryPoint.package) {
94 context._error("Can't import `${libraryId}` from `${entryPoint}`");
95 } else if (path.url.split(libraryId.path)[0] ==
96 path.url.split(entryPoint.path)[0]) {
97 var relativePath =
98 path.relative(libraryId.path, from: path.dirname(entryPoint.path));
99 return "import '${relativePath}'";
100 } else {
101 context._error("Can't import `${libraryId}` from `${entryPoint}`");
102 }
103 }
104
105 class _DirectiveRegistryImpl implements DirectiveRegistry {
106 final Context _context;
107 final StringBuffer _buffer = new StringBuffer();
108
109 _DirectiveRegistryImpl(this._context);
110
111 @override
112 String toString() {
113 return _buffer.isEmpty ? '' : 'reflector${_buffer};';
114 }
115
116 // Adds [entry] to the `registerType` calls which will be generated.
117 void register(AnnotationMatch entry) {
118 var element = entry.element;
119 var annotation = entry.annotation;
120
121 if (annotation.element is! ConstructorElement) {
122 _context._error('Unsupported annotation type. '
123 'Only constructors are supported as Directives.');
124 return;
125 }
126 if (element is! ClassElement) {
127 _context._error('Directives can only be applied to classes.');
128 return;
129 }
130 if (element.node is! ClassDeclaration) {
jakemac 2015/02/17 23:46:46 Just fyi, any call to `.node` is potentially prett
tjblasi 2015/02/18 21:18:25 Added an issue [https://github.com/kegluneq/angula
131 _context._error('Unsupported annotation type. '
132 'Only class declarations are supported as Directives.');
133 return;
134 }
135 final ConstructorElement ctor = element.unnamedConstructor;
136 if (ctor == null) {
137 _context._error('No default constructor found for ${element.name}');
138 return;
139 }
140
141 _buffer.writeln('..registerType(${_codegenClassTypeString(element)}, {'
142 '"factory": ${_codegenFactoryProp(ctor)},'
143 '"parameters": ${_codegenParametersProp(ctor)},'
144 '"annotations": ${_codegenAnnotationsProp(element)}'
145 '})');
146 }
147
148 String _codegenClassTypeString(ClassElement el) {
149 return '${_context._getPrefixDot(el.library)}${el.name}';
150 }
151
152 /// Creates the 'annotations' property for the Angular2 [registerType] call
153 /// for [el].
154 String _codegenAnnotationsProp(ClassElement el) {
155 var writer = new PrintStringWriter();
156 var visitor = new _AnnotationsTransformVisitor(writer, _context);
157 el.node.accept(visitor);
158 return writer.toString();
159 }
160
161 /// Creates the 'factory' property for the Angular2 [registerType] call
162 /// for [ctor].
163 String _codegenFactoryProp(ConstructorElement ctor) {
164 if (ctor.node == null) {
165 // This occurs when the class does not declare a constructor.
166 var prefix = _context._getPrefixDot(ctor.type.element.library);
167 return '() => new ${prefix}${ctor.enclosingElement.displayName}()';
168 } else {
169 var writer = new PrintStringWriter();
170 var visitor = new _FactoryTransformVisitor(writer, _context);
171 ctor.node.accept(visitor);
172 return writer.toString();
173 }
174 }
175
176 /// Creates the 'parameters' property for the Angular2 [registerType] call
177 /// for [ctor].
178 String _codegenParametersProp(ConstructorElement ctor) {
179 if (ctor.node == null) {
180 // This occurs when the class does not declare a constructor.
181 return 'const [const []]';
182 } else {
183 var writer = new PrintStringWriter();
184 var visitor = new _ParameterTransformVisitor(writer, _context);
185 ctor.node.accept(visitor);
186 return writer.toString();
187 }
188 }
189 }
190
191 /// Visitor providing common methods for concrete implementations.
192 abstract class _TransformVisitor extends ToSourceVisitor {
193 final Context _context;
194 final PrintWriter _writer;
195
196 _TransformVisitor(PrintWriter writer, this._context)
197 : this._writer = writer,
198 super(writer);
199
200 /// Safely visit the given node.
201 /// @param node the node to be visited
jakemac 2015/02/17 23:46:45 I haven't really seen us use these JavaDoc style c
tjblasi 2015/02/18 21:18:25 Ah, I stole this code from analyzer, which was gen
202 void _visitNode(AstNode node) {
203 if (node != null) {
204 node.accept(this);
205 }
206 }
207
208 /**
jakemac 2015/02/17 23:46:46 Usually we just do the /// style comments, not sur
tjblasi 2015/02/18 21:18:25 Done.
209 * Safely visit the given node, printing the prefix before the node if it is n on-`null`.
210 *
211 * @param prefix the prefix to be printed if there is a node to visit
212 * @param node the node to be visited
213 */
214 void _visitNodeWithPrefix(String prefix, AstNode node) {
215 if (node != null) {
216 _writer.print(prefix);
217 node.accept(this);
218 }
219 }
220
221 /**
222 * Safely visit the given node, printing the suffix after the node if it is no n-`null`.
223 *
224 * @param suffix the suffix to be printed if there is a node to visit
225 * @param node the node to be visited
226 */
227 void _visitNodeWithSuffix(AstNode node, String suffix) {
228 if (node != null) {
229 node.accept(this);
230 _writer.print(suffix);
231 }
232 }
233
234 @override
235 Object visitSimpleIdentifier(SimpleIdentifier node) {
236 // Make sure the identifier is prefixed if necessary.
237 if (node.bestElement is ClassElementImpl ||
238 node.bestElement is PropertyAccessorElement) {
239 _writer
240 ..print(_context._getPrefixDot(node.bestElement.library))
241 ..print(node.token.lexeme);
242 } else {
243 return super.visitSimpleIdentifier(node);
244 }
245 return null;
246 }
247 }
248
249 /// SourceVisitor designed to accept [ConstructorDeclaration] nodes.
250 class _CtorTransformVisitor extends _TransformVisitor {
251 bool _withParameterTypes = true;
252 bool _withParameterNames = true;
253
254 _CtorTransformVisitor(PrintWriter writer, Context _context)
255 : super(writer, _context);
256
257 /// If [_withParameterTypes] is true, this method outputs [node]'s type
258 /// (appropriately prefixed based on [_libraryPrefixes]. If
259 /// [_withParameterNames] is true, this method outputs [node]'s identifier.
260 Object _visitNormalFormalParameter(NormalFormalParameter node) {
261 if (_withParameterTypes) {
262 var paramType = node.element.type;
263 var prefix = _context._getPrefixDot(paramType.element.library);
264 _writer.print('${prefix}${paramType.displayName}');
265 if (_withParameterNames) {
266 _visitNodeWithPrefix(' ', node.identifier);
267 }
268 } else if (_withParameterNames) {
269 _visitNode(node.identifier);
270 }
271 return null;
272 }
273
274 @override
275 Object visitSimpleFormalParameter(SimpleFormalParameter node) {
276 return _visitNormalFormalParameter(node);
277 }
278
279 @override
280 Object visitFieldFormalParameter(FieldFormalParameter node) {
281 if (node.parameters != null) {
282 _context.error('Parameters in ctor not supported '
283 '(${super.visitFormalParameterList(node)}');
284 }
285 return _visitNormalFormalParameter(node);
286 }
287
288 @override
289 Object visitDefaultFormalParameter(DefaultFormalParameter node) {
290 _visitNode(node.parameter);
291 return null;
292 }
293
294 @override
295 /// Overridden to avoid outputting grouping operators for default parameters.
296 Object visitFormalParameterList(FormalParameterList node) {
297 _writer.print('(');
298 NodeList<FormalParameter> parameters = node.parameters;
299 int size = parameters.length;
300 for (int i = 0; i < size; i++) {
301 if (i > 0) {
302 _writer.print(', ');
303 }
304 parameters[i].accept(this);
305 }
306 _writer.print(')');
307 return null;
308 }
309 }
310
311 /// ToSourceVisitor designed to print 'parameters' values for Angular2's
312 /// [registerType] calls.
313 class _ParameterTransformVisitor extends _CtorTransformVisitor {
314 _ParameterTransformVisitor(PrintWriter writer, Context _context)
315 : super(writer, _context);
316
317 @override
318 Object visitConstructorDeclaration(ConstructorDeclaration node) {
319 _withParameterNames = false;
320 _withParameterTypes = true;
321 _writer.print('const [const [');
322 _visitNode(node.parameters);
323 _writer.print(']]');
324 return null;
325 }
326
327 @override
328 Object visitFormalParameterList(FormalParameterList node) {
329 NodeList<FormalParameter> parameters = node.parameters;
330 int size = parameters.length;
331 for (int i = 0; i < size; i++) {
332 if (i > 0) {
333 _writer.print(', ');
334 }
335 parameters[i].accept(this);
336 }
337 return null;
338 }
339 }
340
341 /// ToSourceVisitor designed to print 'factory' values for Angular2's
342 /// [registerType] calls.
343 class _FactoryTransformVisitor extends _CtorTransformVisitor {
344 _FactoryTransformVisitor(PrintWriter writer, Context _context)
345 : super(writer, _context);
346
347 @override
348 Object visitConstructorDeclaration(ConstructorDeclaration node) {
349 _withParameterNames = true;
350 _withParameterTypes = true;
351 _visitNode(node.parameters);
352 _writer.print(' => new ');
353 _visitNode(node.returnType);
354 _visitNodeWithPrefix(".", node.name);
355 _withParameterTypes = false;
356 _visitNode(node.parameters);
357 return null;
358 }
359 }
360
361 /// ToSourceVisitor designed to print a [ClassDeclaration] node as a
362 /// 'annotations' value for Angular2's [registerType] calls.
363 class _AnnotationsTransformVisitor extends _TransformVisitor {
364 _AnnotationsTransformVisitor(PrintWriter writer, Context _context)
365 : super(writer, _context);
366
367 @override
368 Object visitClassDeclaration(ClassDeclaration node) {
369 _writer.print('const [');
370 var size = node.metadata.length;
371 for (var i = 0; i < size; ++i) {
372 if (i > 0) {
373 _writer.print(', ');
374 }
375 node.metadata[i].accept(this);
376 }
377 _writer.print(']');
378 return null;
379 }
380
381 @override
382 Object visitAnnotation(Annotation node) {
383 _writer.print('const ');
384 _visitNode(node.name);
385 // TODO(tjblasi): Do we need to handle named constructors for annotations?
386 // _visitNodeWithPrefix(".", node.constructorName);
387 _visitNode(node.arguments);
388 return null;
389 }
390 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698