| Index: packages/polymer/lib/src/build/polymer_smoke_generator.dart
 | 
| diff --git a/packages/polymer/lib/src/build/polymer_smoke_generator.dart b/packages/polymer/lib/src/build/polymer_smoke_generator.dart
 | 
| deleted file mode 100644
 | 
| index 11664f62f651370552e4d6bffbbf4f1fdd37ffb3..0000000000000000000000000000000000000000
 | 
| --- a/packages/polymer/lib/src/build/polymer_smoke_generator.dart
 | 
| +++ /dev/null
 | 
| @@ -1,736 +0,0 @@
 | 
| -// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
 | 
| -// for details. All rights reserved. Use of this source code is governed by a
 | 
| -// BSD-style license that can be found in the LICENSE file.
 | 
| -
 | 
| -/// Transfomer that combines multiple Dart script tags into a single one.
 | 
| -library polymer.src.build.polymer_smoke_generator;
 | 
| -
 | 
| -import 'dart:async';
 | 
| -
 | 
| -import 'package:html/dom.dart' show Document, Element, Text;
 | 
| -import 'package:html/dom_parsing.dart';
 | 
| -import 'package:html/parser.dart' show parseFragment;
 | 
| -import 'package:analyzer/src/generated/ast.dart';
 | 
| -import 'package:analyzer/src/generated/element.dart' hide Element;
 | 
| -import 'package:analyzer/src/generated/element.dart' as analyzer show Element;
 | 
| -import 'package:barback/barback.dart';
 | 
| -import 'package:code_transformers/messages/build_logger.dart';
 | 
| -import 'package:code_transformers/assets.dart';
 | 
| -import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
 | 
| -import 'package:path/path.dart' as path;
 | 
| -import 'package:source_span/source_span.dart';
 | 
| -import 'package:smoke/codegen/generator.dart';
 | 
| -import 'package:smoke/codegen/recorder.dart';
 | 
| -import 'package:code_transformers/resolver.dart';
 | 
| -import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens;
 | 
| -
 | 
| -import 'package:polymer_expressions/expression.dart' as pe;
 | 
| -import 'package:polymer_expressions/parser.dart' as pe;
 | 
| -import 'package:polymer_expressions/visitor.dart' as pe;
 | 
| -
 | 
| -import 'package:web_components/build/import_crawler.dart';
 | 
| -
 | 
| -import 'common.dart';
 | 
| -import 'messages.dart';
 | 
| -
 | 
| -/// Method to generate a bootstrap file for Polymer given a [Transform] and a
 | 
| -/// [Resolver]. This can be used inside any transformer to share the [Resolver]
 | 
| -/// with other steps.
 | 
| -Future<Asset> generatePolymerBootstrap(Transform transform, Resolver resolver,
 | 
| -    AssetId entryPointId, AssetId bootstrapId, Document document,
 | 
| -    TransformOptions options, {AssetId resolveFromId}) {
 | 
| -  return new PolymerSmokeGenerator(
 | 
| -      transform, resolver, entryPointId, bootstrapId, document, options,
 | 
| -      resolveFromId: resolveFromId).apply();
 | 
| -}
 | 
| -
 | 
| -class PolymerSmokeGeneratorTransformer extends Transformer
 | 
