OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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 | 4 |
5 /// Transfomer that combines multiple dart script tags into a single one. | 5 /// Transfomer that combines multiple dart script tags into a single one. |
6 library polymer.src.build.script_compactor; | 6 library polymer.src.build.script_compactor; |
7 | 7 |
8 import 'dart:async'; | 8 import 'dart:async'; |
9 import 'dart:convert'; | 9 import 'dart:convert'; |
10 | 10 |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 | 133 |
134 /// Resolved types used for analyzing the user's sources and generating code. | 134 /// Resolved types used for analyzing the user's sources and generating code. |
135 _ResolvedTypes types; | 135 _ResolvedTypes types; |
136 | 136 |
137 /// The resolver instance associated with a single run of this transformer. | 137 /// The resolver instance associated with a single run of this transformer. |
138 Resolver resolver; | 138 Resolver resolver; |
139 | 139 |
140 /// Code generator used to create the static initialization for smoke. | 140 /// Code generator used to create the static initialization for smoke. |
141 final generator = new SmokeCodeGenerator(); | 141 final generator = new SmokeCodeGenerator(); |
142 | 142 |
| 143 _SubExpressionVisitor expressionVisitor; |
| 144 |
143 _ScriptCompactor(Transform transform, this.options, this.resolvers) | 145 _ScriptCompactor(Transform transform, this.options, this.resolvers) |
144 : transform = transform, | 146 : transform = transform, |
145 logger = transform.logger, | 147 logger = transform.logger, |
146 docId = transform.primaryInput.id, | 148 docId = transform.primaryInput.id, |
147 bootstrapId = transform.primaryInput.id.addExtension('_bootstrap.dart'); | 149 bootstrapId = transform.primaryInput.id.addExtension('_bootstrap.dart'); |
148 | 150 |
149 Future apply() => | 151 Future apply() => |
150 _loadDocument() | 152 _loadDocument() |
151 .then(_loadEntryLibraries) | 153 .then(_loadEntryLibraries) |
152 .then(_processHtml) | 154 .then(_processHtml) |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 /// package. This includes: | 223 /// package. This includes: |
222 /// | 224 /// |
223 /// * visiting entry-libraries to extract initializers, | 225 /// * visiting entry-libraries to extract initializers, |
224 /// * visiting polymer-expressions to extract getters and setters, | 226 /// * visiting polymer-expressions to extract getters and setters, |
225 /// * looking for published fields of custom elements, and | 227 /// * looking for published fields of custom elements, and |
226 /// * looking for event handlers and callbacks of change notifications. | 228 /// * looking for event handlers and callbacks of change notifications. |
227 /// | 229 /// |
228 void _extractUsesOfMirrors(_) { | 230 void _extractUsesOfMirrors(_) { |
229 // Generate getters and setters needed to evaluate polymer expressions, and | 231 // Generate getters and setters needed to evaluate polymer expressions, and |
230 // extract information about published attributes. | 232 // extract information about published attributes. |
231 new _HtmlExtractor(logger, generator, publishedAttributes).visit(document); | 233 expressionVisitor = new _SubExpressionVisitor(generator, logger); |
| 234 new _HtmlExtractor(logger, generator, publishedAttributes, |
| 235 expressionVisitor).visit(document); |
232 | 236 |
233 // Create a recorder that uses analyzer data to feed data to [generator]. | 237 // Create a recorder that uses analyzer data to feed data to [generator]. |
234 var recorder = new Recorder(generator, | 238 var recorder = new Recorder(generator, |
235 (lib) => resolver.getImportUri(lib, from: bootstrapId).toString()); | 239 (lib) => resolver.getImportUri(lib, from: bootstrapId).toString()); |
236 | 240 |
237 // Process all classes and top-level functions to include initializers, | 241 // Process all classes and top-level functions to include initializers, |
238 // register custom elements, and include special fields and methods in | 242 // register custom elements, and include special fields and methods in |
239 // custom element classes. | 243 // custom element classes. |
240 for (var id in entryLibraries) { | 244 for (var id in entryLibraries) { |
241 var lib = resolver.getLibrary(id); | 245 var lib = resolver.getLibrary(id); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 withAnnotations: [types.observePropertyElement])); | 305 withAnnotations: [types.observePropertyElement])); |
302 | 306 |
303 // Include @published and @observable properties. | 307 // Include @published and @observable properties. |
304 // Symbols in @published are used when resolving bindings on published | 308 // Symbols in @published are used when resolving bindings on published |
305 // attributes, symbols for @observable are used via path observers when | 309 // attributes, symbols for @observable are used via path observers when |
306 // implementing *Changed an @ObserveProperty. | 310 // implementing *Changed an @ObserveProperty. |
307 // TODO(sigmund): consider including only those symbols mentioned in | 311 // TODO(sigmund): consider including only those symbols mentioned in |
308 // *Changed and @ObserveProperty instead. | 312 // *Changed and @ObserveProperty instead. |
309 recorder.runQuery(cls, new QueryOptions( | 313 recorder.runQuery(cls, new QueryOptions( |
310 includeUpTo: types.htmlElementElement, | 314 includeUpTo: types.htmlElementElement, |
311 withAnnotations: [types.publishedElement, types.observableElement])); | 315 withAnnotations: [types.publishedElement, types.observableElement, |
| 316 types.computedPropertyElement])); |
| 317 |
| 318 // Include @ComputedProperty and process their expressions |
| 319 var computed = []; |
| 320 recorder.runQuery(cls, new QueryOptions( |
| 321 includeUpTo: types.htmlElementElement, |
| 322 withAnnotations: [types.computedPropertyElement]), |
| 323 results: computed); |
| 324 _processComputedExpressions(computed); |
312 | 325 |
313 for (var tagName in tagNames) { | 326 for (var tagName in tagNames) { |
314 // Include an initializer that will call Polymer.register | 327 // Include an initializer that will call Polymer.register |
315 initializers.add(new _CustomTagInitializer(id, tagName, cls.displayName)); | 328 initializers.add(new _CustomTagInitializer(id, tagName, cls.displayName)); |
316 | 329 |
317 // Include also properties published via the `attributes` attribute. | 330 // Include also properties published via the `attributes` attribute. |
318 var attrs = publishedAttributes[tagName]; | 331 var attrs = publishedAttributes[tagName]; |
319 if (attrs == null) continue; | 332 if (attrs == null) continue; |
320 for (var attr in attrs) { | 333 for (var attr in attrs) { |
321 recorder.lookupMember(cls, attr, recursive: true, | 334 recorder.lookupMember(cls, attr, recursive: true, |
(...skipping 10 matching lines...) Expand all Loading... |
332 } | 345 } |
333 if (cls.supertype == null) return false; | 346 if (cls.supertype == null) return false; |
334 cls = cls.supertype.element; | 347 cls = cls.supertype.element; |
335 } | 348 } |
336 return false; | 349 return false; |
337 } | 350 } |
338 | 351 |
339 /// If [meta] is [CustomTag], extract the name associated with the tag. | 352 /// If [meta] is [CustomTag], extract the name associated with the tag. |
340 String _extractTagName(Annotation meta, ClassElement cls) { | 353 String _extractTagName(Annotation meta, ClassElement cls) { |
341 if (meta.element != types.customTagConstructor) return null; | 354 if (meta.element != types.customTagConstructor) return null; |
| 355 return _extractFirstAnnotationArgument(meta, 'CustomTag', cls); |
| 356 } |
| 357 |
| 358 /// Extract the first argument of an annotation and validate that it's type is |
| 359 /// String. For instance, return "bar" from `@Foo("bar")`. |
| 360 String _extractFirstAnnotationArgument(Annotation meta, String name, |
| 361 analyzer.Element context) { |
342 | 362 |
343 // Read argument from the AST | 363 // Read argument from the AST |
344 var args = meta.arguments.arguments; | 364 var args = meta.arguments.arguments; |
345 if (args == null || args.length == 0) { | 365 if (args == null || args.length == 0) { |
346 logger.warning('Missing argument in @CustomTag annotation', | 366 logger.warning('Missing argument in @$name annotation', |
347 span: _spanForNode(cls, meta)); | 367 span: _spanForNode(context, meta)); |
348 return null; | 368 return null; |
349 } | 369 } |
350 | 370 |
351 var res = resolver.evaluateConstant( | 371 var lib = context; |
352 cls.enclosingElement.enclosingElement, args[0]); | 372 while (lib is! LibraryElement) lib = lib.enclosingElement; |
| 373 var res = resolver.evaluateConstant(lib, args[0]); |
353 if (!res.isValid || res.value.type != types.stringType) { | 374 if (!res.isValid || res.value.type != types.stringType) { |
354 logger.warning('The parameter to @CustomTag seems to be invalid.', | 375 logger.warning('The parameter to @$name seems to be invalid.', |
355 span: _spanForNode(cls, args[0])); | 376 span: _spanForNode(context, args[0])); |
356 return null; | 377 return null; |
357 } | 378 } |
358 return res.value.stringValue; | 379 return res.value.stringValue; |
359 } | 380 } |
360 | 381 |
361 /// Adds the top-level [function] as an initalizer if it's marked with | 382 /// Adds the top-level [function] as an initalizer if it's marked with |
362 /// `@initMethod`. | 383 /// `@initMethod`. |
363 _processFunction(FunctionElement function, AssetId id) { | 384 _processFunction(FunctionElement function, AssetId id) { |
364 bool initMethodFound = false; | 385 bool initMethodFound = false; |
365 for (var meta in function.metadata) { | 386 for (var meta in function.metadata) { |
366 var e = meta.element; | 387 var e = meta.element; |
367 if (e is PropertyAccessorElement && | 388 if (e is PropertyAccessorElement && |
368 e.variable == types.initMethodElement) { | 389 e.variable == types.initMethodElement) { |
369 initMethodFound = true; | 390 initMethodFound = true; |
370 break; | 391 break; |
371 } | 392 } |
372 } | 393 } |
373 if (!initMethodFound) return; | 394 if (!initMethodFound) return; |
374 if (function.isPrivate) { | 395 if (function.isPrivate) { |
375 logger.error('@initMethod is no longer supported on private ' | 396 logger.error('@initMethod is no longer supported on private ' |
376 'functions: ${function.displayName}', | 397 'functions: ${function.displayName}', |
377 span: _spanForNode(function, function.node.name)); | 398 span: _spanForNode(function, function.node.name)); |
378 return; | 399 return; |
379 } | 400 } |
380 initializers.add(new _InitMethodInitializer(id, function.displayName)); | 401 initializers.add(new _InitMethodInitializer(id, function.displayName)); |
381 } | 402 } |
382 | 403 |
| 404 /// Process members that are annotated with `@ComputedProperty` and records |
| 405 /// the accessors of their expressions. |
| 406 _processComputedExpressions(List<analyzer.Element> computed) { |
| 407 var constructor = types.computedPropertyElement.constructors.first; |
| 408 for (var member in computed) { |
| 409 for (var meta in member.node.metadata) { |
| 410 if (meta.element != constructor) continue; |
| 411 var expr = _extractFirstAnnotationArgument( |
| 412 meta, 'ComputedProperty', member); |
| 413 if (expr == null) continue; |
| 414 expressionVisitor.run(pe.parse(expr), true, |
| 415 _spanForNode(member.enclosingElement, meta.arguments.arguments[0])); |
| 416 } |
| 417 } |
| 418 } |
| 419 |
383 /// Writes the final output for the bootstrap Dart file and entrypoint HTML | 420 /// Writes the final output for the bootstrap Dart file and entrypoint HTML |
384 /// file. | 421 /// file. |
385 void _emitFiles(_) { | 422 void _emitFiles(_) { |
386 StringBuffer code = new StringBuffer()..writeln(MAIN_HEADER); | 423 StringBuffer code = new StringBuffer()..writeln(MAIN_HEADER); |
387 Map<AssetId, String> prefixes = {}; | 424 Map<AssetId, String> prefixes = {}; |
388 int i = 0; | 425 int i = 0; |
389 for (var id in entryLibraries) { | 426 for (var id in entryLibraries) { |
390 var url = assetUrlFor(id, bootstrapId, logger); | 427 var url = assetUrlFor(id, bootstrapId, logger); |
391 if (url == null) continue; | 428 if (url == null) continue; |
392 code.writeln("import '$url' as i$i;"); | 429 code.writeln("import '$url' as i$i;"); |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
481 'package:polymer/polymer.dart).'; | 518 'package:polymer/polymer.dart).'; |
482 | 519 |
483 /// An html visitor that: | 520 /// An html visitor that: |
484 /// * finds all polymer expressions and records the getters and setters that | 521 /// * finds all polymer expressions and records the getters and setters that |
485 /// will be needed to evaluate them at runtime. | 522 /// will be needed to evaluate them at runtime. |
486 /// * extracts all attributes declared in the `attribute` attributes of | 523 /// * extracts all attributes declared in the `attribute` attributes of |
487 /// polymer elements. | 524 /// polymer elements. |
488 class _HtmlExtractor extends TreeVisitor { | 525 class _HtmlExtractor extends TreeVisitor { |
489 final Map<String, List<String>> publishedAttributes; | 526 final Map<String, List<String>> publishedAttributes; |
490 final SmokeCodeGenerator generator; | 527 final SmokeCodeGenerator generator; |
491 final _SubExpressionVisitor visitor; | 528 final _SubExpressionVisitor expressionVisitor; |
492 final TransformLogger logger; | 529 final TransformLogger logger; |
493 bool _inTemplate = false; | 530 bool _inTemplate = false; |
494 | 531 |
495 _HtmlExtractor(TransformLogger logger, SmokeCodeGenerator generator, | 532 _HtmlExtractor(this.logger, this.generator, this.publishedAttributes, |
496 this.publishedAttributes) | 533 this.expressionVisitor); |
497 : logger = logger, generator = generator, | |
498 visitor = new _SubExpressionVisitor(generator, logger); | |
499 | 534 |
500 void visitElement(Element node) { | 535 void visitElement(Element node) { |
501 if (_inTemplate) _processNormalElement(node); | 536 if (_inTemplate) _processNormalElement(node); |
502 if (node.localName == 'polymer-element') { | 537 if (node.localName == 'polymer-element') { |
503 _processPolymerElement(node); | 538 _processPolymerElement(node); |
504 _processNormalElement(node); | 539 _processNormalElement(node); |
505 } | 540 } |
506 | 541 |
507 if (node.localName == 'template') { | 542 if (node.localName == 'template') { |
508 var last = _inTemplate; | 543 var last = _inTemplate; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
571 | 606 |
572 if (stringExpression == '') return; | 607 if (stringExpression == '') return; |
573 if (stringExpression.startsWith('_')) { | 608 if (stringExpression.startsWith('_')) { |
574 logger.warning('private symbols cannot be used in event handlers', | 609 logger.warning('private symbols cannot be used in event handlers', |
575 span: span); | 610 span: span); |
576 return; | 611 return; |
577 } | 612 } |
578 generator.addGetter(stringExpression); | 613 generator.addGetter(stringExpression); |
579 generator.addSymbol(stringExpression); | 614 generator.addSymbol(stringExpression); |
580 } | 615 } |
581 visitor.run(pe.parse(stringExpression), isTwoWay, span); | 616 expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span); |
582 } | 617 } |
583 } | 618 } |
584 | 619 |
585 /// A polymer-expression visitor that records every getter and setter that will | 620 /// A polymer-expression visitor that records every getter and setter that will |
586 /// be needed to evaluate a single expression at runtime. | 621 /// be needed to evaluate a single expression at runtime. |
587 class _SubExpressionVisitor extends pe.RecursiveVisitor { | 622 class _SubExpressionVisitor extends pe.RecursiveVisitor { |
588 final SmokeCodeGenerator generator; | 623 final SmokeCodeGenerator generator; |
589 final TransformLogger logger; | 624 final TransformLogger logger; |
590 bool _includeSetter; | 625 bool _includeSetter; |
591 Span _currentSpan; | 626 Span _currentSpan; |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 | 718 |
684 /// Element representing the type of `@published`. | 719 /// Element representing the type of `@published`. |
685 final ClassElement publishedElement; | 720 final ClassElement publishedElement; |
686 | 721 |
687 /// Element representing the type of `@observable`. | 722 /// Element representing the type of `@observable`. |
688 final ClassElement observableElement; | 723 final ClassElement observableElement; |
689 | 724 |
690 /// Element representing the type of `@ObserveProperty`. | 725 /// Element representing the type of `@ObserveProperty`. |
691 final ClassElement observePropertyElement; | 726 final ClassElement observePropertyElement; |
692 | 727 |
| 728 /// Element representing the type of `@ComputedProperty`. |
| 729 final ClassElement computedPropertyElement; |
| 730 |
693 /// Element representing the `@initMethod` annotation. | 731 /// Element representing the `@initMethod` annotation. |
694 final TopLevelVariableElement initMethodElement; | 732 final TopLevelVariableElement initMethodElement; |
695 | 733 |
696 | 734 |
697 factory _ResolvedTypes(Resolver resolver) { | 735 factory _ResolvedTypes(Resolver resolver) { |
698 // Load class elements that are used in queries for codegen. | 736 // Load class elements that are used in queries for codegen. |
699 var polymerLib = resolver.getLibrary( | 737 var polymerLib = resolver.getLibrary( |
700 new AssetId('polymer', 'lib/polymer.dart')); | 738 new AssetId('polymer', 'lib/polymer.dart')); |
701 if (polymerLib == null) _definitionError('the polymer library'); | 739 if (polymerLib == null) _definitionError('the polymer library'); |
702 | 740 |
(...skipping 13 matching lines...) Expand all Loading... |
716 initMethodElement = unit.topLevelVariables.firstWhere( | 754 initMethodElement = unit.topLevelVariables.firstWhere( |
717 (t) => t.displayName == 'initMethod'); | 755 (t) => t.displayName == 'initMethod'); |
718 break; | 756 break; |
719 } | 757 } |
720 } | 758 } |
721 var customTagConstructor = | 759 var customTagConstructor = |
722 _lookupType(polymerLib, 'CustomTag').constructors.first; | 760 _lookupType(polymerLib, 'CustomTag').constructors.first; |
723 var publishedElement = _lookupType(polymerLib, 'PublishedProperty'); | 761 var publishedElement = _lookupType(polymerLib, 'PublishedProperty'); |
724 var observableElement = _lookupType(observeLib, 'ObservableProperty'); | 762 var observableElement = _lookupType(observeLib, 'ObservableProperty'); |
725 var observePropertyElement = _lookupType(polymerLib, 'ObserveProperty'); | 763 var observePropertyElement = _lookupType(polymerLib, 'ObserveProperty'); |
| 764 var computedPropertyElement = _lookupType(polymerLib, 'ComputedProperty'); |
726 var polymerClassElement = _lookupType(polymerLib, 'Polymer'); | 765 var polymerClassElement = _lookupType(polymerLib, 'Polymer'); |
727 var htmlElementElement = _lookupType(htmlLib, 'HtmlElement'); | 766 var htmlElementElement = _lookupType(htmlLib, 'HtmlElement'); |
728 var stringType = _lookupType(coreLib, 'String').type; | 767 var stringType = _lookupType(coreLib, 'String').type; |
729 if (initMethodElement == null) _definitionError('@initMethod'); | 768 if (initMethodElement == null) _definitionError('@initMethod'); |
730 | 769 |
731 return new _ResolvedTypes.internal(htmlElementElement, stringType, | 770 return new _ResolvedTypes.internal(htmlElementElement, stringType, |
732 polymerClassElement, customTagConstructor, publishedElement, | 771 polymerClassElement, customTagConstructor, publishedElement, |
733 observableElement, observePropertyElement, initMethodElement); | 772 observableElement, observePropertyElement, computedPropertyElement, |
| 773 initMethodElement); |
734 } | 774 } |
735 | 775 |
736 _ResolvedTypes.internal(this.htmlElementElement, this.stringType, | 776 _ResolvedTypes.internal(this.htmlElementElement, this.stringType, |
737 this.polymerClassElement, this.customTagConstructor, | 777 this.polymerClassElement, this.customTagConstructor, |
738 this.publishedElement, this.observableElement, | 778 this.publishedElement, this.observableElement, |
739 this.observePropertyElement, this.initMethodElement); | 779 this.observePropertyElement, this.computedPropertyElement, |
| 780 this.initMethodElement); |
740 | 781 |
741 static _lookupType(LibraryElement lib, String typeName) { | 782 static _lookupType(LibraryElement lib, String typeName) { |
742 var result = lib.getType(typeName); | 783 var result = lib.getType(typeName); |
743 if (result == null) _definitionError(typeName); | 784 if (result == null) _definitionError(typeName); |
744 return result; | 785 return result; |
745 } | 786 } |
746 | 787 |
747 static _definitionError(name) { | 788 static _definitionError(name) { |
748 throw new StateError("Internal error in polymer-builder: couldn't find " | 789 throw new StateError("Internal error in polymer-builder: couldn't find " |
749 "definition of $name."); | 790 "definition of $name."); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
784 for (var c in combinators) { | 825 for (var c in combinators) { |
785 if (c is ShowElementCombinator) { | 826 if (c is ShowElementCombinator) { |
786 var show = c.shownNames.toSet(); | 827 var show = c.shownNames.toSet(); |
787 elements.retainWhere((e) => show.contains(e.displayName)); | 828 elements.retainWhere((e) => show.contains(e.displayName)); |
788 } else if (c is HideElementCombinator) { | 829 } else if (c is HideElementCombinator) { |
789 var hide = c.hiddenNames.toSet(); | 830 var hide = c.hiddenNames.toSet(); |
790 elements.removeWhere((e) => hide.contains(e.displayName)); | 831 elements.removeWhere((e) => hide.contains(e.displayName)); |
791 } | 832 } |
792 } | 833 } |
793 } | 834 } |
OLD | NEW |