Search in sources :

Example 16 with MemberDefinition

use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.

the class PolymerClassRewriter method appendPropertiesToBlock.

/**
 * Appends all of the given properties to the given block.
 */
private void appendPropertiesToBlock(List<MemberDefinition> props, Node block, String basePath, boolean isExternsBlock) {
    for (MemberDefinition prop : props) {
        Node propertyNode = getPropertyNode(prop, basePath);
        if (propertyNode == null) {
            continue;
        }
        JSTypeExpression propType = PolymerPassStaticUtils.getTypeFromProperty(prop, compiler);
        if (propType == null) {
            continue;
        }
        JSDocInfo info = null;
        if (isExternsBlock) {
            info = replaceJSDocAndAddNewVars(prop, propType, block);
        } else {
            JSDocInfo.Builder infoBuilder = JSDocInfo.Builder.maybeCopyFrom(prop.info);
            infoBuilder.recordType(propType);
            info = infoBuilder.build();
        }
        propertyNode.getFirstChild().setJSDocInfo(info);
        block.addChildToBack(propertyNode);
    }
}
Also used : Node(com.google.javascript.rhino.Node) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) JSDocInfo(com.google.javascript.rhino.JSDocInfo) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Example 17 with MemberDefinition

use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.

the class PolymerClassRewriter method rewritePolymerClassDeclaration.

/**
 * Rewrites a class which extends Polymer.Element to a set of declarations and assignments which
 * can be understood by the compiler.
 *
 * @param clazz The class node
 * @param cls The extracted {@link PolymerClassDefinition} for the Polymer element created by this
 *     call.
 */
void rewritePolymerClassDeclaration(Node clazz, NodeTraversal traversal, final PolymerClassDefinition cls) {
    if (cls.descriptor != null) {
        addTypesToFunctions(cls.descriptor, cls.target.getQualifiedName(), cls.defType);
    }
    PolymerPassStaticUtils.switchDollarSignPropsToBrackets(NodeUtil.getClassMembers(clazz), compiler);
    for (MemberDefinition prop : cls.props) {
        if (prop.value.isObjectLit()) {
            PolymerPassStaticUtils.switchDollarSignPropsToBrackets(prop.value, compiler);
        }
    }
    // For simplicity add everything into a block, before adding it to the AST.
    Node block = IR.block();
    appendBehaviorPropertiesToBlock(cls, block, cls.target.getQualifiedName() + ".prototype.", /*isExternsBlock*/
    false);
    // For each Polymer property we found in the "properties" configuration object, append a
    // property declaration to the prototype (e.g. "/** @type {string} */ MyElement.prototype.foo").
    appendPropertiesToBlock(cls.props, block, cls.target.getQualifiedName() + ".prototype.", /* isExternsBlock= */
    false);
    ImmutableList<MemberDefinition> readOnlyProps = parseReadOnlyProperties(cls, block);
    ImmutableList<MemberDefinition> attributeReflectedProps = parseAttributeReflectedProperties(cls);
    createExportsAndExterns(cls, readOnlyProps, attributeReflectedProps);
    // If an external interface is required, mark the class as implementing it
    if (polymerExportPolicy == PolymerExportPolicy.EXPORT_ALL || !readOnlyProps.isEmpty() || !attributeReflectedProps.isEmpty()) {
        Node jsDocInfoNode = NodeUtil.getBestJSDocInfoNode(clazz);
        JSDocInfo.Builder classInfo = JSDocInfo.Builder.maybeCopyFrom(jsDocInfoNode.getJSDocInfo());
        String interfaceName = cls.getInterfaceName(compiler.getUniqueNameIdSupplier());
        JSTypeExpression interfaceType = new JSTypeExpression(new Node(Token.BANG, IR.string(interfaceName)).srcrefTree(jsDocInfoNode), VIRTUAL_FILE);
        classInfo.recordImplementedInterface(interfaceType);
        jsDocInfoNode.setJSDocInfo(classInfo.build());
    }
    Node insertAfterReference = NodeUtil.getEnclosingStatement(clazz);
    if (block.hasChildren()) {
        removePropertyDocs(cls.descriptor, cls.defType);
        Node newInsertAfterReference = block.getLastChild();
        insertAfterReference.getParent().addChildrenAfter(block.removeChildren(), insertAfterReference);
        compiler.reportChangeToEnclosingScope(insertAfterReference);
        insertAfterReference = newInsertAfterReference;
    }
    addReturnTypeIfMissing(cls, "is", new JSTypeExpression(IR.string("string"), VIRTUAL_FILE));
    Node type = new Node(Token.BANG);
    Node array = IR.string("Array");
    type.addChildToBack(array);
    Node arrayTemplateType = new Node(Token.BLOCK, IR.string("string"));
    array.addChildToBack(arrayTemplateType);
    addReturnTypeIfMissing(cls, "observers", new JSTypeExpression(type, VIRTUAL_FILE));
    addReturnTypeIfMissing(cls, "properties", new JSTypeExpression(IR.string(POLYMER_ELEMENT_PROP_CONFIG), VIRTUAL_FILE));
    // and switch simple observers to direct function references.
    if (propertyRenamingEnabled && cls.descriptor != null) {
        convertSimpleObserverStringsToReferences(cls);
        List<Node> propertySinks = new ArrayList<>();
        if (polymerExportPolicy != PolymerExportPolicy.EXPORT_ALL) {
            propertySinks.addAll(addComputedPropertiesReflectionCalls(cls));
            propertySinks.addAll(addComplexObserverReflectionCalls(cls));
        }
        if (!propertySinks.isEmpty()) {
            if (!propertySinkExternInjected && traversal.getScope().getVar(CheckSideEffects.PROTECTOR_FN) == null) {
                CheckSideEffects.addExtern(compiler);
                propertySinkExternInjected = true;
            }
            for (Node propertyRef : propertySinks) {
                Node name = IR.name(CheckSideEffects.PROTECTOR_FN).srcref(propertyRef);
                name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
                Node protectorCall = IR.call(name, propertyRef).srcref(propertyRef);
                protectorCall.putBooleanProp(Node.FREE_CALL, true);
                protectorCall = IR.exprResult(protectorCall).srcref(propertyRef);
                protectorCall.insertAfter(insertAfterReference);
                insertAfterReference = protectorCall;
            }
            compiler.reportChangeToEnclosingScope(insertAfterReference);
        }
        addPropertiesConfigObjectReflection(cls, cls.descriptor);
    }
}
Also used : Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) JSDocInfo(com.google.javascript.rhino.JSDocInfo) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Example 18 with MemberDefinition

