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

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

Powered by Google App Engine
This is Rietveld 408576698