Search in sources :

Example 6 with BehaviorDefinition

use of com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition in project closure-compiler by google.

the class PolymerClassRewriter method createExportsAndExterns.

/**
 * 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.
 */
private void createExportsAndExterns(final PolymerClassDefinition cls, List<MemberDefinition> readOnlyProps, List<MemberDefinition> attributeReflectedProps) {
    Node block = IR.block();
    String interfaceName = cls.getInterfaceName(compiler.getUniqueNameIdSupplier());
    Node fnNode = NodeUtil.emptyFunction();
    compiler.reportChangeToChangeScope(fnNode);
    Node varNode = IR.var(NodeUtil.newQName(compiler, interfaceName), fnNode);
    JSDocInfo.Builder info = JSDocInfo.builder().parseDocumentation();
    info.recordInterface();
    varNode.setJSDocInfo(info.build());
    block.addChildToBack(varNode);
    String interfaceBasePath = interfaceName + ".prototype.";
    if (polymerExportPolicy == PolymerExportPolicy.EXPORT_ALL) {
        appendBehaviorPropertiesToBlock(cls, block, interfaceBasePath, /*isExternsBlock*/
        true);
        appendPropertiesToBlock(cls.props, block, interfaceBasePath, /* isExternsBlock= */
        true);
        // 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
        appendBehaviorPropertiesToBlock(cls, block, interfaceBasePath, /*isExternsBlock*/
        true);
        appendPropertiesToBlock(cls.props, block, interfaceBasePath, /* isExternsBlock= */
        true);
    } 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);
        }
        // Readonly properties and attributeReflected properties for Polymer Element and its behaviors
        // are stored together
        appendPropertiesToBlock(interfaceProperties, block, interfaceBasePath, /* isExternsBlock= */
        true);
    }
    for (MemberDefinition prop : readOnlyProps) {
        // Add all _set* functions to avoid renaming.
        String propName = prop.name.getString();
        String setterName = "_set" + propName.substring(0, 1).toUpperCase(Locale.ROOT) + propName.substring(1);
        Node setterExprNode = IR.exprResult(NodeUtil.newQName(compiler, interfaceBasePath + setterName));
        JSDocInfo.Builder setterInfo = JSDocInfo.builder().parseDocumentation();
        JSTypeExpression propType = PolymerPassStaticUtils.getTypeFromProperty(prop, compiler);
        JSTypeExpression unknown = new JSTypeExpression(new Node(Token.QMARK), propType.getSourceName());
        setterInfo.recordParameter(propName, unknown);
        setterExprNode.getFirstChild().setJSDocInfo(setterInfo.build());
        block.addChildToBack(setterExprNode);
    }
    block.srcrefTreeIfMissing(externsInsertionRef);
    Node stmts = block.removeChildren();
    externsInsertionRef.addChildrenToBack(stmts);
    compiler.reportChangeToEnclosingScope(stmts);
}
Also used : Node(com.google.javascript.rhino.Node) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) ArrayList(java.util.ArrayList) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) JSDocInfo(com.google.javascript.rhino.JSDocInfo) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition) BehaviorDefinition(com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition) LinkedHashMap(java.util.LinkedHashMap)

Example 7 with BehaviorDefinition

use of com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition in project closure-compiler by google.

the class PolymerClassDefinition method removeBehaviorPropsOverlappingWithElementProps.

/**
 * Removes any behavior properties from the given map that have the same name as a property in the
 * given list.
 *
 * <p>For example, if a Polymer element with a property "name" depends on a behavior with a
 * property "name", then this method removes the behavior property in favor of the element
 * property.
 */
private static void removeBehaviorPropsOverlappingWithElementProps(Map<MemberDefinition, BehaviorDefinition> behaviorProps, List<MemberDefinition> polymerElementProps) {
    if (behaviorProps == null) {
        return;
    }
    Set<String> elementPropNames = polymerElementProps.stream().map(x -> x.name.getString()).collect(Collectors.toSet());
    Iterator<Map.Entry<MemberDefinition, BehaviorDefinition>> behaviorsItr = behaviorProps.entrySet().iterator();
    while (behaviorsItr.hasNext()) {
        MemberDefinition memberDefinition = behaviorsItr.next().getKey();
        if (elementPropNames.contains(memberDefinition.name.getString())) {
            behaviorsItr.remove();
        }
    }
}
Also used : BehaviorDefinition(com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition) Iterator(java.util.Iterator) CaseFormat(com.google.common.base.CaseFormat) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition) Supplier(com.google.common.base.Supplier) FeatureSet(com.google.javascript.jscomp.parsing.parser.FeatureSet) Set(java.util.Set) Collectors(java.util.stream.Collectors) Preconditions.checkState(com.google.common.base.Preconditions.checkState) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) Map(java.util.Map) IR(com.google.javascript.rhino.IR) JSDocInfo(com.google.javascript.rhino.JSDocInfo) ModuleMetadata(com.google.javascript.jscomp.modules.ModuleMetadataMap.ModuleMetadata) Nullable(javax.annotation.Nullable) MoreObjects.toStringHelper(com.google.common.base.MoreObjects.toStringHelper) Node(com.google.javascript.rhino.Node) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Aggregations

BehaviorDefinition (com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition)7 MemberDefinition (com.google.javascript.jscomp.PolymerPass.MemberDefinition)7 Node (com.google.javascript.rhino.Node)7 JSDocInfo (com.google.javascript.rhino.JSDocInfo)6 LinkedHashMap (java.util.LinkedHashMap)6 ArrayList (java.util.ArrayList)4 ImmutableList (com.google.common.collect.ImmutableList)3 FeatureSet (com.google.javascript.jscomp.parsing.parser.FeatureSet)3 HashMap (java.util.HashMap)3 Map (java.util.Map)3 Nullable (javax.annotation.Nullable)3 JSTypeExpression (com.google.javascript.rhino.JSTypeExpression)2 List (java.util.List)2 CaseFormat (com.google.common.base.CaseFormat)1 MoreObjects.toStringHelper (com.google.common.base.MoreObjects.toStringHelper)1 Preconditions.checkState (com.google.common.base.Preconditions.checkState)1 Supplier (com.google.common.base.Supplier)1 ModuleMetadata (com.google.javascript.jscomp.modules.ModuleMetadataMap.ModuleMetadata)1 IR (com.google.javascript.rhino.IR)1 HashSet (java.util.HashSet)1