Search in sources :

Example 11 with MemberDefinition

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

the class PolymerClassDefinition method extractFromCallNode.

/**
 * Validates the class definition and if valid, destructively extracts the class definition from
 * the AST.
 */
@Nullable
static PolymerClassDefinition extractFromCallNode(Node callNode, AbstractCompiler compiler, GlobalNamespace globalNames) {
    Node descriptor = NodeUtil.getArgumentForCallOrNew(callNode, 0);
    if (descriptor == null || !descriptor.isObjectLit()) {
        // report bad class definition
        compiler.report(JSError.make(callNode, PolymerPassErrors.POLYMER_DESCRIPTOR_NOT_VALID));
        return null;
    }
    int paramCount = callNode.getChildCount() - 1;
    if (paramCount != 1) {
        compiler.report(JSError.make(callNode, PolymerPassErrors.POLYMER_UNEXPECTED_PARAMS));
        return null;
    }
    Node elName = NodeUtil.getFirstPropMatchingKey(descriptor, "is");
    if (elName == null) {
        compiler.report(JSError.make(callNode, PolymerPassErrors.POLYMER_MISSING_IS));
        return null;
    }
    Node target;
    if (NodeUtil.isNameDeclaration(callNode.getGrandparent())) {
        target = IR.name(callNode.getParent().getString());
    } else if (callNode.getParent().isAssign()) {
        target = callNode.getParent().getFirstChild().cloneTree();
    } else {
        String elNameStringBase = elName.isQualifiedName() ? elName.getQualifiedName().replace('.', '$') : elName.getString();
        String elNameString = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, elNameStringBase);
        elNameString += "Element";
        target = IR.name(elNameString);
    }
    JSDocInfo classInfo = NodeUtil.getBestJSDocInfo(target);
    JSDocInfo ctorInfo = null;
    Node constructor = NodeUtil.getFirstPropMatchingKey(descriptor, "factoryImpl");
    if (constructor == null) {
        constructor = NodeUtil.emptyFunction();
        compiler.reportChangeToChangeScope(constructor);
        constructor.useSourceInfoFromForTree(callNode);
    } else {
        ctorInfo = NodeUtil.getBestJSDocInfo(constructor);
    }
    Node baseClass = NodeUtil.getFirstPropMatchingKey(descriptor, "extends");
    String nativeBaseElement = baseClass == null ? null : baseClass.getString();
    Node behaviorArray = NodeUtil.getFirstPropMatchingKey(descriptor, "behaviors");
    PolymerBehaviorExtractor behaviorExtractor = new PolymerBehaviorExtractor(compiler, globalNames);
    ImmutableList<BehaviorDefinition> behaviors = behaviorExtractor.extractBehaviors(behaviorArray);
    List<MemberDefinition> allProperties = new ArrayList<>();
    for (BehaviorDefinition behavior : behaviors) {
        overwriteMembersIfPresent(allProperties, behavior.props);
    }
    overwriteMembersIfPresent(allProperties, PolymerPassStaticUtils.extractProperties(descriptor, DefinitionType.ObjectLiteral, compiler));
    FeatureSet newFeatures = null;
    if (!behaviors.isEmpty()) {
        newFeatures = behaviors.get(0).features;
        for (int i = 1; i < behaviors.size(); i++) {
            newFeatures = newFeatures.union(behaviors.get(i).features);
        }
    }
    return new PolymerClassDefinition(DefinitionType.ObjectLiteral, callNode, target, descriptor, classInfo, new MemberDefinition(ctorInfo, null, constructor), nativeBaseElement, allProperties, behaviors, newFeatures);
}
Also used : Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList) FeatureSet(com.google.javascript.jscomp.parsing.parser.FeatureSet) JSDocInfo(com.google.javascript.rhino.JSDocInfo) BehaviorDefinition(com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition) Nullable(javax.annotation.Nullable)

Example 12 with MemberDefinition

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

the class PolymerClassRewriter method rewritePolymerCall.

