| 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 |
| 11 import 'package:html5lib/dom.dart' show Document, Element, Text; | 11 import 'package:html5lib/dom.dart' show Document, Element, Text; |
| 12 import 'package:html5lib/dom_parsing.dart'; | 12 import 'package:html5lib/dom_parsing.dart'; |
| 13 import 'package:html5lib/parser.dart' show parseFragment; | 13 import 'package:html5lib/parser.dart' show parseFragment; |
| 14 import 'package:analyzer/src/generated/ast.dart'; | 14 import 'package:analyzer/src/generated/ast.dart'; |
| 15 import 'package:analyzer/src/generated/element.dart' hide Element; | 15 import 'package:analyzer/src/generated/element.dart' hide Element; |
| 16 import 'package:analyzer/src/generated/element.dart' as analyzer show Element; | 16 import 'package:analyzer/src/generated/element.dart' as analyzer show Element; |
| 17 import 'package:barback/barback.dart'; | 17 import 'package:barback/barback.dart'; |
| 18 import 'package:code_transformers/messages/build_logger.dart'; |
| 18 import 'package:path/path.dart' as path; | 19 import 'package:path/path.dart' as path; |
| 19 import 'package:source_span/source_span.dart'; | 20 import 'package:source_span/source_span.dart'; |
| 20 import 'package:smoke/codegen/generator.dart'; | 21 import 'package:smoke/codegen/generator.dart'; |
| 21 import 'package:smoke/codegen/recorder.dart'; | 22 import 'package:smoke/codegen/recorder.dart'; |
| 22 import 'package:code_transformers/resolver.dart'; | 23 import 'package:code_transformers/resolver.dart'; |
| 23 import 'package:code_transformers/src/dart_sdk.dart'; | 24 import 'package:code_transformers/src/dart_sdk.dart'; |
| 24 import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens; | 25 import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens; |
| 25 | 26 |
| 26 import 'package:polymer_expressions/expression.dart' as pe; | 27 import 'package:polymer_expressions/expression.dart' as pe; |
| 27 import 'package:polymer_expressions/parser.dart' as pe; | 28 import 'package:polymer_expressions/parser.dart' as pe; |
| 28 import 'package:polymer_expressions/visitor.dart' as pe; | 29 import 'package:polymer_expressions/visitor.dart' as pe; |
| 29 | 30 |
| 31 import 'common.dart'; |
| 30 import 'import_inliner.dart' show ImportInliner; // just for docs. | 32 import 'import_inliner.dart' show ImportInliner; // just for docs. |
| 31 import 'common.dart'; | 33 import 'messages.dart'; |
| 32 import 'wrapped_logger.dart'; | |
| 33 | 34 |
| 34 /// Combines Dart script tags into a single script tag, and creates a new Dart | 35 /// Combines Dart script tags into a single script tag, and creates a new Dart |
| 35 /// file that calls the main function of each of the original script tags. | 36 /// file that calls the main function of each of the original script tags. |
| 36 /// | 37 /// |
| 37 /// This transformer assumes that all script tags point to external files. To | 38 /// This transformer assumes that all script tags point to external files. To |
| 38 /// support script tags with inlined code, use this transformer after running | 39 /// support script tags with inlined code, use this transformer after running |
| 39 /// [ImportInliner] on an earlier phase. | 40 /// [ImportInliner] on an earlier phase. |
| 40 /// | 41 /// |
| 41 /// Internally, this transformer will convert each script tag into an import | 42 /// Internally, this transformer will convert each script tag into an import |
| 42 /// statement to a library, and then uses `initPolymer` (see polymer.dart) to | 43 /// statement to a library, and then uses `initPolymer` (see polymer.dart) to |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 } | 102 } |
| 102 | 103 |
| 103 Future apply(Transform transform) => | 104 Future apply(Transform transform) => |
| 104 new _ScriptCompactor(transform, options, resolvers).apply(); | 105 new _ScriptCompactor(transform, options, resolvers).apply(); |
| 105 } | 106 } |
| 106 | 107 |
| 107 /// Helper class mainly use to flatten the async code. | 108 /// Helper class mainly use to flatten the async code. |
| 108 class _ScriptCompactor extends PolymerTransformer { | 109 class _ScriptCompactor extends PolymerTransformer { |
| 109 final TransformOptions options; | 110 final TransformOptions options; |
| 110 final Transform transform; | 111 final Transform transform; |
| 111 final TransformLogger logger; | 112 final BuildLogger logger; |
| 112 final AssetId docId; | 113 final AssetId docId; |
| 113 final AssetId bootstrapId; | 114 final AssetId bootstrapId; |
| 114 | 115 |
| 115 /// HTML document parsed from [docId]. | 116 /// HTML document parsed from [docId]. |
| 116 Document document; | 117 Document document; |
| 117 | 118 |
| 118 /// List of ids for each Dart entry script tag (the main tag and any tag | 119 /// List of ids for each Dart entry script tag (the main tag and any tag |
| 119 /// included on each custom element definition). | 120 /// included on each custom element definition). |
| 120 List<AssetId> entryLibraries; | 121 List<AssetId> entryLibraries; |
| 121 | 122 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 139 Resolver resolver; | 140 Resolver resolver; |
| 140 | 141 |
| 141 /// Code generator used to create the static initialization for smoke. | 142 /// Code generator used to create the static initialization for smoke. |
| 142 final generator = new SmokeCodeGenerator(); | 143 final generator = new SmokeCodeGenerator(); |
| 143 | 144 |
| 144 _SubExpressionVisitor expressionVisitor; | 145 _SubExpressionVisitor expressionVisitor; |
| 145 | 146 |
| 146 _ScriptCompactor(Transform transform, options, this.resolvers) | 147 _ScriptCompactor(Transform transform, options, this.resolvers) |
| 147 : transform = transform, | 148 : transform = transform, |
| 148 options = options, | 149 options = options, |
| 149 logger = options.releaseMode ? transform.logger : | 150 logger = new BuildLogger( |
| 150 new WrappedLogger(transform, convertErrorsToWarnings: true), | 151 transform, convertErrorsToWarnings: !options.releaseMode), |
| 151 docId = transform.primaryInput.id, | 152 docId = transform.primaryInput.id, |
| 152 bootstrapId = transform.primaryInput.id.addExtension('_bootstrap.dart'); | 153 bootstrapId = transform.primaryInput.id.addExtension('_bootstrap.dart'); |
| 153 | 154 |
| 154 Future apply() => | 155 Future apply() => |
| 155 _loadDocument() | 156 _loadDocument() |
| 156 .then(_loadEntryLibraries) | 157 .then(_loadEntryLibraries) |
| 157 .then(_processHtml) | 158 .then(_processHtml) |
| 158 .then(_emitNewEntrypoint) | 159 .then(_emitNewEntrypoint) |
| 159 .then((_) { | 160 .then((_) { |
| 160 // Write out the logs collected by our [WrappedLogger]. | 161 // Write out the logs collected by our [BuildLogger]. |
| 161 if (options.injectBuildLogsInOutput && logger is WrappedLogger) { | 162 if (options.injectBuildLogsInOutput) return logger.writeOutput(); |
| 162 return (logger as WrappedLogger).writeOutput(); | |
| 163 } | |
| 164 }); | 163 }); |
| 165 | 164 |
| 166 /// Loads the primary input as an html document. | 165 /// Loads the primary input as an html document. |
| 167 Future _loadDocument() => | 166 Future _loadDocument() => |
| 168 readPrimaryAsHtml(transform).then((doc) { document = doc; }); | 167 readPrimaryAsHtml(transform, logger).then((doc) { document = doc; }); |
| 169 | 168 |
| 170 /// Populates [entryLibraries] as a list containing the asset ids of each | 169 /// Populates [entryLibraries] as a list containing the asset ids of each |
| 171 /// library loaded on a script tag. The actual work of computing this is done | 170 /// library loaded on a script tag. The actual work of computing this is done |
| 172 /// in an earlier phase and emited in the `entrypoint._data` asset. | 171 /// in an earlier phase and emited in the `entrypoint._data` asset. |
| 173 Future _loadEntryLibraries(_) => | 172 Future _loadEntryLibraries(_) => |
| 174 transform.readInputAsString(docId.addExtension('._data')).then((data) { | 173 transform.readInputAsString(docId.addExtension('._data')).then((data) { |
| 175 var map = JSON.decode(data); | 174 var map = JSON.decode(data); |
| 176 experimentalBootstrap = map['experimental_bootstrap']; | 175 experimentalBootstrap = map['experimental_bootstrap']; |
| 177 entryLibraries = map['script_ids'] | 176 entryLibraries = map['script_ids'] |
| 178 .map((id) => new AssetId.deserialize(id)) | 177 .map((id) => new AssetId.deserialize(id)) |
| 179 .toList(); | 178 .toList(); |
| 179 return Future.forEach(entryLibraries, logger.addLogFilesFromAsset); |
| 180 }); | 180 }); |
| 181 | 181 |
| 182 /// Removes unnecessary script tags, and identifies the main entry point Dart | 182 /// Removes unnecessary script tags, and identifies the main entry point Dart |
| 183 /// script tag (if any). | 183 /// script tag (if any). |
| 184 void _processHtml(_) { | 184 void _processHtml(_) { |
| 185 for (var tag in document.querySelectorAll('script')) { | 185 for (var tag in document.querySelectorAll('script')) { |
| 186 var src = tag.attributes['src']; | 186 var src = tag.attributes['src']; |
| 187 if (src == 'packages/polymer/boot.js') { | 187 if (src == 'packages/polymer/boot.js') { |
| 188 tag.remove(); | 188 tag.remove(); |
| 189 continue; | 189 continue; |
| 190 } | 190 } |
| 191 if (tag.attributes['type'] == 'application/dart') { | 191 if (tag.attributes['type'] == 'application/dart') { |
| 192 logger.warning('unexpected script. The ' | 192 logger.warning(INTERNAL_ERROR_UNEXPECTED_SCRIPT, span: tag.sourceSpan); |
| 193 'ScriptCompactor transformer should run after running the ' | |
| 194 'ImportInliner', span: tag.sourceSpan); | |
| 195 } | 193 } |
| 196 } | 194 } |
| 197 } | 195 } |
| 198 | 196 |
| 199 /// Emits the main HTML and Dart bootstrap code for the application. If there | 197 /// Emits the main HTML and Dart bootstrap code for the application. If there |
| 200 /// were not Dart entry point files, then this simply emits the original HTML. | 198 /// were not Dart entry point files, then this simply emits the original HTML. |
| 201 Future _emitNewEntrypoint(_) { | 199 Future _emitNewEntrypoint(_) { |
| 202 // If we don't find code, there is nothing to do. | 200 // If we don't find code, there is nothing to do. |
| 203 if (entryLibraries.isEmpty) return null; | 201 if (entryLibraries.isEmpty) return null; |
| 204 return _initResolver() | 202 return _initResolver() |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 // Check whether the class has a @CustomTag annotation. Typically we expect | 278 // Check whether the class has a @CustomTag annotation. Typically we expect |
| 281 // a single @CustomTag, but it's possible to have several. | 279 // a single @CustomTag, but it's possible to have several. |
| 282 var tagNames = []; | 280 var tagNames = []; |
| 283 for (var meta in cls.node.metadata) { | 281 for (var meta in cls.node.metadata) { |
| 284 var tagName = _extractTagName(meta, cls); | 282 var tagName = _extractTagName(meta, cls); |
| 285 if (tagName != null) tagNames.add(tagName); | 283 if (tagName != null) tagNames.add(tagName); |
| 286 } | 284 } |
| 287 | 285 |
| 288 if (cls.isPrivate && tagNames.isNotEmpty) { | 286 if (cls.isPrivate && tagNames.isNotEmpty) { |
| 289 var name = tagNames.first; | 287 var name = tagNames.first; |
| 290 logger.error('@CustomTag is not currently supported on private classes:' | 288 logger.error(PRIVATE_CUSTOM_TAG.create( |
| 291 ' $name. Consider making this class public, or create a ' | 289 {'name': name, 'class': cls.name}), |
| 292 'public initialization method marked with `@initMethod` that calls ' | |
| 293 '`Polymer.register($name, ${cls.name})`.', | |
| 294 span: _spanForNode(cls, cls.node.name)); | 290 span: _spanForNode(cls, cls.node.name)); |
| 295 return; | 291 return; |
| 296 } | 292 } |
| 297 | 293 |
| 298 // Include #registerCallback if it exists. Note that by default lookupMember | 294 // Include #registerCallback if it exists. Note that by default lookupMember |
| 299 // and query will also add the corresponding getters and setters. | 295 // and query will also add the corresponding getters and setters. |
| 300 recorder.lookupMember(cls, 'registerCallback'); | 296 recorder.lookupMember(cls, 'registerCallback'); |
| 301 | 297 |
| 302 // Include methods that end with *Changed. | 298 // Include methods that end with *Changed. |
| 303 recorder.runQuery(cls, new QueryOptions( | 299 recorder.runQuery(cls, new QueryOptions( |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 365 } | 361 } |
| 366 | 362 |
| 367 /// Extract the first argument of an annotation and validate that it's type is | 363 /// Extract the first argument of an annotation and validate that it's type is |
| 368 /// String. For instance, return "bar" from `@Foo("bar")`. | 364 /// String. For instance, return "bar" from `@Foo("bar")`. |
| 369 String _extractFirstAnnotationArgument(Annotation meta, String name, | 365 String _extractFirstAnnotationArgument(Annotation meta, String name, |
| 370 analyzer.Element context) { | 366 analyzer.Element context) { |
| 371 | 367 |
| 372 // Read argument from the AST | 368 // Read argument from the AST |
| 373 var args = meta.arguments.arguments; | 369 var args = meta.arguments.arguments; |
| 374 if (args == null || args.length == 0) { | 370 if (args == null || args.length == 0) { |
| 375 logger.warning('Missing argument in @$name annotation', | 371 logger.warning(MISSING_ANNOTATION_ARGUMENT.create({'name': name}), |
| 376 span: _spanForNode(context, meta)); | 372 span: _spanForNode(context, meta)); |
| 377 return null; | 373 return null; |
| 378 } | 374 } |
| 379 | 375 |
| 380 var lib = context; | 376 var lib = context; |
| 381 while (lib is! LibraryElement) lib = lib.enclosingElement; | 377 while (lib is! LibraryElement) lib = lib.enclosingElement; |
| 382 var res = resolver.evaluateConstant(lib, args[0]); | 378 var res = resolver.evaluateConstant(lib, args[0]); |
| 383 if (!res.isValid || res.value.type != types.stringType) { | 379 if (!res.isValid || res.value.type != types.stringType) { |
| 384 logger.warning('The parameter to @$name seems to be invalid.', | 380 logger.warning(INVALID_ANNOTATION_ARGUMENT.create({'name': name}), |
| 385 span: _spanForNode(context, args[0])); | 381 span: _spanForNode(context, args[0])); |
| 386 return null; | 382 return null; |
| 387 } | 383 } |
| 388 return res.value.stringValue; | 384 return res.value.stringValue; |
| 389 } | 385 } |
| 390 | 386 |
| 391 /// Adds the top-level [function] as an initalizer if it's marked with | 387 /// Adds the top-level [function] as an initalizer if it's marked with |
| 392 /// `@initMethod`. | 388 /// `@initMethod`. |
| 393 _processFunction(FunctionElement function, AssetId id) { | 389 _processFunction(FunctionElement function, AssetId id) { |
| 394 bool initMethodFound = false; | 390 bool initMethodFound = false; |
| 395 for (var meta in function.metadata) { | 391 for (var meta in function.metadata) { |
| 396 var e = meta.element; | 392 var e = meta.element; |
| 397 if (e is PropertyAccessorElement && | 393 if (e is PropertyAccessorElement && |
| 398 e.variable == types.initMethodElement) { | 394 e.variable == types.initMethodElement) { |
| 399 initMethodFound = true; | 395 initMethodFound = true; |
| 400 break; | 396 break; |
| 401 } | 397 } |
| 402 } | 398 } |
| 403 if (!initMethodFound) return; | 399 if (!initMethodFound) return; |
| 404 if (function.isPrivate) { | 400 if (function.isPrivate) { |
| 405 logger.error('@initMethod is no longer supported on private ' | 401 logger.error(PRIVATE_INIT_METHOD.create({'name': function.displayName}), |
| 406 'functions: ${function.displayName}', | |
| 407 span: _spanForNode(function, function.node.name)); | 402 span: _spanForNode(function, function.node.name)); |
| 408 return; | 403 return; |
| 409 } | 404 } |
| 410 initializers.add(new _InitMethodInitializer(id, function.displayName)); | 405 initializers.add(new _InitMethodInitializer(id, function.displayName)); |
| 411 } | 406 } |
| 412 | 407 |
| 413 /// Process members that are annotated with `@ComputedProperty` and records | 408 /// Process members that are annotated with `@ComputedProperty` and records |
| 414 /// the accessors of their expressions. | 409 /// the accessors of their expressions. |
| 415 _processComputedExpressions(List<analyzer.Element> computed) { | 410 _processComputedExpressions(List<analyzer.Element> computed) { |
| 416 var constructor = types.computedPropertyElement.constructors.first; | 411 var constructor = types.computedPropertyElement.constructors.first; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 464 | 459 |
| 465 // Include initializers to switch from mirrors_loader to static_loader. | 460 // Include initializers to switch from mirrors_loader to static_loader. |
| 466 if (!initializers.isEmpty) { | 461 if (!initializers.isEmpty) { |
| 467 code.writeln(); | 462 code.writeln(); |
| 468 for (var init in initializers) { | 463 for (var init in initializers) { |
| 469 var initCode = init.asCode(prefixes[init.assetId]); | 464 var initCode = init.asCode(prefixes[init.assetId]); |
| 470 code.write(" $initCode,\n"); | 465 code.write(" $initCode,\n"); |
| 471 } | 466 } |
| 472 code.writeln(' ]);'); | 467 code.writeln(' ]);'); |
| 473 } else { | 468 } else { |
| 474 if (experimentalBootstrap) logger.warning(NO_INITIALIZERS_ERROR); | 469 if (experimentalBootstrap) logger.warning(NO_INITIALIZATION); |
| 475 code.writeln(']);'); | 470 code.writeln(']);'); |
| 476 } | 471 } |
| 477 if (!experimentalBootstrap) { | 472 if (!experimentalBootstrap) { |
| 478 code.writeln(' i${entryLibraries.length - 1}.main();'); | 473 code.writeln(' i${entryLibraries.length - 1}.main();'); |
| 479 } | 474 } |
| 480 | 475 |
| 481 // End of main(). | 476 // End of main(). |
| 482 code.writeln('}'); | 477 code.writeln('}'); |
| 483 transform.addOutput(new Asset.fromString(bootstrapId, code.toString())); | 478 transform.addOutput(new Asset.fromString(bootstrapId, code.toString())); |
| 484 | 479 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 529 String asCode(String prefix) => | 524 String asCode(String prefix) => |
| 530 "() => Polymer.register('$tagName', $prefix.$typeName)"; | 525 "() => Polymer.register('$tagName', $prefix.$typeName)"; |
| 531 } | 526 } |
| 532 | 527 |
| 533 const MAIN_HEADER = """ | 528 const MAIN_HEADER = """ |
| 534 library app_bootstrap; | 529 library app_bootstrap; |
| 535 | 530 |
| 536 import 'package:polymer/polymer.dart'; | 531 import 'package:polymer/polymer.dart'; |
| 537 """; | 532 """; |
| 538 | 533 |
| 539 const NO_INITIALIZERS_ERROR = | |
| 540 'No polymer initializers were found. Make sure to either ' | |
| 541 'annotate your polymer elements with @CustomTag or include a ' | |
| 542 'top level method annotated with @initMethod that registers your ' | |
| 543 'elements. Both annotations are defined in the polymer library (' | |
| 544 'package:polymer/polymer.dart).'; | |
| 545 | 534 |
| 546 /// An html visitor that: | 535 /// An html visitor that: |
| 547 /// * finds all polymer expressions and records the getters and setters that | 536 /// * finds all polymer expressions and records the getters and setters that |
| 548 /// will be needed to evaluate them at runtime. | 537 /// will be needed to evaluate them at runtime. |
| 549 /// * extracts all attributes declared in the `attribute` attributes of | 538 /// * extracts all attributes declared in the `attribute` attributes of |
| 550 /// polymer elements. | 539 /// polymer elements. |
| 551 class _HtmlExtractor extends TreeVisitor { | 540 class _HtmlExtractor extends TreeVisitor { |
| 552 final Map<String, List<String>> publishedAttributes; | 541 final Map<String, List<String>> publishedAttributes; |
| 553 final SmokeCodeGenerator generator; | 542 final SmokeCodeGenerator generator; |
| 554 final _SubExpressionVisitor expressionVisitor; | 543 final _SubExpressionVisitor expressionVisitor; |
| 555 final TransformLogger logger; | 544 final BuildLogger logger; |
| 556 bool _inTemplate = false; | 545 bool _inTemplate = false; |
| 557 | 546 |
| 558 _HtmlExtractor(this.logger, this.generator, this.publishedAttributes, | 547 _HtmlExtractor(this.logger, this.generator, this.publishedAttributes, |
| 559 this.expressionVisitor); | 548 this.expressionVisitor); |
| 560 | 549 |
| 561 void visitElement(Element node) { | 550 void visitElement(Element node) { |
| 562 if (_inTemplate) _processNormalElement(node); | 551 if (_inTemplate) _processNormalElement(node); |
| 563 if (node.localName == 'polymer-element') { | 552 if (node.localName == 'polymer-element') { |
| 564 _processPolymerElement(node); | 553 _processPolymerElement(node); |
| 565 _processNormalElement(node); | 554 _processNormalElement(node); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 618 _addExpression(exp, isEvent, isTwoWay, node.sourceSpan); | 607 _addExpression(exp, isEvent, isTwoWay, node.sourceSpan); |
| 619 } | 608 } |
| 620 }); | 609 }); |
| 621 } | 610 } |
| 622 | 611 |
| 623 void _addExpression(String stringExpression, bool inEvent, bool isTwoWay, | 612 void _addExpression(String stringExpression, bool inEvent, bool isTwoWay, |
| 624 SourceSpan span) { | 613 SourceSpan span) { |
| 625 | 614 |
| 626 if (inEvent) { | 615 if (inEvent) { |
| 627 if (stringExpression.startsWith('@')) { | 616 if (stringExpression.startsWith('@')) { |
| 628 logger.warning('event bindings with @ are no longer supported', | 617 logger.warning(AT_EXPRESSION_REMOVED, span: span); |
| 629 span: span); | |
| 630 return; | 618 return; |
| 631 } | 619 } |
| 632 | 620 |
| 633 if (stringExpression == '') return; | 621 if (stringExpression == '') return; |
| 634 if (stringExpression.startsWith('_')) { | 622 if (stringExpression.startsWith('_')) { |
| 635 logger.warning('private symbols cannot be used in event handlers', | 623 logger.warning(NO_PRIVATE_EVENT_HANDLERS, span: span); |
| 636 span: span); | |
| 637 return; | 624 return; |
| 638 } | 625 } |
| 639 generator.addGetter(stringExpression); | 626 generator.addGetter(stringExpression); |
| 640 generator.addSymbol(stringExpression); | 627 generator.addSymbol(stringExpression); |
| 641 } | 628 } |
| 642 expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span); | 629 expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span); |
| 643 } | 630 } |
| 644 } | 631 } |
| 645 | 632 |
| 646 /// A polymer-expression visitor that records every getter and setter that will | 633 /// A polymer-expression visitor that records every getter and setter that will |
| 647 /// be needed to evaluate a single expression at runtime. | 634 /// be needed to evaluate a single expression at runtime. |
| 648 class _SubExpressionVisitor extends pe.RecursiveVisitor { | 635 class _SubExpressionVisitor extends pe.RecursiveVisitor { |
| 649 final SmokeCodeGenerator generator; | 636 final SmokeCodeGenerator generator; |
| 650 final TransformLogger logger; | 637 final BuildLogger logger; |
| 651 bool _includeSetter; | 638 bool _includeSetter; |
| 652 SourceSpan _currentSpan; | 639 SourceSpan _currentSpan; |
| 653 | 640 |
| 654 _SubExpressionVisitor(this.generator, this.logger); | 641 _SubExpressionVisitor(this.generator, this.logger); |
| 655 | 642 |
| 656 /// Visit [exp], and record getters and setters that are needed in order to | 643 /// Visit [exp], and record getters and setters that are needed in order to |
| 657 /// evaluate it at runtime. [includeSetter] is only true if this expression | 644 /// evaluate it at runtime. [includeSetter] is only true if this expression |
| 658 /// occured in a context where it could be updated, for example in two-way | 645 /// occured in a context where it could be updated, for example in two-way |
| 659 /// bindings such as `<input value={{exp}}>`. | 646 /// bindings such as `<input value={{exp}}>`. |
| 660 void run(pe.Expression exp, bool includeSetter, span) { | 647 void run(pe.Expression exp, bool includeSetter, span) { |
| 661 _currentSpan = span; | 648 _currentSpan = span; |
| 662 _includeSetter = includeSetter; | 649 _includeSetter = includeSetter; |
| 663 visit(exp); | 650 visit(exp); |
| 664 } | 651 } |
| 665 | 652 |
| 666 /// Adds a getter and symbol for [name], and optionally a setter. | 653 /// Adds a getter and symbol for [name], and optionally a setter. |
| 667 _add(String name) { | 654 _add(String name) { |
| 668 if (name.startsWith('_')) { | 655 if (name.startsWith('_')) { |
| 669 logger.warning('private symbols are not supported', span: _currentSpan); | 656 logger.warning(NO_PRIVATE_SYMBOLS_IN_BINDINGS, span: _currentSpan); |
| 670 return; | 657 return; |
| 671 } | 658 } |
| 672 generator.addGetter(name); | 659 generator.addGetter(name); |
| 673 generator.addSymbol(name); | 660 generator.addSymbol(name); |
| 674 if (_includeSetter) generator.addSetter(name); | 661 if (_includeSetter) generator.addSetter(name); |
| 675 } | 662 } |
| 676 | 663 |
| 677 void preVisitExpression(e) { | 664 void preVisitExpression(e) { |
| 678 // For two-way bindings the outermost expression may be updated, so we need | 665 // For two-way bindings the outermost expression may be updated, so we need |
| 679 // both the getter and the setter, but we only need the getter for | 666 // both the getter and the setter, but we only need the getter for |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 851 for (var c in combinators) { | 838 for (var c in combinators) { |
| 852 if (c is ShowElementCombinator) { | 839 if (c is ShowElementCombinator) { |
| 853 var show = c.shownNames.toSet(); | 840 var show = c.shownNames.toSet(); |
| 854 elements.retainWhere((e) => show.contains(e.displayName)); | 841 elements.retainWhere((e) => show.contains(e.displayName)); |
| 855 } else if (c is HideElementCombinator) { | 842 } else if (c is HideElementCombinator) { |
| 856 var hide = c.hiddenNames.toSet(); | 843 var hide = c.hiddenNames.toSet(); |
| 857 elements.removeWhere((e) => hide.contains(e.displayName)); | 844 elements.removeWhere((e) => hide.contains(e.displayName)); |
| 858 } | 845 } |
| 859 } | 846 } |
| 860 } | 847 } |
| OLD | NEW |