OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 library initialize.transformer; | 4 library initialize.transformer; |
5 | 5 |
6 import 'dart:async'; | 6 import 'dart:async'; |
7 import 'dart:collection' show Queue; | 7 import 'dart:collection' show Queue; |
8 import 'package:analyzer/src/generated/ast.dart'; | 8 import 'package:analyzer/src/generated/ast.dart'; |
9 import 'package:analyzer/src/generated/element.dart'; | 9 import 'package:analyzer/src/generated/element.dart'; |
10 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
11 import 'package:code_transformers/assets.dart'; | 11 import 'package:code_transformers/assets.dart'; |
12 import 'package:code_transformers/resolver.dart'; | 12 import 'package:code_transformers/resolver.dart'; |
13 import 'package:glob/glob.dart'; | 13 import 'package:glob/glob.dart'; |
14 import 'package:html5lib/dom.dart' as dom; | 14 import 'package:html5lib/dom.dart' as dom; |
15 import 'package:html5lib/parser.dart' show parse; | 15 import 'package:html5lib/parser.dart' show parse; |
16 import 'package:path/path.dart' as path; | 16 import 'package:path/path.dart' as path; |
17 | 17 |
18 /// Removes the mirror-based initialization logic and replaces it with static | 18 import 'build/initializer_plugin.dart'; |
19 /// logic. | 19 export 'build/initializer_plugin.dart'; |
20 | |
21 /// Create a new [Asset] which inlines your [Initializer] annotations into | |
22 /// a new file that bootstraps your application. | |
23 Asset bootstrapInitializers(Resolver resolver, Transform transform, | |
Siggi Cherem (dart-lang)
2015/02/13 16:45:37
looking at the other CL, I think we should rename
jakemac
2015/02/13 20:16:05
chose generateBootstrapFile
| |
24 AssetId primaryAssetId, AssetId newEntryPointId, | |
25 {bool errorIfNotFound: true, List<InitializerPlugin> plugins, | |
26 bool appendDefaultPlugin: true}) { | |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
I wonder if we need this, or we should just let th
jakemac
2015/02/13 20:16:05
I think it would be an extremely rare case where w
| |
27 if (appendDefaultPlugin) { | |
28 if (plugins == null) plugins = []; | |
29 plugins.add(const DefaultInitializerPlugin()); | |
30 } | |
31 return new _BootstrapFileBuilder( | |
32 resolver, transform, primaryAssetId, newEntryPointId, errorIfNotFound, | |
33 plugins: plugins).run(); | |
34 } | |
35 | |
36 /// Transformer which removes the mirror-based initialization logic and replaces | |
37 /// it with static logic. | |
20 class InitializeTransformer extends Transformer { | 38 class InitializeTransformer extends Transformer { |
21 final Resolvers _resolvers; | 39 final Resolvers _resolvers; |
22 final Iterable<Glob> _entryPointGlobs; | 40 final Iterable<Glob> _entryPointGlobs; |
23 final bool _errorIfNotFound; | 41 final bool _errorIfNotFound; |
42 final List<InitializerPlugin> plugins; | |
24 | 43 |
25 InitializeTransformer(List<String> entryPoints, {bool errorIfNotFound: true}) | 44 InitializeTransformer(List<String> entryPoints, |
45 {bool errorIfNotFound: true, this.plugins}) | |
26 : _entryPointGlobs = entryPoints.map((e) => new Glob(e)), | 46 : _entryPointGlobs = entryPoints.map((e) => new Glob(e)), |
27 _errorIfNotFound = errorIfNotFound, | 47 _errorIfNotFound = errorIfNotFound, |
28 _resolvers = new Resolvers.fromMock({ | 48 _resolvers = new Resolvers.fromMock({ |
29 // The list of types below is derived from: | 49 // The list of types below is derived from: |
30 // * types that are used internally by the resolver (see | 50 // * types that are used internally by the resolver (see |
31 // _initializeFrom in resolver.dart). | 51 // _initializeFrom in resolver.dart). |
32 // TODO(jakemac): Move this into code_transformers so it can be shared . | 52 // TODO(jakemac): Move this into code_transformers so it can be shared . |
33 'dart:core': ''' | 53 'dart:core': ''' |
34 library dart.core; | 54 library dart.core; |
35 class Object {} | 55 class Object {} |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
99 var newEntryPointId = new AssetId(primaryId.package, | 119 var newEntryPointId = new AssetId(primaryId.package, |
100 '${path.url.withoutExtension(primaryId.path)}.initialize.dart'); | 120 '${path.url.withoutExtension(primaryId.path)}.initialize.dart'); |
101 return transform.hasInput(newEntryPointId).then((exists) { | 121 return transform.hasInput(newEntryPointId).then((exists) { |
102 if (exists) { | 122 if (exists) { |
103 transform.logger | 123 transform.logger |
104 .error('New entry point file $newEntryPointId already exists.'); | 124 .error('New entry point file $newEntryPointId already exists.'); |
105 return null; | 125 return null; |
106 } | 126 } |
107 | 127 |
108 return _resolvers.get(transform, [primaryId]).then((resolver) { | 128 return _resolvers.get(transform, [primaryId]).then((resolver) { |
109 new _BootstrapFileBuilder(resolver, transform, primaryId, | 129 transform.addOutput(bootstrapInitializers( |
110 newEntryPointId, _errorIfNotFound).run(); | 130 resolver, transform, primaryId, newEntryPointId, |
131 errorIfNotFound: _errorIfNotFound, plugins: plugins)); | |
111 resolver.release(); | 132 resolver.release(); |
112 return newEntryPointId; | 133 return newEntryPointId; |
113 }); | 134 }); |
114 }); | 135 }); |
115 } | 136 } |
137 // Finds the first (and only) dart script on an html page and returns the | |
138 // [AssetId] that points to it | |
139 AssetId _findMainScript( | |
140 dom.Document document, AssetId entryPoint, Transform transform) { | |
141 var scripts = document.querySelectorAll('script[type="application/dart"]'); | |
142 if (scripts.length != 1) { | |
143 transform.logger.error('Expected exactly one dart script in $entryPoint ' | |
144 'but found ${scripts.length}.'); | |
145 return null; | |
146 } | |
147 | |
148 var src = scripts[0].attributes['src']; | |
149 // TODO(jakemac): Support inline scripts, | |
150 // https://github.com/dart-lang/initialize/issues/20 | |
151 if (src == null) { | |
152 transform.logger.error('Inline scripts are not supported at this time.'); | |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
we could also point to the bug number here so peop
jakemac
2015/02/13 20:16:06
Done.
| |
153 return null; | |
154 } | |
155 | |
156 return uriToAssetId( | |
157 entryPoint, src, transform.logger, scripts[0].sourceSpan); | |
158 } | |
116 | 159 |
117 // Replaces script tags pointing to [originalDartFile] with [newDartFile] in | 160 // Replaces script tags pointing to [originalDartFile] with [newDartFile] in |
118 // [entryPoint]. | 161 // [entryPoint]. |
119 void _replaceEntryWithBootstrap(Transform transform, dom.Document document, | 162 void _replaceEntryWithBootstrap(Transform transform, dom.Document document, |
120 AssetId entryPoint, AssetId originalDartFile, AssetId newDartFile) { | 163 AssetId entryPoint, AssetId originalDartFile, AssetId newDartFile) { |
121 var found = false; | 164 var found = false; |
122 var scripts = document | 165 var scripts = document |
123 .querySelectorAll('script[type="application/dart"]') | 166 .querySelectorAll('script[type="application/dart"]') |
124 .where((script) => uriToAssetId(entryPoint, script.attributes['src'], | 167 .where((script) => uriToAssetId(entryPoint, script.attributes['src'], |
125 transform.logger, script.sourceSpan) == originalDartFile) | 168 transform.logger, script.sourceSpan) == |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
formatter bug? yikes!
jakemac
2015/02/13 20:16:06
Ya this is pretty bad :(. I broke down and changed
| |
169 originalDartFile) | |
126 .toList(); | 170 .toList(); |
127 | 171 |
128 if (scripts.length != 1) { | 172 if (scripts.length != 1) { |
129 transform.logger.error( | 173 transform.logger.error( |
130 'Expected exactly one script pointing to $originalDartFile in ' | 174 'Expected exactly one script pointing to $originalDartFile in ' |
131 '$entryPoint, but found ${scripts.length}.'); | 175 '$entryPoint, but found ${scripts.length}.'); |
132 return; | 176 return; |
133 } | 177 } |
134 scripts[0].attributes['src'] = path.url.relative(newDartFile.path, | 178 scripts[0].attributes['src'] = path.url.relative(newDartFile.path, |
135 from: path.dirname(entryPoint.path)); | 179 from: path.dirname(entryPoint.path)); |
136 transform.addOutput(new Asset.fromString(entryPoint, document.outerHtml)); | 180 transform.addOutput(new Asset.fromString(entryPoint, document.outerHtml)); |
137 } | 181 } |
138 | |
139 AssetId _findMainScript( | |
140 dom.Document document, AssetId entryPoint, Transform transform) { | |
141 var scripts = document.querySelectorAll('script[type="application/dart"]'); | |
142 if (scripts.length != 1) { | |
143 transform.logger.error('Expected exactly one dart script in $entryPoint ' | |
144 'but found ${scripts.length}.'); | |
145 return null; | |
146 } | |
147 | |
148 var src = scripts[0].attributes['src']; | |
149 // TODO(jakemac): Support inline scripts, | |
150 // https://github.com/dart-lang/initialize/issues/20 | |
151 if (src == null) { | |
152 transform.logger.error('Inline scripts are not supported at this time.'); | |
153 return null; | |
154 } | |
155 | |
156 return uriToAssetId( | |
157 entryPoint, src, transform.logger, scripts[0].sourceSpan); | |
158 } | |
159 } | 182 } |
160 | 183 |
184 // Class which builds a bootstrap file. | |
161 class _BootstrapFileBuilder { | 185 class _BootstrapFileBuilder { |
162 final Resolver _resolver; | 186 final Resolver _resolver; |
163 final Transform _transform; | 187 final Transform _transform; |
164 final bool _errorIfNotFound; | 188 final bool _errorIfNotFound; |
165 AssetId _entryPoint; | 189 AssetId _entryPoint; |
166 AssetId _newEntryPoint; | 190 AssetId _newEntryPoint; |
167 | 191 |
168 /// The resolved initialize library. | 192 /// The resolved initialize library. |
169 LibraryElement _initializeLibrary; | 193 LibraryElement _initializeLibrary; |
170 /// The resolved Initializer class from the initialize library. | 194 /// The resolved Initializer class from the initialize library. |
171 ClassElement _initializer; | 195 ClassElement _initializer; |
172 | 196 |
173 /// Queue for intialization annotations. | 197 /// Queue for intialization annotations. |
174 final _initQueue = new Queue<_InitializerData>(); | 198 final _initQueue = new Queue<InitializerData>(); |
175 /// All the annotations we have seen for each element | 199 /// All the annotations we have seen for each element |
176 final _seenAnnotations = new Map<Element, Set<ElementAnnotation>>(); | 200 final _seenAnnotations = new Map<Element, Set<ElementAnnotation>>(); |
177 | 201 |
202 /// The list of [InitializerPlugin]s to apply. The first plugin which asks to | |
203 /// be applied to a given initializer is the only one that will apply. | |
204 List<InitializerPlugin> _plugins; | |
205 | |
178 TransformLogger _logger; | 206 TransformLogger _logger; |
179 | 207 |
180 _BootstrapFileBuilder(this._resolver, this._transform, this._entryPoint, | 208 _BootstrapFileBuilder(this._resolver, this._transform, this._entryPoint, |
181 this._newEntryPoint, this._errorIfNotFound) { | 209 this._newEntryPoint, this._errorIfNotFound, |
210 {List<InitializerPlugin> plugins}) { | |
182 _logger = _transform.logger; | 211 _logger = _transform.logger; |
183 _initializeLibrary = | 212 _initializeLibrary = |
184 _resolver.getLibrary(new AssetId('initialize', 'lib/initialize.dart')); | 213 _resolver.getLibrary(new AssetId('initialize', 'lib/initialize.dart')); |
185 if (_initializeLibrary != null) { | 214 if (_initializeLibrary != null) { |
186 _initializer = _initializeLibrary.getType('Initializer'); | 215 _initializer = _initializeLibrary.getType('Initializer'); |
187 } else if (_errorIfNotFound) { | 216 } else if (_errorIfNotFound) { |
188 _logger.warning('Unable to read "package:initialize/initialize.dart". ' | 217 _logger.warning('Unable to read "package:initialize/initialize.dart". ' |
189 'This file must be imported via $_entryPoint or a transitive ' | 218 'This file must be imported via $_entryPoint or a transitive ' |
190 'dependency.'); | 219 'dependency.'); |
191 } | 220 } |
221 _plugins = | |
222 plugins != null ? plugins : const [const DefaultInitializerPlugin()]; | |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
not sure if the const list here will be much cheap
jakemac
2015/02/13 20:16:05
left the const on the DefaultInitializerPlugin sin
| |
192 } | 223 } |
193 | 224 |
194 /// Adds the new entry point file to the transform. Should only be ran once. | 225 /// Creates and returns the new bootstrap file. |
195 void run() { | 226 Asset run() { |
196 var entryLib = _resolver.getLibrary(_entryPoint); | 227 var entryLib = _resolver.getLibrary(_entryPoint); |
197 _readLibraries(entryLib); | 228 _readLibraries(entryLib); |
198 | 229 |
199 _transform.addOutput( | 230 return new Asset.fromString(_newEntryPoint, _buildNewEntryPoint(entryLib)); |
200 new Asset.fromString(_newEntryPoint, _buildNewEntryPoint(entryLib))); | |
201 } | 231 } |
202 | 232 |
203 /// Reads Initializer annotations on this library and all its dependencies in | 233 /// Reads Initializer annotations on this library and all its dependencies in |
204 /// post-order. | 234 /// post-order. |
205 void _readLibraries(LibraryElement library, [Set<LibraryElement> seen]) { | 235 void _readLibraries(LibraryElement library, [Set<LibraryElement> seen]) { |
206 if (seen == null) seen = new Set<LibraryElement>(); | 236 if (seen == null) seen = new Set<LibraryElement>(); |
207 seen.add(library); | 237 seen.add(library); |
208 | 238 |
209 // Visit all our dependencies. | 239 // Visit all our dependencies. |
210 for (var importedLibrary in _sortedLibraryImports(library)) { | 240 for (var importedLibrary in _sortedLibraryImports(library)) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
245 return _isInitializer(e.variable.evaluationResult.value.type); | 275 return _isInitializer(e.variable.evaluationResult.value.type); |
246 } else if (e is ConstructorElement) { | 276 } else if (e is ConstructorElement) { |
247 return _isInitializer(e.returnType); | 277 return _isInitializer(e.returnType); |
248 } | 278 } |
249 return false; | 279 return false; |
250 }).where((ElementAnnotation meta) { | 280 }).where((ElementAnnotation meta) { |
251 _seenAnnotations.putIfAbsent(element, () => new Set<ElementAnnotation>()); | 281 _seenAnnotations.putIfAbsent(element, () => new Set<ElementAnnotation>()); |
252 return !_seenAnnotations[element].contains(meta); | 282 return !_seenAnnotations[element].contains(meta); |
253 }).forEach((ElementAnnotation meta) { | 283 }).forEach((ElementAnnotation meta) { |
254 _seenAnnotations[element].add(meta); | 284 _seenAnnotations[element].add(meta); |
255 _initQueue.addLast(new _InitializerData(element, meta)); | 285 _initQueue.addLast(new InitializerData(element, meta)); |
256 found = true; | 286 found = true; |
257 }); | 287 }); |
258 return found; | 288 return found; |
259 } | 289 } |
260 | 290 |
261 String _buildNewEntryPoint(LibraryElement entryLib) { | 291 String _buildNewEntryPoint(LibraryElement entryLib) { |
262 var importsBuffer = new StringBuffer(); | 292 var importsBuffer = new StringBuffer(); |
263 var initializersBuffer = new StringBuffer(); | 293 var initializersBuffer = new StringBuffer(); |
264 var libraryPrefixes = new Map<LibraryElement, String>(); | 294 var libraryPrefixes = new Map<LibraryElement, String>(); |
265 | 295 |
266 // Import the static_loader, initializer, and original entry point. | 296 // Import the static_loader, initializer, and original entry point. |
267 importsBuffer | 297 importsBuffer |
268 .writeln("import 'package:initialize/src/static_loader.dart';"); | 298 .writeln("import 'package:initialize/src/static_loader.dart';"); |
269 importsBuffer.writeln("import 'package:initialize/initialize.dart';"); | 299 importsBuffer.writeln("import 'package:initialize/initialize.dart';"); |
270 libraryPrefixes[entryLib] = 'i0'; | 300 libraryPrefixes[entryLib] = 'i0'; |
271 | 301 |
272 initializersBuffer.writeln(' initializers.addAll(['); | 302 initializersBuffer.writeln(' initializers.addAll(['); |
273 while (_initQueue.isNotEmpty) { | 303 while (_initQueue.isNotEmpty) { |
274 var next = _initQueue.removeFirst(); | 304 var next = _initQueue.removeFirst(); |
275 | 305 |
276 libraryPrefixes.putIfAbsent( | 306 libraryPrefixes.putIfAbsent( |
277 next.element.library, () => 'i${libraryPrefixes.length}'); | 307 next.element.library, () => 'i${libraryPrefixes.length}'); |
278 libraryPrefixes.putIfAbsent( | 308 libraryPrefixes.putIfAbsent(next.elementAnnotation.element.library, |
279 next.annotation.element.library, () => 'i${libraryPrefixes.length}'); | 309 () => 'i${libraryPrefixes.length}'); |
280 | 310 |
281 _writeInitializer(next, libraryPrefixes, initializersBuffer); | 311 // Run the first plugin which asks to be ran and then stop. |
312 var i = 0; | |
313 var pluginRan = false; | |
314 var pluginText; | |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
delete this one? (it's declared again inside the l
jakemac
2015/02/13 20:16:06
Done.
| |
315 while (!pluginRan && i < _plugins.length) { | |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
maybe we could simplify it like this:
var data
jakemac
2015/02/13 20:16:05
Done, this is much better for sure. I also added a
| |
316 var plugin = _plugins[i]; | |
317 var pluginData = new InitializerPluginData( | |
318 next, _newEntryPoint, libraryPrefixes, _resolver, _logger); | |
319 i++; | |
320 if (!plugin.shouldApply(pluginData)) continue; | |
321 var pluginText = plugin.apply(pluginData); | |
322 if (pluginText != null) { | |
323 initializersBuffer.writeln('$pluginText,'); | |
324 } | |
325 pluginRan = true; | |
326 } | |
282 } | 327 } |
283 initializersBuffer.writeln(' ]);'); | 328 initializersBuffer.writeln(' ]);'); |
284 | 329 |
285 libraryPrefixes | 330 libraryPrefixes |
286 .forEach((lib, prefix) => _writeImport(lib, prefix, importsBuffer)); | 331 .forEach((lib, prefix) => _writeImport(lib, prefix, importsBuffer)); |
287 | 332 |
288 // TODO(jakemac): copyright and library declaration | 333 // TODO(jakemac): copyright and library declaration |
289 return ''' | 334 return ''' |
290 $importsBuffer | 335 $importsBuffer |
291 main() { | 336 main() { |
(...skipping 15 matching lines...) Expand all Loading... | |
307 path.url.split(_newEntryPoint.path)[0]) { | 352 path.url.split(_newEntryPoint.path)[0]) { |
308 var relativePath = | 353 var relativePath = |
309 path.relative(id.path, from: path.dirname(_newEntryPoint.path)); | 354 path.relative(id.path, from: path.dirname(_newEntryPoint.path)); |
310 buffer.write("import '${relativePath}'"); | 355 buffer.write("import '${relativePath}'"); |
311 } else { | 356 } else { |
312 _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); | 357 _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); |
313 } | 358 } |
314 buffer.writeln(' as $prefix;'); | 359 buffer.writeln(' as $prefix;'); |
315 } | 360 } |
316 | 361 |
317 _writeInitializer(_InitializerData data, | |
318 Map<LibraryElement, String> libraryPrefixes, StringBuffer buffer) { | |
319 final annotationElement = data.annotation.element; | |
320 final element = data.element; | |
321 | |
322 final metaPrefix = libraryPrefixes[annotationElement.library]; | |
323 var elementString; | |
324 if (element is LibraryElement) { | |
325 var segments = element.source.uri.pathSegments; | |
326 var package = segments[0]; | |
327 var libraryPath; | |
328 var packageString; | |
329 if (_newEntryPoint.package == package && | |
330 _newEntryPoint.path.startsWith('${segments[1]}/')) { | |
331 // reset `package` to null, we will do a relative path in this case. | |
332 packageString = 'null'; | |
333 libraryPath = path.url.relative( | |
334 path.url.joinAll(segments.getRange(1, segments.length)), | |
335 from: path.url.dirname(path.url.join(_newEntryPoint.path))); | |
336 } else if (segments[1] == 'lib') { | |
337 packageString = "'$package'"; | |
338 libraryPath = path.url.joinAll(segments.getRange(2, segments.length)); | |
339 } else { | |
340 _logger.error('Unable to import `${element.source.uri.path}` from ' | |
341 '${_newEntryPoint.path}.'); | |
342 } | |
343 | |
344 elementString = "const LibraryIdentifier(" | |
345 "#${element.name}, $packageString, '$libraryPath')"; | |
346 } else if (element is ClassElement || element is FunctionElement) { | |
347 elementString = | |
348 '${libraryPrefixes[data.element.library]}.${element.name}'; | |
349 } else { | |
350 _logger.error('Initializers can only be applied to top level functins, ' | |
351 'libraries, and classes.'); | |
352 } | |
353 | |
354 if (annotationElement is ConstructorElement) { | |
355 var node = data.element.node; | |
356 List<Annotation> astMeta; | |
357 if (node is SimpleIdentifier) { | |
358 astMeta = node.parent.parent.metadata; | |
359 } else if (node is ClassDeclaration || node is FunctionDeclaration) { | |
360 astMeta = node.metadata; | |
361 } else { | |
362 _logger.error( | |
363 'Initializer annotations are only supported on libraries, classes, ' | |
364 'and top level methods. Found $node.'); | |
365 } | |
366 final annotation = | |
367 astMeta.firstWhere((m) => m.elementAnnotation == data.annotation); | |
368 final clazz = annotation.name; | |
369 final constructor = annotation.constructorName == null | |
370 ? '' | |
371 : '.${annotation.constructorName}'; | |
372 // TODO(jakemac): Support more than raw values here | |
373 // https://github.com/dart-lang/static_init/issues/5 | |
374 final args = _buildArgsString(annotation.arguments, libraryPrefixes); | |
375 buffer.write(''' | |
376 new InitEntry(const $metaPrefix.${clazz}$constructor$args, $elementString), | |
377 '''); | |
378 } else if (annotationElement is PropertyAccessorElement) { | |
379 buffer.write(''' | |
380 new InitEntry($metaPrefix.${annotationElement.name}, $elementString), | |
381 '''); | |
382 } else { | |
383 _logger.error('Unsupported annotation type. Only constructors and ' | |
384 'properties are supported as initializers.'); | |
385 } | |
386 } | |
387 | |
388 String _buildArgsString( | |
389 ArgumentList args, Map<LibraryElement, String> libraryPrefixes) { | |
390 var buffer = new StringBuffer(); | |
391 buffer.write('('); | |
392 var first = true; | |
393 for (var arg in args.arguments) { | |
394 if (!first) buffer.write(', '); | |
395 first = false; | |
396 | |
397 Expression expression; | |
398 if (arg is NamedExpression) { | |
399 buffer.write('${arg.name.label.name}: '); | |
400 expression = arg.expression; | |
401 } else { | |
402 expression = arg; | |
403 } | |
404 | |
405 buffer.write(_expressionString(expression, libraryPrefixes)); | |
406 } | |
407 buffer.write(')'); | |
408 return buffer.toString(); | |
409 } | |
410 | |
411 String _expressionString( | |
412 Expression expression, Map<LibraryElement, String> libraryPrefixes) { | |
413 var buffer = new StringBuffer(); | |
414 if (expression is StringLiteral) { | |
415 var value = expression.stringValue; | |
416 if (value == null) { | |
417 _logger.error('Only const strings are allowed in initializer ' | |
418 'expressions, found $expression'); | |
419 } | |
420 value = value.replaceAll(r'\', r'\\').replaceAll(r"'", r"\'"); | |
421 buffer.write("'$value'"); | |
422 } else if (expression is BooleanLiteral || | |
423 expression is DoubleLiteral || | |
424 expression is IntegerLiteral || | |
425 expression is NullLiteral) { | |
426 buffer.write('${expression}'); | |
427 } else if (expression is ListLiteral) { | |
428 buffer.write('const ['); | |
429 var first = true; | |
430 for (Expression listExpression in expression.elements) { | |
431 if (!first) buffer.write(', '); | |
432 first = false; | |
433 buffer.write(_expressionString(listExpression, libraryPrefixes)); | |
434 } | |
435 buffer.write(']'); | |
436 } else if (expression is MapLiteral) { | |
437 buffer.write('const {'); | |
438 var first = true; | |
439 for (MapLiteralEntry entry in expression.entries) { | |
440 if (!first) buffer.write(', '); | |
441 first = false; | |
442 buffer.write(_expressionString(entry.key, libraryPrefixes)); | |
443 buffer.write(': '); | |
444 buffer.write(_expressionString(entry.value, libraryPrefixes)); | |
445 } | |
446 buffer.write('}'); | |
447 } else if (expression is Identifier) { | |
448 var element = expression.bestElement; | |
449 if (element == null || !element.isPublic) { | |
450 _logger.error('Private constants are not supported in intializer ' | |
451 'constructors, found $element.'); | |
452 } | |
453 libraryPrefixes.putIfAbsent( | |
454 element.library, () => 'i${libraryPrefixes.length}'); | |
455 | |
456 buffer.write('${libraryPrefixes[element.library]}.'); | |
457 if (element is ClassElement) { | |
458 buffer.write(element.name); | |
459 } else if (element is PropertyAccessorElement) { | |
460 var variable = element.variable; | |
461 if (variable is FieldElement) { | |
462 buffer.write('${variable.enclosingElement.name}.'); | |
463 } | |
464 buffer.write('${variable.name}'); | |
465 } else { | |
466 _logger.error('Unsupported argument to initializer constructor.'); | |
467 } | |
468 } else { | |
469 _logger.error('Only literals and identifiers are allowed for initializer ' | |
470 'expressions, found $expression.'); | |
471 } | |
472 return buffer.toString(); | |
473 } | |
474 | |
475 bool _isInitializer(InterfaceType type) { | 362 bool _isInitializer(InterfaceType type) { |
476 // If `_initializer` wasn't found then it was never loaded (even | 363 // If `_initializer` wasn't found then it was never loaded (even |
477 // transitively), and so no annotations can be initializers. | 364 // transitively), and so no annotations can be initializers. |
478 if (_initializer == null) return false; | 365 if (_initializer == null) return false; |
479 if (type == null) return false; | 366 if (type == null) return false; |
480 if (type.element.type == _initializer.type) return true; | 367 if (type.element.type == _initializer.type) return true; |
481 if (_isInitializer(type.superclass)) return true; | 368 if (_isInitializer(type.superclass)) return true; |
482 for (var interface in type.interfaces) { | 369 for (var interface in type.interfaces) { |
483 if (_isInitializer(interface)) return true; | 370 if (_isInitializer(interface)) return true; |
484 } | 371 } |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
554 | 441 |
555 // And finally compare based on the relative uri if both are file paths. | 442 // And finally compare based on the relative uri if both are file paths. |
556 var aUri = path.relative(a.source.uri.path, | 443 var aUri = path.relative(a.source.uri.path, |
557 from: path.dirname(library.source.uri.path)); | 444 from: path.dirname(library.source.uri.path)); |
558 var bUri = path.relative(b.source.uri.path, | 445 var bUri = path.relative(b.source.uri.path, |
559 from: path.dirname(library.source.uri.path)); | 446 from: path.dirname(library.source.uri.path)); |
560 return aUri.compareTo(bUri); | 447 return aUri.compareTo(bUri); |
561 })).map((import) => import.importedLibrary); | 448 })).map((import) => import.importedLibrary); |
562 } | 449 } |
563 | 450 |
564 // Element/ElementAnnotation pair. | 451 /// Element/ElementAnnotation pair. |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
reword - maybe just focus on the fact that this is
jakemac
2015/02/13 20:16:06
Done.
| |
565 class _InitializerData { | 452 class InitializerData { |
566 final Element element; | 453 final Element element; |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
Since this class is now part of the plugin API, we
jakemac
2015/02/13 20:16:05
done, also renamed elementAnnotation -> annotation
| |
567 final ElementAnnotation annotation; | 454 final ElementAnnotation elementAnnotation; |
455 AstNode _node; | |
568 | 456 |
569 _InitializerData(this.element, this.annotation); | 457 // At least cache this for now, but provides a path to doing something better. |
Siggi Cherem (dart-lang)
2015/02/13 00:50:36
nit: add a "TODO(jackemac): " prefix here
jakemac
2015/02/13 20:16:05
Done.
| |
458 // Ideally `element` would actually be the getter, and `node` would be the | |
459 // property. | |
460 AstNode get node { | |
461 if (_node == null) _node = element.node; | |
462 return _node; | |
463 } | |
464 | |
465 Annotation get annotation { | |
466 var annotatedNode; | |
467 if (node is SimpleIdentifier && node.parent is LibraryIdentifier) { | |
468 annotatedNode = node.parent.parent; | |
469 } else if (node is ClassDeclaration || node is FunctionDeclaration) { | |
470 annotatedNode = node; | |
471 } else { | |
472 return null; | |
473 } | |
474 if (annotatedNode is! AnnotatedNode) return null; | |
475 var astMeta = annotatedNode.metadata; | |
476 | |
477 return astMeta.firstWhere((m) => m.elementAnnotation == elementAnnotation); | |
478 } | |
479 | |
480 InitializerData(this.element, this.elementAnnotation); | |
jakemac
2015/02/13 20:16:05
I also renamed this to a private constructor, so w
| |
570 } | 481 } |
571 | 482 |
572 // Reads a file list from a barback settings configuration field. | 483 // Reads a file list from a barback settings configuration field. |
573 _readFileList(BarbackSettings settings, String field) { | 484 _readFileList(BarbackSettings settings, String field) { |
574 var value = settings.configuration[field]; | 485 var value = settings.configuration[field]; |
575 if (value == null) return null; | 486 if (value == null) return null; |
576 var files = []; | 487 var files = []; |
577 bool error; | 488 bool error; |
578 if (value is List) { | 489 if (value is List) { |
579 files = value; | 490 files = value; |
580 error = value.any((e) => e is! String); | 491 error = value.any((e) => e is! String); |
581 } else if (value is String) { | 492 } else if (value is String) { |
582 files = [value]; | 493 files = [value]; |
583 error = false; | 494 error = false; |
584 } else { | 495 } else { |
585 error = true; | 496 error = true; |
586 } | 497 } |
587 if (error) { | 498 if (error) { |
588 print('Bad value for "$field" in the initialize transformer. ' | 499 print('Bad value for "$field" in the initialize transformer. ' |
589 'Expected either one String or a list of Strings.'); | 500 'Expected either one String or a list of Strings.'); |
590 } | 501 } |
591 return files; | 502 return files; |
592 } | 503 } |
OLD | NEW |