/**
 * Rewrites a given call to Polymer({}) to a set of declarations and assignments which can be
 * understood by the compiler.
 *
 * @param exprRoot The root expression of the call to Polymer({}).
 * @param cls The extracted {@link PolymerClassDefinition} for the Polymer element created by this
 *     call.
 */
void rewritePolymerCall(Node exprRoot, final PolymerClassDefinition cls, boolean isInGlobalScope) {
    Node objLit = checkNotNull(cls.descriptor);
    // Add {@code @lends} to the object literal.
    JSDocInfoBuilder objLitDoc = new JSDocInfoBuilder(true);
    objLitDoc.recordLends(cls.target.getQualifiedName() + ".prototype");
    objLit.setJSDocInfo(objLitDoc.build());
    addTypesToFunctions(objLit, cls.target.getQualifiedName(), cls.defType);
    PolymerPassStaticUtils.switchDollarSignPropsToBrackets(objLit, compiler);
    PolymerPassStaticUtils.quoteListenerAndHostAttributeKeys(objLit, 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();
    JSDocInfoBuilder constructorDoc = this.getConstructorDoc(cls);
    // Remove the original constructor JS docs from the objlit.
    Node ctorKey = cls.constructor.value.getParent();
    if (ctorKey != null) {
        ctorKey.removeProp(Node.JSDOC_INFO_PROP);
    }
    if (cls.target.isGetProp()) {
        // foo.bar = Polymer({...});
        Node assign = IR.assign(cls.target.cloneTree(), cls.constructor.value.cloneTree());
        NodeUtil.markNewScopesChanged(assign, compiler);
        assign.setJSDocInfo(constructorDoc.build());
        Node exprResult = IR.exprResult(assign);
        exprResult.useSourceInfoIfMissingFromForTree(cls.target);
        block.addChildToBack(exprResult);
    } else {
        // var foo = Polymer({...}); OR Polymer({...});
        Node var = IR.var(cls.target.cloneTree(), cls.constructor.value.cloneTree());
        NodeUtil.markNewScopesChanged(var, compiler);
        var.useSourceInfoIfMissingFromForTree(exprRoot);
        var.setJSDocInfo(constructorDoc.build());
        block.addChildToBack(var);
    }
    appendPropertiesToBlock(cls, block, cls.target.getQualifiedName() + ".prototype.");
    appendBehaviorMembersToBlock(cls, block);
    ImmutableList<MemberDefinition> readOnlyProps = parseReadOnlyProperties(cls, block);
    ImmutableList<MemberDefinition> attributeReflectedProps = parseAttributeReflectedProperties(cls);
    addInterfaceExterns(cls, readOnlyProps, attributeReflectedProps);
    removePropertyDocs(objLit, PolymerClassDefinition.DefinitionType.ObjectLiteral);
    Node statements = block.removeChildren();
    Node parent = exprRoot.getParent();
    // global.
    if (this.polymerVersion == 1 && !isInGlobalScope && !cls.target.isGetProp()) {
        Node scriptNode = NodeUtil.getEnclosingScript(parent);
        scriptNode.addChildrenToFront(statements);
        compiler.reportChangeToChangeScope(scriptNode);
    } else {
        Node beforeRoot = exprRoot.getPrevious();
        if (beforeRoot == null) {
            parent.addChildrenToFront(statements);
        } else {
            parent.addChildrenAfter(statements, beforeRoot);
        }
        compiler.reportChangeToEnclosingScope(parent);
    }
    compiler.reportChangeToEnclosingScope(statements);
    // we might need to update the FeatureSet.
    if (cls.features != null) {
        Node scriptNode = NodeUtil.getEnclosingScript(parent);
        FeatureSet oldFeatures = (FeatureSet) scriptNode.getProp(Node.FEATURE_SET);
        FeatureSet newFeatures = oldFeatures.union(cls.features);
        if (!newFeatures.equals(oldFeatures)) {
            scriptNode.putProp(Node.FEATURE_SET, newFeatures);
            compiler.reportChangeToChangeScope(scriptNode);
        }
    }
    if (NodeUtil.isNameDeclaration(exprRoot)) {
        Node assignExpr = varToAssign(exprRoot);
        parent.replaceChild(exprRoot, assignExpr);
        compiler.reportChangeToEnclosingScope(assignExpr);
    }
    // with the class members.
    if (polymerVersion > 1 && propertyRenamingEnabled && cls.descriptor != null) {
        Node props = NodeUtil.getFirstPropMatchingKey(cls.descriptor, "properties");
        if (props != null && props.isObjectLit()) {
            addObjectReflectionCall(cls, props);
        }
    }
}
Also used : Node(com.google.javascript.rhino.Node) FeatureSet(com.google.javascript.jscomp.parsing.parser.FeatureSet) JSDocInfoBuilder(com.google.javascript.rhino.JSDocInfoBuilder) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Example 13 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, final PolymerClassDefinition cls, boolean isInGlobalScope) {
    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();
    appendPropertiesToBlock(cls, block, cls.target.getQualifiedName() + ".prototype.");
    ImmutableList<MemberDefinition> readOnlyProps = parseReadOnlyProperties(cls, block);
    ImmutableList<MemberDefinition> attributeReflectedProps = parseAttributeReflectedProperties(cls);
    addInterfaceExterns(cls, readOnlyProps, attributeReflectedProps);
    // If an external interface is required, mark the class as implementing it
    if (!readOnlyProps.isEmpty() || !attributeReflectedProps.isEmpty()) {
        JSDocInfoBuilder classInfo = JSDocInfoBuilder.maybeCopyFrom(clazz.getJSDocInfo());
        String interfaceName = getInterfaceName(cls);
        JSTypeExpression interfaceType = new JSTypeExpression(new Node(Token.BANG, IR.string(interfaceName)), VIRTUAL_FILE);
        classInfo.recordImplementedInterface(interfaceType);
        clazz.setJSDocInfo(classInfo.build());
    }
    if (block.hasChildren()) {
        removePropertyDocs(cls.descriptor, cls.defType);
        Node stmt = NodeUtil.getEnclosingStatement(clazz);
        stmt.getParent().addChildrenAfter(block.removeChildren(), stmt);
        compiler.reportChangeToEnclosingScope(stmt);
    }
    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));
    // with the class members.
    if (propertyRenamingEnabled && cls.descriptor != null) {
        addObjectReflectionCall(cls, cls.descriptor);
    }
}
Also used : Node(com.google.javascript.rhino.Node) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) JSDocInfoBuilder(com.google.javascript.rhino.JSDocInfoBuilder) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Example 14 with MemberDefinition

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