use of com.google.javascript.jscomp.PolymerPass.MemberDefinition 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 19 with MemberDefinition

use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.

the class PolymerPassSuppressBehaviors method suppressDefaultValues.

private void suppressDefaultValues(Node behaviorValue) {
    for (MemberDefinition property : PolymerPassStaticUtils.extractProperties(behaviorValue, PolymerClassDefinition.DefinitionType.ObjectLiteral, compiler, /**
     * constructor=
     */
    null)) {
        if (!property.value.isObjectLit()) {
            continue;
        }
        Node defaultValue = NodeUtil.getFirstPropMatchingKey(property.value, "value");
        if (defaultValue == null || !defaultValue.isFunction()) {
            continue;
        }
        Node defaultValueKey = defaultValue.getParent();
        JSDocInfo.Builder suppressDoc = JSDocInfo.Builder.maybeCopyFrom(defaultValueKey.getJSDocInfo());
        suppressDoc.recordSuppressions(ImmutableSet.of("checkTypes", "globalThis", "visibility"));
        defaultValueKey.setJSDocInfo(suppressDoc.build());
    }
}
Also used : Node(com.google.javascript.rhino.Node) JSDocInfo(com.google.javascript.rhino.JSDocInfo) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Example 20 with MemberDefinition

use of com.google.javascript.jscomp.PolymerPass.MemberDefinition in project closure-compiler by google.

the class PolymerBehaviorExtractor method getNonPropertyMembersToCopy.

/**
 * @return A list of MemberDefinitions in a behavior which are not in the properties block, but
 *     should still be copied to the element prototype.
 */
private static ImmutableList<MemberDefinition> getNonPropertyMembersToCopy(Node behaviorObjLit) {
    checkState(behaviorObjLit.isObjectLit());
    ImmutableList.Builder<MemberDefinition> membersToCopy = ImmutableList.builder();
    for (Node keyNode = behaviorObjLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) {
        boolean isNonFunctionMember = keyNode.isGetterDef() || (keyNode.isStringKey() && !keyNode.getFirstChild().isFunction());
        if (isNonFunctionMember && !BEHAVIOR_NAMES_NOT_TO_COPY.contains(keyNode.getString())) {
            membersToCopy.add(new MemberDefinition(NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.getFirstChild()));
        }
    }
    return membersToCopy.build();
}
Also used : ImmutableList(com.google.common.collect.ImmutableList) Node(com.google.javascript.rhino.Node) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Aggregations

MemberDefinition (com.google.javascript.jscomp.PolymerPass.MemberDefinition)22 Node (com.google.javascript.rhino.Node)21 JSDocInfo (com.google.javascript.rhino.JSDocInfo)13 JSTypeExpression (com.google.javascript.rhino.JSTypeExpression)9 BehaviorDefinition (com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition)7 ArrayList (java.util.ArrayList)7 ImmutableList (com.google.common.collect.ImmutableList)6 LinkedHashMap (java.util.LinkedHashMap)6 FeatureSet (com.google.javascript.jscomp.parsing.parser.FeatureSet)5 JSDocInfoBuilder (com.google.javascript.rhino.JSDocInfoBuilder)4 HashMap (java.util.HashMap)4 Nullable (javax.annotation.Nullable)4 Map (java.util.Map)3 HashSet (java.util.HashSet)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