Search in sources :

Example 76 with JSTypeExpression

use of com.google.javascript.rhino.JSTypeExpression 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 77 with JSTypeExpression

use of com.google.javascript.rhino.JSTypeExpression 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 78 with JSTypeExpression

use of com.google.javascript.rhino.JSTypeExpression 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 79 with JSTypeExpression

use of com.google.javascript.rhino.JSTypeExpression in project closure-compiler by google.

the class FunctionTypeBuilder method inferInheritance.

/**
 * Infer any supertypes from the JSDocInfo or the passed-in base type.
 *
 * @param info JSDoc info that is attached to the type declaration, if any
 * @param classExtendsType The type of the extends clause in `class C extends SuperClass {}`, if
 *     present.
 * @return this object
 */
FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info, @Nullable ObjectType classExtendsType) {
    if (info != null && info.hasBaseType()) {
        if (isConstructor || isInterface) {
            JSType infoBaseType = info.getBaseType().evaluate(templateScope, typeRegistry);
            if (!areCompatibleExtendsTypes(infoBaseType.toMaybeObjectType(), classExtendsType)) {
                this.isKnownAmbiguous = true;
            }
            if (infoBaseType.setValidator(new ExtendedTypeValidator())) {
                baseType = infoBaseType.toObjectType();
            }
        } else {
            reportWarning(EXTENDS_WITHOUT_TYPEDEF, formatFnName());
            this.isKnownAmbiguous = true;
        }
    } else if (classExtendsType != null && (isConstructor || isInterface)) {
        // This case is:
        // // no JSDoc here
        // class extends astBaseType {...}
        // 
        // It may well be that astBaseType is something dynamically created, like a value passed into
        // a function. A common pattern is:
        // 
        // function mixinX(superClass) {
        // return class extends superClass {
        // ...
        // };
        // }
        // The ExtendedTypeValidator() used in the JSDocInfo case above will report errors for these
        // cases, and we don't want that.
        // Since astBaseType is an actual value in code rather than an annotation, we can
        // rely on validation elsewhere to ensure it is actually defined.
        baseType = classExtendsType;
    }
    // Implemented interfaces (for constructors only).
    if (info != null && info.getImplementedInterfaceCount() > 0) {
        if (isConstructor) {
            implementedInterfaces = new ArrayList<>();
            for (JSTypeExpression t : info.getImplementedInterfaces()) {
                JSType maybeInterType = t.evaluate(templateScope, typeRegistry);
                if (maybeInterType.setValidator(new ImplementedTypeValidator())) {
                    implementedInterfaces.add((ObjectType) maybeInterType);
                }
            }
        } else if (isInterface) {
            reportWarning(TypeCheck.CONFLICTING_IMPLEMENTED_TYPE, formatFnName());
        } else {
            reportWarning(CONSTRUCTOR_REQUIRED, "@implements", formatFnName());
        }
    }
    // We've already emitted a warning if this is not an interface.
    if (isInterface) {
        extendedInterfaces = new ArrayList<>();
        if (info != null) {
            for (JSTypeExpression t : info.getExtendedInterfaces()) {
                JSType maybeInterfaceType = t.evaluate(templateScope, typeRegistry);
                if (maybeInterfaceType != null) {
                    // setValidator runs validation and returns whether validation was successful (except
                    // for not-yet resolved named types, where validation is delayed).
                    // This code must run even for non-object types (which we know are invalid) to generate
                    // and record the user error message.
                    boolean isValid = maybeInterfaceType.setValidator(new ExtendedTypeValidator());
                    // setValidator might not (e.g. due to delayed execution).
                    if (isValid && maybeInterfaceType.toMaybeObjectType() != null) {
                        extendedInterfaces.add(maybeInterfaceType.toMaybeObjectType());
                    }
                }
                // de-dupe baseType (from extends keyword) if it's also in @extends jsdoc.
                if (classExtendsType != null && maybeInterfaceType.isSubtypeOf(classExtendsType)) {
                    classExtendsType = null;
                }
            }
        }
        if (classExtendsType != null && classExtendsType.setValidator(new ExtendedTypeValidator())) {
            // case is:
            // /**
            // * @interface
            // * @extends {OtherInterface}
            // */
            // class SomeInterface extends astBaseType {}
            // Add the explicit extends type to the extended interfaces listed in JSDoc.
            extendedInterfaces.add(classExtendsType);
        }
    }
    return this;
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) JSTypeExpression(com.google.javascript.rhino.JSTypeExpression)

Example 80 with JSTypeExpression

use of com.google.javascript.rhino.JSTypeExpression in project closure-compiler by google.

the class FunctionTypeBuilder method inferReturnType.

/**
 * Infer the return type from JSDocInfo.
 * @param fromInlineDoc Indicates whether return type is inferred from inline
 * doc attached to function name
 */
FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info, boolean fromInlineDoc) {
    if (info != null) {
        JSTypeExpression returnTypeExpr = fromInlineDoc ? info.getType() : info.getReturnType();
        if (returnTypeExpr != null) {
            returnType = returnTypeExpr.evaluate(templateScope, typeRegistry);
            returnTypeInferred = false;
        }
    }
    return this;
}
Also used : JSTypeExpression(com.google.javascript.rhino.JSTypeExpression)

Aggregations

JSTypeExpression (com.google.javascript.rhino.JSTypeExpression)101 Node (com.google.javascript.rhino.Node)67 JSDocInfo (com.google.javascript.rhino.JSDocInfo)58 Test (org.junit.Test)26 JSDocInfoBuilder (com.google.javascript.rhino.JSDocInfoBuilder)18 MemberDefinition (com.google.javascript.jscomp.PolymerPass.MemberDefinition)9 JSType (com.google.javascript.rhino.jstype.JSType)9 ArrayList (java.util.ArrayList)8 TypeDeclarationNode (com.google.javascript.rhino.Node.TypeDeclarationNode)7 Map (java.util.Map)6 NodeSubject.assertNode (com.google.javascript.jscomp.testing.NodeSubject.assertNode)4 HashMap (java.util.HashMap)4 HashSet (java.util.HashSet)4 ImmutableList (com.google.common.collect.ImmutableList)3 ImmutableMap (com.google.common.collect.ImmutableMap)3 Visibility (com.google.javascript.rhino.JSDocInfo.Visibility)3 LinkedHashMap (java.util.LinkedHashMap)3 ImmutableSet (com.google.common.collect.ImmutableSet)2 Name (com.google.javascript.jscomp.GlobalNamespace.Name)2 Ref (com.google.javascript.jscomp.GlobalNamespace.Ref)2