private void createExportsAndExterns(
      final PolymerClassDefinition cls,
      List<MemberDefinition> readOnlyProps,
      List<MemberDefinition> attributeReflectedProps) {
    Node block = IR.block();

    String interfaceName = getInterfaceName(cls);
    Node fnNode = NodeUtil.emptyFunction();
    compiler.reportChangeToChangeScope(fnNode);
    Node varNode = IR.var(NodeUtil.newQName(compiler, interfaceName), fnNode);

    JSDocInfoBuilder info = new JSDocInfoBuilder(true);
    info.recordInterface();
    varNode.setJSDocInfo(info.build());
    block.addChildToBack(varNode);
    String interfaceBasePath = interfaceName + ".prototype.";

    if (polymerExportPolicy == PolymerExportPolicy.EXPORT_ALL) {
      // Properties from behaviors were added to our element definition earlier.
      appendPropertiesToBlock(cls.props, block, interfaceBasePath);

      // Methods from behaviors were not already added to our element definition, so we need to
      // export those in addition to methods defined directly on the element. Note it's possible
      // and valid for two behaviors, or a behavior and an element, to implement the same method,
      // so we de-dupe by name. We're not checking that the signatures are compatible in the way
      // that normal class inheritance would, but that's not easy to do since these aren't classes.
      // Class mixins replace Polymer behaviors and are supported directly by Closure, so new code
      // should use those instead.
      LinkedHashMap<String, MemberDefinition> uniqueMethods = new LinkedHashMap<>();
      if (cls.behaviors != null) {
        for (BehaviorDefinition behavior : cls.behaviors) {
          for (MemberDefinition method : behavior.functionsToCopy) {
            uniqueMethods.put(method.name.getString(), method);
          }
        }
      }
      for (MemberDefinition method : cls.methods) {
        uniqueMethods.put(method.name.getString(), method);
      }
      for (MemberDefinition method : uniqueMethods.values()) {
        addMethodToObjectExternsUsingExportAnnotation(cls, method);
      }

    } else if (polymerVersion == 1) {
      // For Polymer 1, all declared properties are non-renameable
      appendPropertiesToBlock(cls.props, block, interfaceBasePath);
    } else {
      // For Polymer 2, only read-only properties and reflectToAttribute properties are
      // non-renameable. Other properties follow the ALL_UNQUOTED renaming rules.
      List<MemberDefinition> interfaceProperties = new ArrayList<>();
      interfaceProperties.addAll(readOnlyProps);
      if (attributeReflectedProps != null) {
        interfaceProperties.addAll(attributeReflectedProps);
      }
      appendPropertiesToBlock(interfaceProperties, block, interfaceBasePath);
    }

    for (MemberDefinition prop : readOnlyProps) {
      // Add all _set* functions to avoid renaming.
      String propName = prop.name.getString();
      String setterName = "_set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
      Node setterExprNode =
          IR.exprResult(NodeUtil.newQName(compiler, interfaceBasePath + setterName));

      JSDocInfoBuilder setterInfo = new JSDocInfoBuilder(true);
      JSTypeExpression propType = PolymerPassStaticUtils.getTypeFromProperty(prop, compiler);
      setterInfo.recordParameter(propName, propType);
      setterExprNode.getFirstChild().setJSDocInfo(setterInfo.build());

      block.addChildToBack(setterExprNode);
    }

    block.useSourceInfoIfMissingFromForTree(polymerElementExterns);

    Node scopeRoot = polymerElementExterns;
    if (!scopeRoot.isScript()) {
      scopeRoot = scopeRoot.getParent();
    }
    Node stmts = block.removeChildren();
    scopeRoot.addChildrenToBack(stmts);

    compiler.reportChangeToEnclosingScope(stmts);
  }