the class PolymerClassRewriter method addInterfaceExterns.

/**
 * Adds an interface for the given ClassDefinition to externs. This allows generated setter
 * functions for read-only properties to avoid renaming altogether.
 *
 * @see https://www.polymer-project.org/0.8/docs/devguide/properties.html#read-only
 */
private void addInterfaceExterns(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);
    if (polymerVersion == 1) {
        // For Polymer 1, all declared properties are non-renameable
        appendPropertiesToBlock(cls, block, interfaceName + ".prototype.");
    } else {
        List<MemberDefinition> interfaceProperties = new ArrayList<>();
        interfaceProperties.addAll(readOnlyProps);
        if (attributeReflectedProps != null) {
            interfaceProperties.addAll(attributeReflectedProps);
        }
        // For Polymer 2, only read-only properties and reflectToAttribute properties are
        // non-renameable. Other properties follow the ALL_UNQUOTED renaming rules.
        PolymerClassDefinition tmpDef = new PolymerClassDefinition(cls.defType, cls.definition, cls.target, cls.descriptor, null, null, null, interfaceProperties, null, null);
        // disallow renaming of readonly properties
        appendPropertiesToBlock(tmpDef, block, interfaceName + ".prototype.");
    }
    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, interfaceName + ".prototype." + 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);
}
Also used : Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression) JSDocInfoBuilder(com.google.javascript.rhino.JSDocInfoBuilder) MemberDefinition(com.google.javascript.jscomp.PolymerPass.MemberDefinition)