| -    with PolymerTransformer {
 | 
| -  final Resolvers resolvers;
 | 
| -  final TransformOptions options;
 | 
| -
 | 
| -  PolymerSmokeGeneratorTransformer(this.options, {String sdkDir})
 | 
| -      // TODO(sigmund): consider restoring here a resolver that uses the real
 | 
| -      // SDK once the analyzer is lazy and only an resolves what it needs:
 | 
| -      //: resolvers = new Resolvers(sdkDir != null ? sdkDir : dartSdkDirectory);
 | 
| -      : resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
 | 
| -
 | 
| -  /// Only run on entry point .html files.
 | 
| -  bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
 | 
| -
 | 
| -  Future apply(Transform transform) {
 | 
| -    var logger = new BuildLogger(transform,
 | 
| -        convertErrorsToWarnings: !options.releaseMode,
 | 
| -        detailsUri: 'http://goo.gl/5HPeuP');
 | 
| -    var primaryId = transform.primaryInput.id;
 | 
| -    return readPrimaryAsHtml(transform, logger).then((document) {
 | 
| -      var script = document.querySelector('script[type="application/dart"]');
 | 
| -      if (script == null) return null;
 | 
| -      var entryScriptId = uriToAssetId(
 | 
| -          primaryId, script.attributes['src'], logger, script.sourceSpan);
 | 
| -      var bootstrapId = primaryId.addExtension('_bootstrap.dart');
 | 
| -      script.attributes['src'] = path.basename(bootstrapId.path);
 | 
| -
 | 
| -      return resolvers.get(transform, [entryScriptId]).then((resolver) {
 | 
| -        return generatePolymerBootstrap(transform, resolver, entryScriptId,
 | 
| -            bootstrapId, document, options).then((bootstrapAsset) {
 | 
| -          transform.addOutput(bootstrapAsset);
 | 
| -          transform
 | 
| -              .addOutput(new Asset.fromString(primaryId, document.outerHtml));
 | 
| -          resolver.release();
 | 
| -        });
 | 
| -      });
 | 
| -    });
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -/// Class which generates the static smoke configuration for polymer.
 | 
| -// TODO(jakemac): Investigate further turning this into an [InitializerPlugin].
 | 
| -// The main difficulty is this actually recognizes any class which extends the
 | 
| -// [PolymerElement] class, not just things annotated with [CustomTag].
 | 
| -class PolymerSmokeGenerator {
 | 
| -  final TransformOptions options;
 | 
| -  final Transform transform;
 | 
| -  final BuildLogger logger;
 | 
| -  final AssetId docId;
 | 
| -  final AssetId bootstrapId;
 | 
| -
 | 
| -  /// Id of the Dart script found in the document (can only be one).
 | 
| -  AssetId entryScriptId;
 | 
| -
 | 
| -  /// Id of the Dart script to start resolution from.
 | 
| -  AssetId resolveFromId;
 | 
| -
 | 
| -  /// HTML document parsed from [docId].
 | 
| -  Document document;
 | 
| -
 | 
| -  /// Attributes published on a custom-tag. We make these available via
 | 
| -  /// reflection even if @published was not used.
 | 
| -  final Map<String, List<String>> publishedAttributes = {};
 | 
| -
 | 
| -  /// Resolved types used for analyzing the user's sources and generating code.
 | 
| -  _ResolvedTypes types;
 | 
| -
 | 
| -  /// The resolver instance associated with a single run of this transformer.
 | 
| -  Resolver resolver;
 | 
| -
 | 
| -  /// Code generator used to create the static initialization for smoke.
 | 
| -  final generator = new SmokeCodeGenerator();
 | 
| -
 | 
| -  _SubExpressionVisitor expressionVisitor;
 | 
| -
 | 
| -  PolymerSmokeGenerator(Transform transform, Resolver resolver,
 | 
| -      this.entryScriptId, this.bootstrapId, this.document, options,
 | 
| -      {this.resolveFromId})
 | 
| -      : transform = transform,
 | 
| -        options = options,
 | 
| -        logger = new BuildLogger(transform,
 | 
| -            convertErrorsToWarnings: !options.releaseMode,
 | 
| -            detailsUri: 'http://goo.gl/5HPeuP'),
 | 
| -        docId = transform.primaryInput.id,
 | 
| -        resolver = resolver {
 | 
| -    _ResolvedTypes.logger = logger;
 | 
| -    types = new _ResolvedTypes(resolver);
 | 
| -    if (resolveFromId == null) resolveFromId = entryScriptId;
 | 
| -  }
 | 
| -
 | 
| -  Future<Asset> apply() {
 | 
| -    return _extractUsesOfMirrors().then((_) {
 | 
| -      var bootstrapAsset = _buildBootstrap();
 | 
| -      _modifyDocument();
 | 
| -
 | 
| -      // Write out the logs collected by our [BuildLogger].
 | 
| -      if (options.injectBuildLogsInOutput) {
 | 
| -        return logger.writeOutput().then((_) => bootstrapAsset);
 | 
| -      }
 | 
| -      return bootstrapAsset;
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  /// Inspects the entire program to find out anything that polymer accesses
 | 
| -  /// using mirrors and produces static information that can be used to replace
 | 
| -  /// the mirror-based loader and the uses of mirrors through the `smoke`
 | 
| -  /// package. This includes:
 | 
| -  ///
 | 
| -  ///   * visiting polymer-expressions to extract getters and setters,
 | 
| -  ///   * looking for published fields of custom elements, and
 | 
| -  ///   * looking for event handlers and callbacks of change notifications.
 | 
| -  ///
 | 
| -  Future _extractUsesOfMirrors() {
 | 
| -    // Generate getters and setters needed to evaluate polymer expressions, and
 | 
| -    // extract information about published attributes.
 | 
| -    expressionVisitor = new _SubExpressionVisitor(generator, logger);
 | 
| -
 | 
| -    return new ImportCrawler(transform, transform.primaryInput.id, logger,
 | 
| -        primaryDocument: document).crawlImports().then((documentData) {
 | 
| -      for (var data in documentData.values) {
 | 
| -        new _HtmlExtractor(
 | 
| -                logger, generator, publishedAttributes, expressionVisitor)
 | 
| -            .visit(data.document);
 | 
| -      }
 | 
| -
 | 
| -      // Create a recorder that uses analyzer data to feed data to [generator].
 | 
| -      var recorder = new Recorder(generator,
 | 
| -          (lib) => resolver.getImportUri(lib, from: bootstrapId).toString());
 | 
| -
 | 
| -      // Process all classes to include special fields and methods in custom
 | 
| -      // element classes.
 | 
| -      _visitLibraries(resolver.getLibrary(resolveFromId), recorder);
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  _visitLibraries(LibraryElement library, Recorder recorder,
 | 
| -      [Set<LibraryElement> librariesSeen, Set<ClassElement> classesSeen]) {
 | 
| -    if (librariesSeen == null) librariesSeen = new Set<LibraryElement>();
 | 
| -    librariesSeen.add(library);
 | 
| -
 | 
| -    // Visit all our dependencies.
 | 
| -    for (var importedLibrary in _libraryDependencies(library)) {
 | 
| -      // Don't include anything from the sdk.
 | 
| -      if (importedLibrary.isInSdk) continue;
 | 
| -      if (librariesSeen.contains(importedLibrary)) continue;
 | 
| -      _visitLibraries(importedLibrary, recorder, librariesSeen, classesSeen);
 | 
| -    }
 | 
| -
 | 
| -    // After visiting dependencies, then visit classes in this library.
 | 
| -    if (classesSeen == null) classesSeen = new Set<ClassElement>();
 | 
| -    var classes = _visibleClassesOf(library);
 | 
| -    for (var clazz in classes) {
 | 
| -      _processClass(clazz, recorder);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  Iterable<LibraryElement> _libraryDependencies(LibraryElement library) {
 | 
| -    getLibrary(UriReferencedElement element) {
 | 
| -      if (element is ImportElement) return element.importedLibrary;
 | 
| -      if (element is ExportElement) return element.exportedLibrary;
 | 
| -    }
 | 
| -
 | 
| -    return (new List.from(library.imports)..addAll(library.exports))
 | 
| -        .map(getLibrary);
 | 
| -  }
 | 
| -
 | 
| -  /// Process a class ([cls]). If it contains an appropriate [CustomTag]
 | 
| -  /// annotation, we make sure to include everything that might be accessed or
 | 
| -  /// queried from them using the smoke package. In particular, polymer uses
 | 
| -  /// smoke for the following:
 | 
| -  ///    * invoke #registerCallback on custom elements classes, if present.
 | 
| -  ///    * query for methods ending in `*Changed`.
 | 
| -  ///    * query for methods with the `@ObserveProperty` annotation.
 | 
| -  ///    * query for non-final properties labeled with `@published`.
 | 
| -  ///    * read declarations of properties named in the `attributes` attribute.
 | 
| -  ///    * read/write the value of published properties .
 | 
| -  ///    * invoke methods in event handlers.
 | 
| -  _processClass(ClassElement cls, Recorder recorder) {
 | 
| -    if (!_hasPolymerMixin(cls)) return;
 | 
| -    if (cls.node is! ClassDeclaration) return;
 | 
| -    var node = cls.node as ClassDeclaration;
 | 
| -
 | 
| -    // Check whether the class has a @CustomTag annotation. Typically we expect
 | 
| -    // a single @CustomTag, but it's possible to have several.
 | 
| -    var tagNames = [];
 | 
| -    for (var meta in node.metadata) {
 | 
| -      var tagName = _extractTagName(meta, cls);
 | 
| -      if (tagName != null) tagNames.add(tagName);
 | 
| -    }
 | 
| -
 | 
| -    if (cls.isPrivate && tagNames.isNotEmpty) {
 | 
| -      var name = tagNames.first;
 | 
| -      logger.error(PRIVATE_CUSTOM_TAG.create({'name': name, 'class': cls.name}),
 | 
| -          span: _spanForNode(cls, node.name));
 | 
| -      return;
 | 
| -    }
 | 
| -
 | 
| -    // Include #registerCallback if it exists. Note that by default lookupMember
 | 
| -    // and query will also add the corresponding getters and setters.
 | 
| -    recorder.lookupMember(cls, 'registerCallback');
 | 
| -
 | 
| -    // Include methods that end with *Changed.
 | 
| -    recorder.runQuery(cls, new QueryOptions(
 | 
| -        includeFields: false,
 | 
| -        includeProperties: false,
 | 
| -        includeInherited: true,
 | 
| -        includeMethods: true,
 | 
| -        includeUpTo: types.htmlElementElement,
 | 
| -        matches: (n) => n.endsWith('Changed') && n != 'attributeChanged'));
 | 
| -
 | 
| -    // Include methods marked with @ObserveProperty.
 | 
| -    recorder.runQuery(cls, new QueryOptions(
 | 
| -        includeFields: false,
 | 
| -        includeProperties: false,
 | 
| -        includeInherited: true,
 | 
| -        includeMethods: true,
 | 
| -        includeUpTo: types.htmlElementElement,
 | 
| -        withAnnotations: [types.observePropertyElement]));
 | 
| -
 | 
| -    // Include @published and @observable properties.
 | 
| -    // Symbols in @published are used when resolving bindings on published
 | 
| -    // attributes, symbols for @observable are used via path observers when
 | 
| -    // implementing *Changed an @ObserveProperty.
 | 
| -    // TODO(sigmund): consider including only those symbols mentioned in
 | 
| -    // *Changed and @ObserveProperty instead.
 | 
| -    recorder.runQuery(cls, new QueryOptions(
 | 
| -        includeUpTo: types.htmlElementElement,
 | 
| -        withAnnotations: [
 | 
| -      types.publishedElement,
 | 
| -      types.observableElement,
 | 
| -      types.computedPropertyElement
 | 
| -    ]));
 | 
| -
 | 
| -    // Include @ComputedProperty and process their expressions
 | 
| -    var computed = [];
 | 
| -    recorder.runQuery(cls, new QueryOptions(
 | 
| -        includeUpTo: types.htmlElementElement,
 | 
| -        withAnnotations: [types.computedPropertyElement]), results: computed);
 | 
| -    _processComputedExpressions(computed);
 | 
| -
 | 
| -    for (var tagName in tagNames) {
 | 
| -      // Include also properties published via the `attributes` attribute.
 | 
| -      var attrs = publishedAttributes[tagName];
 | 
| -      if (attrs == null) continue;
 | 
| -      for (var attr in attrs) {
 | 
| -        recorder.lookupMember(cls, attr,
 | 
| -            recursive: true, includeUpTo: types.htmlElementElement);
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  /// Determines if [cls] or a supertype has a mixin of the Polymer class.
 | 
| -  bool _hasPolymerMixin(ClassElement cls) {
 | 
| -    while (cls != types.htmlElementElement) {
 | 
| -      for (var m in cls.mixins) {
 | 
| -        if (m.element == types.polymerClassElement) return true;
 | 
| -      }
 | 
| -      if (cls.supertype == null) return false;
 | 
| -      cls = cls.supertype.element;
 | 
| -    }
 | 
| -    return false;
 | 
| -  }
 | 
| -
 | 
| -  /// If [meta] is [CustomTag], extract the name associated with the tag.
 | 
| -  String _extractTagName(Annotation meta, ClassElement cls) {
 | 
| -    if (meta.element != types.customTagConstructor) return null;
 | 
| -    return _extractFirstAnnotationArgument(meta, 'CustomTag', cls);
 | 
| -  }
 | 
| -
 | 
| -  /// Extract the first argument of an annotation and validate that it's type is
 | 
| -  /// String. For instance, return "bar" from `@Foo("bar")`.
 | 
| -  String _extractFirstAnnotationArgument(
 | 
| -      Annotation meta, String name, analyzer.Element context) {
 | 
| -
 | 
| -    // Read argument from the AST
 | 
| -    var args = meta.arguments.arguments;
 | 
| -    if (args == null || args.length == 0) {
 | 
| -      logger.warning(MISSING_ANNOTATION_ARGUMENT.create({'name': name}),
 | 
| -          span: _spanForNode(context, meta));
 | 
| -      return null;
 | 
| -    }
 | 
| -
 | 
| -    var lib = context;
 | 
| -    while (lib is! LibraryElement) lib = lib.enclosingElement;
 | 
| -    var res = resolver.evaluateConstant(lib, args[0]);
 | 
| -    if (!res.isValid || res.value.type != types.stringType) {
 | 
| -      logger.warning(INVALID_ANNOTATION_ARGUMENT.create({'name': name}),
 | 
| -          span: _spanForNode(context, args[0]));
 | 
| -      return null;
 | 
| -    }
 | 
| -    return res.value.stringValue;
 | 
| -  }
 | 
| -
 | 
| -  /// Process members that are annotated with `@ComputedProperty` and records
 | 
| -  /// the accessors of their expressions.
 | 
| -  _processComputedExpressions(List<analyzer.Element> computed) {
 | 
| -    var constructor = types.computedPropertyElement.constructors.first;
 | 
| -    for (var member in computed) {
 | 
| -      for (var meta in member.node.metadata) {
 | 
| -        if (meta.element != constructor) continue;
 | 
| -        var expr =
 | 
| -            _extractFirstAnnotationArgument(meta, 'ComputedProperty', member);
 | 
| -        if (expr == null) continue;
 | 
| -        expressionVisitor.run(pe.parse(expr), true,
 | 
| -            _spanForNode(member.enclosingElement, meta.arguments.arguments[0]));
 | 
| -      }
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  // Builds the bootstrap Dart file asset.
 | 
| -  Asset _buildBootstrap() {
 | 
| -    StringBuffer code = new StringBuffer()..writeln(MAIN_HEADER);
 | 
| -
 | 
| -    // TODO(jakemac): Inject this at some other stage.
 | 
| -    // https://github.com/dart-lang/polymer-dart/issues/22
 | 
| -    if (options.injectBuildLogsInOutput) {
 | 
| -      code.writeln("import 'package:polymer/src/build/log_injector.dart';");
 | 
| -    }
 | 
| -
 | 
| -    var entryScriptUrl = assetUrlFor(entryScriptId, bootstrapId, logger);
 | 
| -    code.writeln("import '$entryScriptUrl' as i0;");
 | 
| -
 | 
| -    // Include smoke initialization.
 | 
| -    generator.writeImports(code);
 | 
| -    generator.writeTopLevelDeclarations(code);
 | 
| -    code.writeln('\nmain() {');
 | 
| -    code.write('  useGeneratedCode(');
 | 
| -    generator.writeStaticConfiguration(code);
 | 
| -    code.writeln(');');
 | 
| -
 | 
| -    // TODO(jakemac): Inject this at some other stage.
 | 
| -    // https://github.com/dart-lang/polymer-dart/issues/22
 | 
| -    if (options.injectBuildLogsInOutput) {
 | 
| -      var buildUrl = "${path.basename(docId.path)}$LOG_EXTENSION";
 | 
| -      code.writeln("  new LogInjector().injectLogsFromUrl('$buildUrl');");
 | 
| -    }
 | 
| -
 | 
| -    code.writeln('  configureForDeployment();');
 | 
| -    code.writeln('  return i0.main();');
 | 
| -
 | 
| -    // End of main().
 | 
| -    code.writeln('}');
 | 
| -    return new Asset.fromString(bootstrapId, code.toString());
 | 
| -  }
 | 
| -
 | 
| -  // Add the styles for the logger widget.
 | 
| -  // TODO(jakemac): Inject this at some other stage.
 | 
| -  // https://github.com/dart-lang/polymer-dart/issues/22
 | 
| -  void _modifyDocument() {
 | 
| -    if (options.injectBuildLogsInOutput) {
 | 
| -      document.head.append(parseFragment(
 | 
| -          '<link rel="stylesheet" type="text/css"'
 | 
| -          ' href="packages/polymer/src/build/log_injector.css">'));
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  _spanForNode(analyzer.Element context, AstNode node) {
 | 
| -    var file = resolver.getSourceFile(context);
 | 
| -    return file.span(node.offset, node.end);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -const MAIN_HEADER = """
 | 
| -library app_bootstrap;
 | 
| -
 | 
| -import 'package:polymer/polymer.dart';
 | 
| -""";
 | 
| -
 | 
| -/// An html visitor that:
 | 
| -///   * finds all polymer expressions and records the getters and setters that
 | 
| -///     will be needed to evaluate them at runtime.
 | 
| -///   * extracts all attributes declared in the `attribute` attributes of
 | 
| -///     polymer elements.
 | 
| -class _HtmlExtractor extends TreeVisitor {
 | 
| -  final Map<String, List<String>> publishedAttributes;
 | 
| -  final SmokeCodeGenerator generator;
 | 
| -  final _SubExpressionVisitor expressionVisitor;
 | 
| -  final BuildLogger logger;
 | 
| -  bool _inTemplate = false;
 | 
| -  bool _inPolymerJs = false;
 | 
| -
 | 
| -  _HtmlExtractor(this.logger, this.generator, this.publishedAttributes,
 | 
| -      this.expressionVisitor);
 | 
| -
 | 
| -  void visitElement(Element node) {
 | 
| -    if (_inTemplate) _processNormalElement(node);
 | 
| -    var lastInPolymerJs = _inPolymerJs;
 | 
| -    if (node.localName == 'polymer-element') {
 | 
| -      // Detect Polymer JS elements, the current logic is any element with only
 | 
| -      // non-Dart script tags.
 | 
| -      var scripts = node.querySelectorAll('script');
 | 
| -      _inPolymerJs = scripts.isNotEmpty &&
 | 
| -          scripts.every((s) => s.attributes['type'] != 'application/dart');
 | 
| -      _processPolymerElement(node);
 | 
| -      _processNormalElement(node);
 | 
| -    }
 | 
| -
 | 
| -    if (node.localName == 'template') {
 | 
| -      var last = _inTemplate;
 | 
| -      _inTemplate = true;
 | 
| -      super.visitElement(node);
 | 
| -      _inTemplate = last;
 | 
| -    } else {
 | 
| -      super.visitElement(node);
 | 
| -    }
 | 
| -    _inPolymerJs = lastInPolymerJs;
 | 
| -  }
 | 
| -
 | 
| -  void visitText(Text node) {
 | 
| -    // Nothing here applies if inside a polymer js element
 | 
| -    if (!_inTemplate || _inPolymerJs) return;
 | 
| -    var bindings = _Mustaches.parse(node.data);
 | 
| -    if (bindings == null) return;
 | 
| -    for (var e in bindings.expressions) {
 | 
| -      _addExpression(e, false, false, node.sourceSpan);
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  /// Registers getters and setters for all published attributes.
 | 
| -  void _processPolymerElement(Element node) {
 | 
| -    // Nothing here applies if inside a polymer js element
 | 
| -    if (_inPolymerJs) return;
 | 
| -
 | 
| -    var tagName = node.attributes['name'];
 | 
| -    var value = node.attributes['attributes'];
 | 
| -    if (value != null) {
 | 
| -      publishedAttributes[tagName] =
 | 
| -          value.split(ATTRIBUTES_REGEX).map((a) => a.trim()).toList();
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  /// Produces warnings for misuses of on-foo event handlers, and for instanting
 | 
| -  /// custom tags incorrectly.
 | 
| -  void _processNormalElement(Element node) {
 | 
| -    // Nothing here applies if inside a polymer js element
 | 
| -    if (_inPolymerJs) return;
 | 
| -
 | 
| -    var tag = node.localName;
 | 
| -    var isCustomTag = isCustomTagName(tag) || node.attributes['is'] != null;
 | 
| -
 | 
| -    // Event handlers only allowed inside polymer-elements
 | 
| -    node.attributes.forEach((name, value) {
 | 
| -      var bindings = _Mustaches.parse(value);
 | 
| -      if (bindings == null) return;
 | 
| -      var isEvent = false;
 | 
| -      var isTwoWay = false;
 | 
| -      if (name is String) {
 | 
| -        name = name.toLowerCase();
 | 
| -        isEvent = name.startsWith('on-');
 | 
| -        isTwoWay = !isEvent &&
 | 
| -            bindings.isWhole &&
 | 
| -            (isCustomTag ||
 | 
| -                tag == 'input' && (name == 'value' || name == 'checked') ||
 | 
| -                tag == 'select' &&
 | 
| -                    (name == 'selectedindex' || name == 'value') ||
 | 
| -                tag == 'textarea' && name == 'value');
 | 
| -      }
 | 
| -      for (var exp in bindings.expressions) {
 | 
| -        _addExpression(exp, isEvent, isTwoWay, node.sourceSpan);
 | 
| -      }
 | 
| -    });
 | 
| -  }
 | 
| -
 | 
| -  void _addExpression(
 | 
| -      String stringExpression, bool inEvent, bool isTwoWay, SourceSpan span) {
 | 
| -    if (inEvent) {
 | 
| -      if (stringExpression.startsWith('@')) {
 | 
| -        logger.warning(AT_EXPRESSION_REMOVED, span: span);
 | 
| -        return;
 | 
| -      }
 | 
| -
 | 
| -      if (stringExpression == '') return;
 | 
| -      if (stringExpression.startsWith('_')) {
 | 
| -        logger.warning(NO_PRIVATE_EVENT_HANDLERS, span: span);
 | 
| -        return;
 | 
| -      }
 | 
| -      generator.addGetter(stringExpression);
 | 
| -      generator.addSymbol(stringExpression);
 | 
| -    }
 | 
| -    expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -/// A polymer-expression visitor that records every getter and setter that will
 | 
| -/// be needed to evaluate a single expression at runtime.
 | 
| -class _SubExpressionVisitor extends pe.RecursiveVisitor {
 | 
| -  final SmokeCodeGenerator generator;
 | 
| -  final BuildLogger logger;
 | 
| -  bool _includeSetter;
 | 
| -  SourceSpan _currentSpan;
 | 
| -
 | 
| -  _SubExpressionVisitor(this.generator, this.logger);
 | 
| -
 | 
| -  /// Visit [exp], and record getters and setters that are needed in order to
 | 
| -  /// evaluate it at runtime. [includeSetter] is only true if this expression
 | 
| -  /// occured in a context where it could be updated, for example in two-way
 | 
| -  /// bindings such as `<input value={{exp}}>`.
 | 
| -  void run(pe.Expression exp, bool includeSetter, span) {
 | 
| -    _currentSpan = span;
 | 
| -    _includeSetter = includeSetter;
 | 
| -    visit(exp);
 | 
| -  }
 | 
| -
 | 
| -  /// Adds a getter and symbol for [name], and optionally a setter.
 | 
| -  _add(String name) {
 | 
| -    if (name.startsWith('_')) {
 | 
| -      logger.warning(NO_PRIVATE_SYMBOLS_IN_BINDINGS, span: _currentSpan);
 | 
| -      return;
 | 
| -    }
 | 
| -    generator.addGetter(name);
 | 
| -    generator.addSymbol(name);
 | 
| -    if (_includeSetter) generator.addSetter(name);
 | 
| -  }
 | 
| -
 | 
| -  void preVisitExpression(e) {
 | 
| -    // For two-way bindings the outermost expression may be updated, so we need
 | 
| -    // both the getter and the setter, but we only need the getter for
 | 
| -    // subexpressions. We exclude setters as soon as we go deeper in the tree,
 | 
| -    // except when we see a filter (that can potentially be a two-way
 | 
| -    // transformer).
 | 
| -    if (e is pe.BinaryOperator && e.operator == '|') return;
 | 
| -    _includeSetter = false;
 | 
| -  }
 | 
| -
 | 
| -  visitIdentifier(pe.Identifier e) {
 | 
| -    if (e.value != 'this') _add(e.value);
 | 
| -    super.visitIdentifier(e);
 | 
| -  }
 | 
| -
 | 
| -  visitGetter(pe.Getter e) {
 | 
| -    _add(e.name);
 | 
| -    super.visitGetter(e);
 | 
| -  }
 | 
| -
 | 
| -  visitInvoke(pe.Invoke e) {
 | 
| -    _includeSetter = false; // Invoke is only valid as an r-value.
 | 
| -    if (e.method != null) _add(e.method);
 | 
| -    super.visitInvoke(e);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -/// Parses and collects information about bindings found in polymer templates.
 | 
| -class _Mustaches {
 | 
| -  /// Each expression that appears within `{{...}}` and `[[...]]`.
 | 
| -  final List<String> expressions;
 | 
| -
 | 
| -  /// Whether the whole text returned by [parse] was a single expression.
 | 
| -  final bool isWhole;
 | 
| -
 | 
| -  _Mustaches(this.isWhole, this.expressions);
 | 
| -
 | 
| -  static _Mustaches parse(String text) {
 | 
| -    if (text == null || text.isEmpty) return null;
 | 
| -    // Use template-binding's parser, but provide a delegate function factory to
 | 
| -    // save the expressions without parsing them as [PropertyPath]s.
 | 
| -    var tokens = MustacheTokens.parse(text, (s) => () => s);
 | 
| -    if (tokens == null) return null;
 | 
| -    var length = tokens.length;
 | 
| -    bool isWhole =
 | 
| -        length == 1 && tokens.getText(length) == '' && tokens.getText(0) == '';
 | 
| -    var expressions = new List(length);
 | 
| -    for (int i = 0; i < length; i++) {
 | 
| -      expressions[i] = tokens.getPrepareBinding(i)();
 | 
| -    }
 | 
| -    return new _Mustaches(isWhole, expressions);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -/// Holds types that are used in queries
 | 
| -class _ResolvedTypes {
 | 
| -  /// Element representing `HtmlElement`.
 | 
| -  final ClassElement htmlElementElement;
 | 
| -
 | 
| -  /// Element representing `String`.
 | 
| -  final InterfaceType stringType;
 | 
| -
 | 
| -  /// Element representing `Polymer`.
 | 
| -  final ClassElement polymerClassElement;
 | 
| -
 | 
| -  /// Element representing the constructor of `@CustomTag`.
 | 
| -  final ConstructorElement customTagConstructor;
 | 
| -
 | 
| -  /// Element representing the type of `@published`.
 | 
| -  final ClassElement publishedElement;
 | 
| -
 | 
| -  /// Element representing the type of `@observable`.
 | 
| -  final ClassElement observableElement;
 | 
| -
 | 
| -  /// Element representing the type of `@ObserveProperty`.
 | 
| -  final ClassElement observePropertyElement;
 | 
| -
 | 
| -  /// Element representing the type of `@ComputedProperty`.
 | 
| -  final ClassElement computedPropertyElement;
 | 
| -
 | 
| -  /// Logger for reporting errors.
 | 
| -  static BuildLogger logger;
 | 
| -
 | 
| -  factory _ResolvedTypes(Resolver resolver) {
 | 
| -    var coreLib = resolver.getLibraryByUri(Uri.parse('dart:core'));
 | 
| -    // coreLib should never be null, its ok to throw if this fails.
 | 
| -    var stringType = _lookupType(coreLib, 'String').type;
 | 
| -
 | 
| -    // Load class elements that are used in queries for codegen.
 | 
| -    var polymerLib =
 | 
| -        resolver.getLibrary(new AssetId('polymer', 'lib/polymer.dart'));
 | 
| -    if (polymerLib == null) {
 | 
| -      _definitionError('polymer');
 | 
| -      return new _ResolvedTypes.internal(
 | 
| -          null, stringType, null, null, null, null, null, null);
 | 
| -    }
 | 
| -
 | 
| -    var htmlLib = resolver.getLibraryByUri(Uri.parse('dart:html'));
 | 
| -    var observeLib =
 | 
| -        resolver.getLibrary(new AssetId('observe', 'lib/src/metadata.dart'));
 | 
| -
 | 
| -    var customTagConstructor =
 | 
| -        _lookupType(polymerLib, 'CustomTag').constructors.first;
 | 
| -    var publishedElement = _lookupType(polymerLib, 'PublishedProperty');
 | 
| -    var observePropertyElement = _lookupType(polymerLib, 'ObserveProperty');
 | 
| -    var computedPropertyElement = _lookupType(polymerLib, 'ComputedProperty');
 | 
| -    var polymerClassElement = _lookupType(polymerLib, 'Polymer');
 | 
| -    var observableElement = _lookupType(observeLib, 'ObservableProperty');
 | 
| -    var htmlElementElement = _lookupType(htmlLib, 'HtmlElement');
 | 
| -
 | 
| -    return new _ResolvedTypes.internal(htmlElementElement, stringType,
 | 
| -        polymerClassElement, customTagConstructor, publishedElement,
 | 
| -        observableElement, observePropertyElement, computedPropertyElement);
 | 
| -  }
 | 
| -
 | 
| -  _ResolvedTypes.internal(this.htmlElementElement, this.stringType,
 | 
| -      this.polymerClassElement, this.customTagConstructor,
 | 
| -      this.publishedElement, this.observableElement,
 | 
| -      this.observePropertyElement, this.computedPropertyElement);
 | 
| -
 | 
| -  static _lookupType(LibraryElement lib, String typeName) {
 | 
| -    var result = lib.getType(typeName);
 | 
| -    if (result == null) _definitionError(typeName);
 | 
| -    return result;
 | 
| -  }
 | 
| -
 | 
| -  static _definitionError(name) {
 | 
| -    var message = MISSING_POLYMER_DART;
 | 
| -    if (logger != null) {
 | 
| -      logger.warning(message);
 | 
| -    } else {
 | 
| -      throw new StateError(message.snippet);
 | 
| -    }
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -/// Retrieves all classes that are visible if you were to import [lib]. This
 | 
| -/// includes exported classes from other libraries.
 | 
| -List<ClassElement> _visibleClassesOf(LibraryElement lib) {
 | 
| -  var result = [];
 | 
| -  result.addAll(lib.units.expand((u) => u.types));
 | 
| -  for (var e in lib.exports) {
 | 
| -    var exported = e.exportedLibrary.units.expand((u) => u.types).toList();
 | 
| -    _filter(exported, e.combinators);
 | 
| -    result.addAll(exported);
 | 
| -  }
 | 
| -  return result;
 | 
| -}
 | 
| -
 | 
| -/// Retrieves all top-level methods that are visible if you were to import
 | 
| -/// [lib]. This includes exported methods from other libraries too.
 | 
| -List<FunctionElement> _visibleTopLevelMethodsOf(LibraryElement lib) {
 | 
| -  var result = [];
 | 
| -  result.addAll(lib.units.expand((u) => u.functions));
 | 
| -  for (var e in lib.exports) {
 | 
| -    var exported = e.exportedLibrary.units.expand((u) => u.functions).toList();
 | 
| -    _filter(exported, e.combinators);
 | 
| -    result.addAll(exported);
 | 
| -  }
 | 
| -  return result;
 | 
| -}
 | 
| -
 | 
| -/// Filters [elements] that come from an export, according to its show/hide
 | 
| -/// combinators. This modifies [elements] in place.
 | 
| -void _filter(
 | 
| -    List<analyzer.Element> elements, List<NamespaceCombinator> combinators) {
 | 
| -  for (var c in combinators) {
 | 
| -    if (c is ShowElementCombinator) {
 | 
| -      var show = c.shownNames.toSet();
 | 
| -      elements.retainWhere((e) => show.contains(e.displayName));
 | 
| -    } else if (c is HideElementCombinator) {
 | 
| -      var hide = c.hiddenNames.toSet();
 | 
| -      elements.removeWhere((e) => hide.contains(e.displayName));
 | 
| -    }
 | 
| -  }
 | 
| -}
 | 
| 
 |