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

Side by Side Diff: lib/transformer.dart

Issue 923733002: Major refactor of the transformer, added an `InitializePlugin` class which allows you to hook direc… (Closed) Base URL: git@github.com:dart-lang/static-init.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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698