private void createExportsAndExterns ( final PolymerClassDefinition cls , List < MemberDefinition > readOnlyProps , List < MemberDefinition > attributeReflectedProps ) { Node block = IR . block ( ) ; String interfaceName = getInterfaceName ( cls ) ; Node fnNode = NodeUtil . emptyFunction ( ) ; compiler . reportChangeToChangeScope ( fnNode ) ; Node varNode = IR . var ( NodeUtil . newQName ( compiler , interfaceName ) , fnNode ) ; JSDocInfoBuilder info = new JSDocInfoBuilder ( true ) ; info . recordInterface ( ) ; varNode . setJSDocInfo ( info . build ( ) ) ; block . addChildToBack ( varNode ) ; String interfaceBasePath = interfaceName + ".prototype." ; if ( polymerExportPolicy == PolymerExportPolicy . EXPORT_ALL ) { appendPropertiesToBlock ( cls . props , block , interfaceBasePath ) ; LinkedHashMap < String , MemberDefinition > uniqueMethods = new LinkedHashMap < > ( ) ; if ( cls . behaviors != null ) { for ( BehaviorDefinition behavior : cls . behaviors ) { for ( MemberDefinition method : behavior . functionsToCopy ) { uniqueMethods . put ( method . name . getString ( ) , method ) ; } } } for ( MemberDefinition method : cls . methods ) { uniqueMethods . put ( method . name . getString ( ) , method ) ; } for ( MemberDefinition method : uniqueMethods . values ( ) ) { addMethodToObjectExternsUsingExportAnnotation ( cls , method ) ; } } else if ( polymerVersion == 1 ) { appendPropertiesToBlock ( cls . props , block , interfaceBasePath ) ; } else { List < MemberDefinition > interfaceProperties = new ArrayList < > ( ) ; interfaceProperties . addAll ( readOnlyProps ) ; if ( attributeReflectedProps != null ) { interfaceProperties . addAll ( attributeReflectedProps ) ; } appendPropertiesToBlock ( interfaceProperties , block , interfaceBasePath ) ; } for ( MemberDefinition prop : readOnlyProps ) { String propName = prop . name . getString ( ) ; String setterName = "_set" + propName . substring ( 0 , 1 ) . toUpperCase ( ) + propName . substring ( 1 ) ; Node setterExprNode = IR . exprResult ( NodeUtil . newQName ( compiler , interfaceBasePath + setterName ) ) ; JSDocInfoBuilder setterInfo = new JSDocInfoBuilder ( true ) ; JSTypeExpression propType = PolymerPassStaticUtils . getTypeFromProperty ( prop , compiler ) ; setterInfo . recordParameter ( propName , propType ) ; setterExprNode . getFirstChild ( ) . setJSDocInfo ( setterInfo . build ( ) ) ; block . addChildToBack ( setterExprNode ) ; } block . useSourceInfoIfMissingFromForTree ( polymerElementExterns ) ; Node scopeRoot = polymerElementExterns ; if ( ! scopeRoot . isScript ( ) ) { scopeRoot = scopeRoot . getParent ( ) ; } Node stmts = block . removeChildren ( ) ; scopeRoot . addChildrenToBack ( stmts ) ; compiler . reportChangeToEnclosingScope ( stmts ) ; }
Create exports and externs to protect element properties and methods from renaming and dead
code removal.

<p>Since Polymer templates, observers, and computed properties rely on string references to
element properties and methods, and because we don't yet have a way to update those references
reliably, we instead export or extern them.

<p>For properties, we create a new interface called {@code Polymer<ElementName>Interface}, add
all element properties to it, mark that the element class {@code @implements} this interface,
and add the interface to the Closure externs. The specific set of properties we add to this
interface is determined by the value of {@code polymerExportPolicy}.

<p>For methods, when {@code polymerExportPolicy = EXPORT_ALL}, we instead append to {@code
Object.prototype} in the externs using {@code @export} annotations. This approach is a
compromise, with the following alternatives considered:

<p>Alternative 1: Add methods to our generated {@code Polymer<ElementName>Interface} in the
externs. Pro: More optimal than {@code Object.prototype} when type-aware optimizations are
enabled. Con 1: When a class {@code @implements} an interface, and when {@code
report_missing_override} is enabled, any method on the class that is also in the interface must
have an {@code @override} annotation, which means we generate a spurious warning for all
methods. Con 2: An unresolved bug was encountered (b/115942961) relating to a mismatch between
the signatures of the class and the generated interface.

<p>Alternative 2: Generate goog.exportProperty calls, which causes aliases on the prototype
from original to optimized names to be set. Pro: Compiled code can still use the optimized
name. Con: In practice, for Polymer applications, we see a net increase in bundle size due to
the high number of new {@code Foo.prototype.originalName = Foo.prototype.z} expressions.

<p>Alternative 3: Append directly to the {@code Object.prototype} externs, instead of using
{@code @export} annotations for the {@link GenerateExports} pass. Pro: Doesn't depend on the
{@code generate_exports} and {@code export_local_property_definitions} flags. Con: The
PolymerPass runs in the type checking phase, so modifying {@code Object.prototype} here causes
unwanted type checking effects, such as allowing the method to be called on any object, and
generating incorrect warnings when {@code report_missing_override} is enabled.