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

Side by Side Diff: packages/polymer/lib/src/build/polymer_smoke_generator.dart

Issue 2312183003: Removed Polymer from Observatory deps (Closed)
Patch Set: Created 4 years, 3 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 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
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.
4
5 /// Transfomer that combines multiple Dart script tags into a single one.
6 library polymer.src.build.polymer_smoke_generator;
7
8 import 'dart:async';
9
10 import 'package:html/dom.dart' show Document, Element, Text;
11 import 'package:html/dom_parsing.dart';
12 import 'package:html/parser.dart' show parseFragment;
13 import 'package:analyzer/src/generated/ast.dart';
14 import 'package:analyzer/src/generated/element.dart' hide Element;
15 import 'package:analyzer/src/generated/element.dart' as analyzer show Element;
16 import 'package:barback/barback.dart';
17 import 'package:code_transformers/messages/build_logger.dart';
18 import 'package:code_transformers/assets.dart';
19 import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
20 import 'package:path/path.dart' as path;
21 import 'package:source_span/source_span.dart';
22 import 'package:smoke/codegen/generator.dart';
23 import 'package:smoke/codegen/recorder.dart';
24 import 'package:code_transformers/resolver.dart';
25 import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens;
26
27 import 'package:polymer_expressions/expression.dart' as pe;
28 import 'package:polymer_expressions/parser.dart' as pe;
29 import 'package:polymer_expressions/visitor.dart' as pe;
30
31 import 'package:web_components/build/import_crawler.dart';
32
33 import 'common.dart';
34 import 'messages.dart';
35
36 /// Method to generate a bootstrap file for Polymer given a [Transform] and a
37 /// [Resolver]. This can be used inside any transformer to share the [Resolver]
38 /// with other steps.
39 Future<Asset> generatePolymerBootstrap(Transform transform, Resolver resolver,
40 AssetId entryPointId, AssetId bootstrapId, Document document,
41 TransformOptions options, {AssetId resolveFromId}) {
42 return new PolymerSmokeGenerator(
43 transform, resolver, entryPointId, bootstrapId, document, options,
44 resolveFromId: resolveFromId).apply();
45 }
46
47 class PolymerSmokeGeneratorTransformer extends Transformer
48 with PolymerTransformer {
49 final Resolvers resolvers;
50 final TransformOptions options;
51
52 PolymerSmokeGeneratorTransformer(this.options, {String sdkDir})
53 // TODO(sigmund): consider restoring here a resolver that uses the real
54 // SDK once the analyzer is lazy and only an resolves what it needs:
55 //: resolvers = new Resolvers(sdkDir != null ? sdkDir : dartSdkDirectory);
56 : resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
57
58 /// Only run on entry point .html files.
59 bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
60
61 Future apply(Transform transform) {
62 var logger = new BuildLogger(transform,
63 convertErrorsToWarnings: !options.releaseMode,
64 detailsUri: 'http://goo.gl/5HPeuP');
65 var primaryId = transform.primaryInput.id;
66 return readPrimaryAsHtml(transform, logger).then((document) {
67 var script = document.querySelector('script[type="application/dart"]');
68 if (script == null) return null;
69 var entryScriptId = uriToAssetId(
70 primaryId, script.attributes['src'], logger, script.sourceSpan);
71 var bootstrapId = primaryId.addExtension('_bootstrap.dart');
72 script.attributes['src'] = path.basename(bootstrapId.path);
73
74 return resolvers.get(transform, [entryScriptId]).then((resolver) {
75 return generatePolymerBootstrap(transform, resolver, entryScriptId,
76 bootstrapId, document, options).then((bootstrapAsset) {
77 transform.addOutput(bootstrapAsset);
78 transform
79 .addOutput(new Asset.fromString(primaryId, document.outerHtml));
80 resolver.release();
81 });
82 });
83 });
84 }
85 }
86
87 /// Class which generates the static smoke configuration for polymer.
88 // TODO(jakemac): Investigate further turning this into an [InitializerPlugin].
89 // The main difficulty is this actually recognizes any class which extends the
90 // [PolymerElement] class, not just things annotated with [CustomTag].
91 class PolymerSmokeGenerator {
92 final TransformOptions options;
93 final Transform transform;
94 final BuildLogger logger;
95 final AssetId docId;
96 final AssetId bootstrapId;
97
98 /// Id of the Dart script found in the document (can only be one).
99 AssetId entryScriptId;
100
101 /// Id of the Dart script to start resolution from.
102 AssetId resolveFromId;
103
104 /// HTML document parsed from [docId].
105 Document document;
106
107 /// Attributes published on a custom-tag. We make these available via
108 /// reflection even if @published was not used.
109 final Map<String, List<String>> publishedAttributes = {};
110
111 /// Resolved types used for analyzing the user's sources and generating code.
112 _ResolvedTypes types;
113
114 /// The resolver instance associated with a single run of this transformer.
115 Resolver resolver;
116
117 /// Code generator used to create the static initialization for smoke.
118 final generator = new SmokeCodeGenerator();
119
120 _SubExpressionVisitor expressionVisitor;
121
122 PolymerSmokeGenerator(Transform transform, Resolver resolver,
123 this.entryScriptId, this.bootstrapId, this.document, options,
124 {this.resolveFromId})
125 : transform = transform,
126 options = options,
127 logger = new BuildLogger(transform,
128 convertErrorsToWarnings: !options.releaseMode,
129 detailsUri: 'http://goo.gl/5HPeuP'),
130 docId = transform.primaryInput.id,
131 resolver = resolver {
132 _ResolvedTypes.logger = logger;
133 types = new _ResolvedTypes(resolver);
134 if (resolveFromId == null) resolveFromId = entryScriptId;
135 }
136
137 Future<Asset> apply() {
138 return _extractUsesOfMirrors().then((_) {
139 var bootstrapAsset = _buildBootstrap();
140 _modifyDocument();
141
142 // Write out the logs collected by our [BuildLogger].
143 if (options.injectBuildLogsInOutput) {
144 return logger.writeOutput().then((_) => bootstrapAsset);
145 }
146 return bootstrapAsset;
147 });
148 }
149
150 /// Inspects the entire program to find out anything that polymer accesses
151 /// using mirrors and produces static information that can be used to replace
152 /// the mirror-based loader and the uses of mirrors through the `smoke`
153 /// package. This includes:
154 ///
155 /// * visiting polymer-expressions to extract getters and setters,
156 /// * looking for published fields of custom elements, and
157 /// * looking for event handlers and callbacks of change notifications.
158 ///
159 Future _extractUsesOfMirrors() {
160 // Generate getters and setters needed to evaluate polymer expressions, and
161 // extract information about published attributes.
162 expressionVisitor = new _SubExpressionVisitor(generator, logger);
163
164 return new ImportCrawler(transform, transform.primaryInput.id, logger,
165 primaryDocument: document).crawlImports().then((documentData) {
166 for (var data in documentData.values) {
167 new _HtmlExtractor(
168 logger, generator, publishedAttributes, expressionVisitor)
169 .visit(data.document);
170 }
171
172 // Create a recorder that uses analyzer data to feed data to [generator].
173 var recorder = new Recorder(generator,
174 (lib) => resolver.getImportUri(lib, from: bootstrapId).toString());
175
176 // Process all classes to include special fields and methods in custom
177 // element classes.
178 _visitLibraries(resolver.getLibrary(resolveFromId), recorder);
179 });
180 }
181
182 _visitLibraries(LibraryElement library, Recorder recorder,
183 [Set<LibraryElement> librariesSeen, Set<ClassElement> classesSeen]) {
184 if (librariesSeen == null) librariesSeen = new Set<LibraryElement>();
185 librariesSeen.add(library);
186
187 // Visit all our dependencies.
188 for (var importedLibrary in _libraryDependencies(library)) {
189 // Don't include anything from the sdk.
190 if (importedLibrary.isInSdk) continue;
191 if (librariesSeen.contains(importedLibrary)) continue;
192 _visitLibraries(importedLibrary, recorder, librariesSeen, classesSeen);
193 }
194
195 // After visiting dependencies, then visit classes in this library.
196 if (classesSeen == null) classesSeen = new Set<ClassElement>();
197 var classes = _visibleClassesOf(library);
198 for (var clazz in classes) {
199 _processClass(clazz, recorder);
200 }
201 }
202
203 Iterable<LibraryElement> _libraryDependencies(LibraryElement library) {
204 getLibrary(UriReferencedElement element) {
205 if (element is ImportElement) return element.importedLibrary;
206 if (element is ExportElement) return element.exportedLibrary;
207 }
208
209 return (new List.from(library.imports)..addAll(library.exports))
210 .map(getLibrary);
211 }
212
213 /// Process a class ([cls]). If it contains an appropriate [CustomTag]
214 /// annotation, we make sure to include everything that might be accessed or
215 /// queried from them using the smoke package. In particular, polymer uses
216 /// smoke for the following:
217 /// * invoke #registerCallback on custom elements classes, if present.
218 /// * query for methods ending in `*Changed`.
219 /// * query for methods with the `@ObserveProperty` annotation.
220 /// * query for non-final properties labeled with `@published`.
221 /// * read declarations of properties named in the `attributes` attribute.
222 /// * read/write the value of published properties .
223 /// * invoke methods in event handlers.
224 _processClass(ClassElement cls, Recorder recorder) {
225 if (!_hasPolymerMixin(cls)) return;
226 if (cls.node is! ClassDeclaration) return;
227 var node = cls.node as ClassDeclaration;
228
229 // Check whether the class has a @CustomTag annotation. Typically we expect
230 // a single @CustomTag, but it's possible to have several.
231 var tagNames = [];
232 for (var meta in node.metadata) {
233 var tagName = _extractTagName(meta, cls);
234 if (tagName != null) tagNames.add(tagName);
235 }
236
237 if (cls.isPrivate && tagNames.isNotEmpty) {
238 var name = tagNames.first;
239 logger.error(PRIVATE_CUSTOM_TAG.create({'name': name, 'class': cls.name}),
240 span: _spanForNode(cls, node.name));
241 return;
242 }
243
244 // Include #registerCallback if it exists. Note that by default lookupMember
245 // and query will also add the corresponding getters and setters.
246 recorder.lookupMember(cls, 'registerCallback');
247
248 // Include methods that end with *Changed.
249 recorder.runQuery(cls, new QueryOptions(
250 includeFields: false,
251 includeProperties: false,
252 includeInherited: true,
253 includeMethods: true,
254 includeUpTo: types.htmlElementElement,
255 matches: (n) => n.endsWith('Changed') && n != 'attributeChanged'));
256
257 // Include methods marked with @ObserveProperty.
258 recorder.runQuery(cls, new QueryOptions(
259 includeFields: false,
260 includeProperties: false,
261 includeInherited: true,
262 includeMethods: true,
263 includeUpTo: types.htmlElementElement,
264 withAnnotations: [types.observePropertyElement]));
265
266 // Include @published and @observable properties.
267 // Symbols in @published are used when resolving bindings on published
268 // attributes, symbols for @observable are used via path observers when
269 // implementing *Changed an @ObserveProperty.
270 // TODO(sigmund): consider including only those symbols mentioned in
271 // *Changed and @ObserveProperty instead.
272 recorder.runQuery(cls, new QueryOptions(
273 includeUpTo: types.htmlElementElement,
274 withAnnotations: [
275 types.publishedElement,
276 types.observableElement,
277 types.computedPropertyElement
278 ]));
279
280 // Include @ComputedProperty and process their expressions
281 var computed = [];
282 recorder.runQuery(cls, new QueryOptions(
283 includeUpTo: types.htmlElementElement,
284 withAnnotations: [types.computedPropertyElement]), results: computed);
285 _processComputedExpressions(computed);
286
287 for (var tagName in tagNames) {
288 // Include also properties published via the `attributes` attribute.
289 var attrs = publishedAttributes[tagName];
290 if (attrs == null) continue;
291 for (var attr in attrs) {
292 recorder.lookupMember(cls, attr,
293 recursive: true, includeUpTo: types.htmlElementElement);
294 }
295 }
296 }
297
298 /// Determines if [cls] or a supertype has a mixin of the Polymer class.
299 bool _hasPolymerMixin(ClassElement cls) {
300 while (cls != types.htmlElementElement) {
301 for (var m in cls.mixins) {
302 if (m.element == types.polymerClassElement) return true;
303 }
304 if (cls.supertype == null) return false;
305 cls = cls.supertype.element;
306 }
307 return false;
308 }
309
310 /// If [meta] is [CustomTag], extract the name associated with the tag.
311 String _extractTagName(Annotation meta, ClassElement cls) {
312 if (meta.element != types.customTagConstructor) return null;
313 return _extractFirstAnnotationArgument(meta, 'CustomTag', cls);
314 }
315
316 /// Extract the first argument of an annotation and validate that it's type is
317 /// String. For instance, return "bar" from `@Foo("bar")`.
318 String _extractFirstAnnotationArgument(
319 Annotation meta, String name, analyzer.Element context) {
320
321 // Read argument from the AST
322 var args = meta.arguments.arguments;
323 if (args == null || args.length == 0) {
324 logger.warning(MISSING_ANNOTATION_ARGUMENT.create({'name': name}),
325 span: _spanForNode(context, meta));
326 return null;
327 }
328
329 var lib = context;
330 while (lib is! LibraryElement) lib = lib.enclosingElement;
331 var res = resolver.evaluateConstant(lib, args[0]);
332 if (!res.isValid || res.value.type != types.stringType) {
333 logger.warning(INVALID_ANNOTATION_ARGUMENT.create({'name': name}),
334 span: _spanForNode(context, args[0]));
335 return null;
336 }
337 return res.value.stringValue;
338 }
339
340 /// Process members that are annotated with `@ComputedProperty` and records
341 /// the accessors of their expressions.
342 _processComputedExpressions(List<analyzer.Element> computed) {
343 var constructor = types.computedPropertyElement.constructors.first;
344 for (var member in computed) {
345 for (var meta in member.node.metadata) {
346 if (meta.element != constructor) continue;
347 var expr =
348 _extractFirstAnnotationArgument(meta, 'ComputedProperty', member);
349 if (expr == null) continue;
350 expressionVisitor.run(pe.parse(expr), true,
351 _spanForNode(member.enclosingElement, meta.arguments.arguments[0]));
352 }
353 }
354 }
355
356 // Builds the bootstrap Dart file asset.
357 Asset _buildBootstrap() {
358 StringBuffer code = new StringBuffer()..writeln(MAIN_HEADER);
359
360 // TODO(jakemac): Inject this at some other stage.
361 // https://github.com/dart-lang/polymer-dart/issues/22
362 if (options.injectBuildLogsInOutput) {
363 code.writeln("import 'package:polymer/src/build/log_injector.dart';");
364 }
365
366 var entryScriptUrl = assetUrlFor(entryScriptId, bootstrapId, logger);
367 code.writeln("import '$entryScriptUrl' as i0;");
368
369 // Include smoke initialization.
370 generator.writeImports(code);
371 generator.writeTopLevelDeclarations(code);
372 code.writeln('\nmain() {');
373 code.write(' useGeneratedCode(');
374 generator.writeStaticConfiguration(code);
375 code.writeln(');');
376
377 // TODO(jakemac): Inject this at some other stage.
378 // https://github.com/dart-lang/polymer-dart/issues/22
379 if (options.injectBuildLogsInOutput) {
380 var buildUrl = "${path.basename(docId.path)}$LOG_EXTENSION";
381 code.writeln(" new LogInjector().injectLogsFromUrl('$buildUrl');");
382 }
383
384 code.writeln(' configureForDeployment();');
385 code.writeln(' return i0.main();');
386
387 // End of main().
388 code.writeln('}');
389 return new Asset.fromString(bootstrapId, code.toString());
390 }
391
392 // Add the styles for the logger widget.
393 // TODO(jakemac): Inject this at some other stage.
394 // https://github.com/dart-lang/polymer-dart/issues/22
395 void _modifyDocument() {
396 if (options.injectBuildLogsInOutput) {
397 document.head.append(parseFragment(
398 '<link rel="stylesheet" type="text/css"'
399 ' href="packages/polymer/src/build/log_injector.css">'));
400 }
401 }
402
403 _spanForNode(analyzer.Element context, AstNode node) {
404 var file = resolver.getSourceFile(context);
405 return file.span(node.offset, node.end);
406 }
407 }
408
409 const MAIN_HEADER = """
410 library app_bootstrap;
411
412 import 'package:polymer/polymer.dart';
413 """;
414
415 /// An html visitor that:
416 /// * finds all polymer expressions and records the getters and setters that
417 /// will be needed to evaluate them at runtime.
418 /// * extracts all attributes declared in the `attribute` attributes of
419 /// polymer elements.
420 class _HtmlExtractor extends TreeVisitor {
421 final Map<String, List<String>> publishedAttributes;
422 final SmokeCodeGenerator generator;
423 final _SubExpressionVisitor expressionVisitor;
424 final BuildLogger logger;
425 bool _inTemplate = false;
426 bool _inPolymerJs = false;
427
428 _HtmlExtractor(this.logger, this.generator, this.publishedAttributes,
429 this.expressionVisitor);
430
431 void visitElement(Element node) {
432 if (_inTemplate) _processNormalElement(node);
433 var lastInPolymerJs = _inPolymerJs;
434 if (node.localName == 'polymer-element') {
435 // Detect Polymer JS elements, the current logic is any element with only
436 // non-Dart script tags.
437 var scripts = node.querySelectorAll('script');
438 _inPolymerJs = scripts.isNotEmpty &&
439 scripts.every((s) => s.attributes['type'] != 'application/dart');
440 _processPolymerElement(node);
441 _processNormalElement(node);
442 }
443
444 if (node.localName == 'template') {
445 var last = _inTemplate;
446 _inTemplate = true;
447 super.visitElement(node);
448 _inTemplate = last;
449 } else {
450 super.visitElement(node);
451 }
452 _inPolymerJs = lastInPolymerJs;
453 }
454
455 void visitText(Text node) {
456 // Nothing here applies if inside a polymer js element
457 if (!_inTemplate || _inPolymerJs) return;
458 var bindings = _Mustaches.parse(node.data);
459 if (bindings == null) return;
460 for (var e in bindings.expressions) {
461 _addExpression(e, false, false, node.sourceSpan);
462 }
463 }
464
465 /// Registers getters and setters for all published attributes.
466 void _processPolymerElement(Element node) {
467 // Nothing here applies if inside a polymer js element
468 if (_inPolymerJs) return;
469
470 var tagName = node.attributes['name'];
471 var value = node.attributes['attributes'];
472 if (value != null) {
473 publishedAttributes[tagName] =
474 value.split(ATTRIBUTES_REGEX).map((a) => a.trim()).toList();
475 }
476 }
477
478 /// Produces warnings for misuses of on-foo event handlers, and for instanting
479 /// custom tags incorrectly.
480 void _processNormalElement(Element node) {
481 // Nothing here applies if inside a polymer js element
482 if (_inPolymerJs) return;
483
484 var tag = node.localName;
485 var isCustomTag = isCustomTagName(tag) || node.attributes['is'] != null;
486
487 // Event handlers only allowed inside polymer-elements
488 node.attributes.forEach((name, value) {
489 var bindings = _Mustaches.parse(value);
490 if (bindings == null) return;
491 var isEvent = false;
492 var isTwoWay = false;
493 if (name is String) {
494 name = name.toLowerCase();
495 isEvent = name.startsWith('on-');
496 isTwoWay = !isEvent &&
497 bindings.isWhole &&
498 (isCustomTag ||
499 tag == 'input' && (name == 'value' || name == 'checked') ||
500 tag == 'select' &&
501 (name == 'selectedindex' || name == 'value') ||
502 tag == 'textarea' && name == 'value');
503 }
504 for (var exp in bindings.expressions) {
505 _addExpression(exp, isEvent, isTwoWay, node.sourceSpan);
506 }
507 });
508 }
509
510 void _addExpression(
511 String stringExpression, bool inEvent, bool isTwoWay, SourceSpan span) {
512 if (inEvent) {
513 if (stringExpression.startsWith('@')) {
514 logger.warning(AT_EXPRESSION_REMOVED, span: span);
515 return;
516 }
517
518 if (stringExpression == '') return;
519 if (stringExpression.startsWith('_')) {
520 logger.warning(NO_PRIVATE_EVENT_HANDLERS, span: span);
521 return;
522 }
523 generator.addGetter(stringExpression);
524 generator.addSymbol(stringExpression);
525 }
526 expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span);
527 }
528 }
529
530 /// A polymer-expression visitor that records every getter and setter that will
531 /// be needed to evaluate a single expression at runtime.
532 class _SubExpressionVisitor extends pe.RecursiveVisitor {
533 final SmokeCodeGenerator generator;
534 final BuildLogger logger;
535 bool _includeSetter;
536 SourceSpan _currentSpan;
537
538 _SubExpressionVisitor(this.generator, this.logger);
539
540 /// Visit [exp], and record getters and setters that are needed in order to
541 /// evaluate it at runtime. [includeSetter] is only true if this expression
542 /// occured in a context where it could be updated, for example in two-way
543 /// bindings such as `<input value={{exp}}>`.
544 void run(pe.Expression exp, bool includeSetter, span) {
545 _currentSpan = span;
546 _includeSetter = includeSetter;
547 visit(exp);
548 }
549
550 /// Adds a getter and symbol for [name], and optionally a setter.
551 _add(String name) {
552 if (name.startsWith('_')) {
553 logger.warning(NO_PRIVATE_SYMBOLS_IN_BINDINGS, span: _currentSpan);
554 return;
555 }
556 generator.addGetter(name);
557 generator.addSymbol(name);
558 if (_includeSetter) generator.addSetter(name);
559 }
560
561 void preVisitExpression(e) {
562 // For two-way bindings the outermost expression may be updated, so we need
563 // both the getter and the setter, but we only need the getter for
564 // subexpressions. We exclude setters as soon as we go deeper in the tree,
565 // except when we see a filter (that can potentially be a two-way
566 // transformer).
567 if (e is pe.BinaryOperator && e.operator == '|') return;
568 _includeSetter = false;
569 }
570
571 visitIdentifier(pe.Identifier e) {
572 if (e.value != 'this') _add(e.value);
573 super.visitIdentifier(e);
574 }
575
576 visitGetter(pe.Getter e) {
577 _add(e.name);
578 super.visitGetter(e);
579 }
580
581 visitInvoke(pe.Invoke e) {
582 _includeSetter = false; // Invoke is only valid as an r-value.
583 if (e.method != null) _add(e.method);
584 super.visitInvoke(e);
585 }
586 }
587
588 /// Parses and collects information about bindings found in polymer templates.
589 class _Mustaches {
590 /// Each expression that appears within `{{...}}` and `[[...]]`.
591 final List<String> expressions;
592
593 /// Whether the whole text returned by [parse] was a single expression.
594 final bool isWhole;
595
596 _Mustaches(this.isWhole, this.expressions);
597
598 static _Mustaches parse(String text) {
599 if (text == null || text.isEmpty) return null;
600 // Use template-binding's parser, but provide a delegate function factory to
601 // save the expressions without parsing them as [PropertyPath]s.
602 var tokens = MustacheTokens.parse(text, (s) => () => s);
603 if (tokens == null) return null;
604 var length = tokens.length;
605 bool isWhole =
606 length == 1 && tokens.getText(length) == '' && tokens.getText(0) == '';
607 var expressions = new List(length);
608 for (int i = 0; i < length; i++) {
609 expressions[i] = tokens.getPrepareBinding(i)();
610 }
611 return new _Mustaches(isWhole, expressions);
612 }
613 }
614
615 /// Holds types that are used in queries
616 class _ResolvedTypes {
617 /// Element representing `HtmlElement`.
618 final ClassElement htmlElementElement;
619
620 /// Element representing `String`.
621 final InterfaceType stringType;
622
623 /// Element representing `Polymer`.
624 final ClassElement polymerClassElement;
625
626 /// Element representing the constructor of `@CustomTag`.
627 final ConstructorElement customTagConstructor;
628
629 /// Element representing the type of `@published`.
630 final ClassElement publishedElement;
631
632 /// Element representing the type of `@observable`.
633 final ClassElement observableElement;
634
635 /// Element representing the type of `@ObserveProperty`.
636 final ClassElement observePropertyElement;
637
638 /// Element representing the type of `@ComputedProperty`.
639 final ClassElement computedPropertyElement;
640
641 /// Logger for reporting errors.
642 static BuildLogger logger;
643
644 factory _ResolvedTypes(Resolver resolver) {
645 var coreLib = resolver.getLibraryByUri(Uri.parse('dart:core'));
646 // coreLib should never be null, its ok to throw if this fails.
647 var stringType = _lookupType(coreLib, 'String').type;
648
649 // Load class elements that are used in queries for codegen.
650 var polymerLib =
651 resolver.getLibrary(new AssetId('polymer', 'lib/polymer.dart'));
652 if (polymerLib == null) {
653 _definitionError('polymer');
654 return new _ResolvedTypes.internal(
655 null, stringType, null, null, null, null, null, null);
656 }
657
658 var htmlLib = resolver.getLibraryByUri(Uri.parse('dart:html'));
659 var observeLib =
660 resolver.getLibrary(new AssetId('observe', 'lib/src/metadata.dart'));
661
662 var customTagConstructor =
663 _lookupType(polymerLib, 'CustomTag').constructors.first;
664 var publishedElement = _lookupType(polymerLib, 'PublishedProperty');
665 var observePropertyElement = _lookupType(polymerLib, 'ObserveProperty');
666 var computedPropertyElement = _lookupType(polymerLib, 'ComputedProperty');
667 var polymerClassElement = _lookupType(polymerLib, 'Polymer');
668 var observableElement = _lookupType(observeLib, 'ObservableProperty');
669 var htmlElementElement = _lookupType(htmlLib, 'HtmlElement');
670
671 return new _ResolvedTypes.internal(htmlElementElement, stringType,
672 polymerClassElement, customTagConstructor, publishedElement,
673 observableElement, observePropertyElement, computedPropertyElement);
674 }
675
676 _ResolvedTypes.internal(this.htmlElementElement, this.stringType,
677 this.polymerClassElement, this.customTagConstructor,
678 this.publishedElement, this.observableElement,
679 this.observePropertyElement, this.computedPropertyElement);
680
681 static _lookupType(LibraryElement lib, String typeName) {
682 var result = lib.getType(typeName);
683 if (result == null) _definitionError(typeName);
684 return result;
685 }
686
687 static _definitionError(name) {
688 var message = MISSING_POLYMER_DART;
689 if (logger != null) {
690 logger.warning(message);
691 } else {
692 throw new StateError(message.snippet);
693 }
694 }
695 }
696
697 /// Retrieves all classes that are visible if you were to import [lib]. This
698 /// includes exported classes from other libraries.
699 List<ClassElement> _visibleClassesOf(LibraryElement lib) {
700 var result = [];
701 result.addAll(lib.units.expand((u) => u.types));
702 for (var e in lib.exports) {
703 var exported = e.exportedLibrary.units.expand((u) => u.types).toList();
704 _filter(exported, e.combinators);
705 result.addAll(exported);
706 }
707 return result;
708 }
709
710 /// Retrieves all top-level methods that are visible if you were to import
711 /// [lib]. This includes exported methods from other libraries too.
712 List<FunctionElement> _visibleTopLevelMethodsOf(LibraryElement lib) {
713 var result = [];
714 result.addAll(lib.units.expand((u) => u.functions));
715 for (var e in lib.exports) {
716 var exported = e.exportedLibrary.units.expand((u) => u.functions).toList();
717 _filter(exported, e.combinators);
718 result.addAll(exported);
719 }
720 return result;
721 }
722
723 /// Filters [elements] that come from an export, according to its show/hide
724 /// combinators. This modifies [elements] in place.
725 void _filter(
726 List<analyzer.Element> elements, List<NamespaceCombinator> combinators) {
727 for (var c in combinators) {
728 if (c is ShowElementCombinator) {
729 var show = c.shownNames.toSet();
730 elements.retainWhere((e) => show.contains(e.displayName));
731 } else if (c is HideElementCombinator) {
732 var hide = c.hiddenNames.toSet();
733 elements.removeWhere((e) => hide.contains(e.displayName));
734 }
735 }
736 }
OLDNEW
« no previous file with comments | « packages/polymer/lib/src/build/polymer_bootstrap.dart ('k') | packages/polymer/lib/src/build/runner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698