Example 15 with MemberDefinition

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

the class PolymerClassRewriter method appendBehaviorMembersToBlock.

/**
 * Appends all required behavior functions and non-property members to the given block.
 */
private void appendBehaviorMembersToBlock(final PolymerClassDefinition cls, Node block) {
    String qualifiedPath = cls.target.getQualifiedName() + ".prototype.";
    Map<String, Node> nameToExprResult = new HashMap<>();
    for (BehaviorDefinition behavior : cls.behaviors) {
        for (MemberDefinition behaviorFunction : behavior.functionsToCopy) {
            String fnName = behaviorFunction.name.getString();
            // Don't copy functions already defined by the element itself.
            if (NodeUtil.getFirstPropMatchingKey(cls.descriptor, fnName) != null) {
                continue;
            }
            // Avoid copying over the same function twice. The last definition always wins.
            if (nameToExprResult.containsKey(fnName)) {
                nameToExprResult.get(fnName).detach();
            }
            Node fnValue = behaviorFunction.value.cloneTree();
            NodeUtil.markNewScopesChanged(fnValue, compiler);
            Node exprResult = IR.exprResult(IR.assign(NodeUtil.newQName(compiler, qualifiedPath + fnName), fnValue));
            exprResult.srcrefTreeIfMissing(behaviorFunction.name);
            JSDocInfo.Builder info = getJSDocInfoBuilderForBehavior(behavior, behaviorFunction);
            // Uses of private members that come from behaviors are not recognized correctly,
            // so just suppress that warning.
            info.recordSuppression("unusedPrivateMembers");
            // making the type system understand that methods are "inherited" from behaviors.
            if (behaviorFunction.info != null && behaviorFunction.info.getVisibility() == Visibility.PROTECTED) {
                info.overwriteVisibility(Visibility.PUBLIC);
            }
            // symbols which do not exist in the element's scope. Only copy a function stub.
            if (!behavior.isGlobalDeclaration) {
                Node body = NodeUtil.getFunctionBody(fnValue);
                if (fnValue.isArrowFunction() && !NodeUtil.getFunctionBody(fnValue).isBlock()) {
                    // replace `() => <someExpr>` with `() => undefined`
                    body.replaceWith(NodeUtil.newUndefinedNode(body));
                } else {
                    body.removeChildren();
                }
                // Remove any non-named parameters, which may reference locals.
                int paramIndex = 0;
                for (Node param = NodeUtil.getFunctionParameters(fnValue).getFirstChild(); param != null; ) {
                    final Node next = param.getNext();
                    makeParamSafe(param, paramIndex++);
                    param = next;
                }
            }
            exprResult.getFirstChild().setJSDocInfo(info.build());
            block.addChildToBack(exprResult);
            nameToExprResult.put(fnName, exprResult);
        }
        // Copy other members.
        for (MemberDefinition behaviorProp : behavior.nonPropertyMembersToCopy) {
            String propName = behaviorProp.name.getString();
            if (nameToExprResult.containsKey(propName)) {
                nameToExprResult.get(propName).detach();
            }
            Node exprResult = IR.exprResult(NodeUtil.newQName(compiler, qualifiedPath + propName));
            exprResult.srcrefTree(behaviorProp.name);
            JSDocInfo.Builder info = getJSDocInfoBuilderForBehavior(behavior, behaviorProp);
            if (behaviorProp.name.isGetterDef()) {
                info = JSDocInfo.builder().parseDocumentation();
                if (behaviorProp.info != null && behaviorProp.info.getReturnType() != null) {
                    info.recordType(behaviorProp.info.getReturnType());
                }
            }
            exprResult.getFirstChild().setJSDocInfo(info.build());
            block.addChildToBack(exprResult);
            nameToExprResult.put(propName, exprResult);
        }
    }
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Node(com.google.javascript.rhino.Node) JSDocInfo(com.google.javascript.rhino.JSDocInfo) BehaviorDefinition(com.google.javascript.jscomp.PolymerBehaviorExtractor.BehaviorDefinition) 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