Search in sources :

Example 1 with RawNominalType

use of com.google.javascript.jscomp.newtypes.RawNominalType in project closure-compiler by google.

the class GlobalTypeInfoCollector method process.

@Override
public void process(Node externs, Node root) {
    checkNotNull(warnings, "Cannot rerun GlobalTypeInfoCollector.process");
    checkArgument(externs == null || externs.isRoot());
    checkArgument(root.isRoot(), "Root must be ROOT, but is %s", root.getToken());
    this.compiler.setMostRecentTypechecker(MostRecentTypechecker.NTI);
    NTIScope globalScope = new NTIScope(root, null, ImmutableList.<String>of(), getCommonTypes());
    globalScope.addUnknownTypeNames(this.globalTypeInfo.getUnknownTypeNames());
    this.globalTypeInfo.setGlobalScope(globalScope);
    this.scopes.add(globalScope);
    // Processing of a scope is split into many separate phases, and it's not
    // straightforward to remember which phase does what.
    // (1) Find names of classes, interfaces, typedefs, enums, and namespaces
    // defined in the global scope.
    CollectNamedTypes rootCnt = new CollectNamedTypes(globalScope);
    NodeTraversal.traverseEs6(this.compiler, externs, this.orderedExterns);
    rootCnt.collectNamedTypesInExterns();
    defineObjectAndFunctionIfMissing();
    NodeTraversal.traverseEs6(compiler, root, rootCnt);
    // (2) Determine the type represented by each typedef and each enum
    globalScope.resolveTypedefs(getTypeParser());
    globalScope.resolveEnums(getTypeParser());
    // (3) Repeat steps 1-2 for all the other scopes (outer-to-inner)
    for (int i = 1; i < this.scopes.size(); i++) {
        NTIScope s = this.scopes.get(i);
        CollectNamedTypes cnt = new CollectNamedTypes(s);
        NodeTraversal.traverseEs6(compiler, s.getBody(), cnt);
        s.resolveTypedefs(getTypeParser());
        s.resolveEnums(getTypeParser());
        if (NewTypeInference.measureMem) {
            NewTypeInference.updatePeakMem();
        }
    }
    // (4) The bulk of the global-scope processing happens here:
    // - Create scopes for functions
    // - Declare properties on types
    ProcessScope rootPs = new ProcessScope(globalScope);
    if (externs != null) {
        NodeTraversal.traverseEs6(compiler, externs, rootPs);
    }
    NodeTraversal.traverseEs6(compiler, root, rootPs);
    // (5) Things that must happen after the traversal of the scope
    rootPs.finishProcessingScope();
    // (6) Repeat steps 4-5 for all the other scopes (outer-to-inner)
    for (int i = 1; i < this.scopes.size(); i++) {
        NTIScope s = this.scopes.get(i);
        ProcessScope ps = new ProcessScope(s);
        NodeTraversal.traverseEs6(compiler, s.getBody(), ps);
        ps.finishProcessingScope();
        if (NewTypeInference.measureMem) {
            NewTypeInference.updatePeakMem();
        }
    }
    // (7) Adjust types of properties based on inheritance information.
    // Report errors in the inheritance chain. Do Window last.
    Collection<RawNominalType> windows = new ArrayList<>();
    for (Map.Entry<Node, RawNominalType> entry : nominaltypesByNode.entrySet()) {
        RawNominalType rawType = entry.getValue();
        if (this.window != null && rawType.hasAncestorClass(this.window)) {
            windows.add(rawType);
            continue;
        }
        checkAndFreezeNominalType(rawType);
    }
    JSType globalThisType = null;
    if (this.window != null) {
        // Copy properties from window to Window.prototype, because sometimes
        // people pass window around rather than using it directly.
        Namespace winNs = globalScope.getNamespace(WINDOW_INSTANCE);
        if (winNs != null) {
            winNs.copyWindowProperties(getCommonTypes(), this.window);
        }
        for (RawNominalType rawType : windows) {
            checkAndFreezeNominalType(rawType);
        }
        if (winNs != null) {
            ((NamespaceLit) winNs).setWindowType(this.window.getAsNominalType());
            // Type the global THIS as window
            globalThisType = winNs.toJSType();
        }
    }
    if (globalThisType == null) {
        // Type the global THIS as a loose object
        globalThisType = getCommonTypes().getTopObject().withLoose();
    }
    getCommonTypes().setGlobalThis(globalThisType);
    globalScope.setDeclaredType((new FunctionTypeBuilder(getCommonTypes())).addReceiverType(globalThisType).buildDeclaration());
    this.globalTypeInfo.setRawNominalTypes(nominaltypesByNode.values());
    nominaltypesByNode = null;
    propertyDefs = null;
    for (NTIScope s : this.scopes) {
        s.freezeScope();
    }
    this.simpleInference.setScopesAreFrozen();
    // Traverse the externs and annotate them with types.
    // Only works for the top level, not inside function bodies.
    NodeTraversal.traverseEs6(this.compiler, externs, new NodeTraversal.AbstractShallowCallback() {

        @Override
        public void visit(NodeTraversal t, Node n, Node parent) {
            if (n.isQualifiedName()) {
                Declaration d = getGlobalScope().getDeclaration(QualifiedName.fromNode(n), false);
                JSType type = simpleInferDeclaration(d);
                if (type == null) {
                    type = simpleInferExpr(n, getGlobalScope());
                }
                // Type-based passes expect the externs to be annotated, so use ? when type is null.
                n.setTypeI(type != null ? type : getCommonTypes().UNKNOWN);
            }
        }
    });
    Map<Node, String> unknownTypes = getTypeParser().getUnknownTypesMap();
    for (Map.Entry<Node, String> unknownTypeEntry : unknownTypes.entrySet()) {
        this.warnings.add(JSError.make(unknownTypeEntry.getKey(), UNRECOGNIZED_TYPE_NAME, unknownTypeEntry.getValue()));
    }
    // package, so we collect its warnings here.
    for (JSError warning : getTypeParser().getWarnings()) {
        this.warnings.add(warning);
    }
    this.warnings = null;
    this.funNameGen = null;
    reorderScopesForNTI();
    this.compiler.setExternProperties(ImmutableSet.copyOf(getExternPropertyNames()));
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList) NamespaceLit(com.google.javascript.jscomp.newtypes.NamespaceLit) AbstractShallowCallback(com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback) Namespace(com.google.javascript.jscomp.newtypes.Namespace) FunctionNamespace(com.google.javascript.jscomp.newtypes.FunctionNamespace) RawNominalType(com.google.javascript.jscomp.newtypes.RawNominalType) Declaration(com.google.javascript.jscomp.newtypes.Declaration) Map(java.util.Map) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) FunctionTypeBuilder(com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)

Example 2 with RawNominalType

use of com.google.javascript.jscomp.newtypes.RawNominalType in project closure-compiler by google.

the class GlobalTypeInfoCollector method defineObjectAndFunctionIfMissing.

private void defineObjectAndFunctionIfMissing() {
    JSTypes commonTypes = getCommonTypes();
    if (commonTypes.getObjectType() == null) {
        commonTypes.setObjectType(dummyRawTypeForMissingExterns("Object"));
    }
    if (commonTypes.getLiteralObjNominalType() == null) {
        RawNominalType objLitRawType = dummyRawTypeForMissingExterns(JSTypes.OBJLIT_CLASS_NAME);
        objLitRawType.addSuperClass(commonTypes.getObjectType());
        commonTypes.setLiteralObjNominalType(objLitRawType);
    }
    if (commonTypes.getFunctionType() == null) {
        commonTypes.setFunctionType(dummyRawTypeForMissingExterns("Function"));
    }
}
Also used : JSTypes(com.google.javascript.jscomp.newtypes.JSTypes) RawNominalType(com.google.javascript.jscomp.newtypes.RawNominalType)

Example 3 with RawNominalType

use of com.google.javascript.jscomp.newtypes.RawNominalType in project closure-compiler by google.

the class GlobalTypeInfoCollector method checkAndFreezeNominalType.

/**
 * Reconcile the properties of this nominal type with the properties it inherits from its
 * supertypes, and then freeze the type.
 *
 * NOTE(dimvar): The logic is somewhat convoluted, but I'm not sure how to make it clearer.
 * There is just a lot of case analysis involved.
 *
 * - Collect all super definitions of methods and other properties.
 * - During the collection process, warn when a property on this type is not a subtype of
 *   an inherited property on the supertype.
 * - If there are multiple inherited definitions for a property, meet them to find a single
 *   inherited type. If they meet to bottom, warn.
 * - If this type has its own definition of the property, and not just inherits it, adjust the
 *   type of the local definition based on the inherited property type.
 */
private void checkAndFreezeNominalType(RawNominalType rawType) {
    if (rawType.isFrozen()) {
        return;
    }
    checkState(inProgressFreezes.add(rawType), "Cycle in freeze order: %s (%s)", rawType, inProgressFreezes);
    NominalType superClass = rawType.getSuperClass();
    Set<String> nonInheritedPropNames = rawType.getAllNonInheritedProps();
    if (superClass != null && !superClass.isFrozen()) {
        checkAndFreezeNominalType(superClass.getRawNominalType());
    }
    for (NominalType superInterf : rawType.getInterfaces()) {
        if (!superInterf.isFrozen()) {
            checkAndFreezeNominalType(superInterf.getRawNominalType());
        }
    }
    // If this type defines a method m, collect all inherited definitions of m here in order
    // to possibly adjust the type of m.
    Multimap<String, DeclaredFunctionType> superMethodTypes = LinkedHashMultimap.create();
    // For each property p on this type, regardless of whether it is defined on the type or just
    // inherited, collect all inherited definitions of p here.
    Multimap<String, JSType> superPropTypes = LinkedHashMultimap.create();
    // Collect inherited types for extended classes
    if (superClass != null) {
        checkState(superClass.isFrozen());
        // TODO(blickly): Can we optimize this to skip unnecessary iterations?
        for (String pname : superClass.getPropertyNames()) {
            if (superClass.isAbstractClass() && superClass.hasAbstractMethod(pname) && !rawType.isAbstractClass() && !rawType.mayHaveOwnNonStrayProp(pname)) {
                warnings.add(JSError.make(rawType.getDefSite(), ABSTRACT_METHOD_NOT_IMPLEMENTED_IN_CONCRETE_CLASS, pname, superClass.getName()));
            }
            nonInheritedPropNames.remove(pname);
            checkSuperProperty(rawType, superClass, pname, superMethodTypes, superPropTypes);
        }
    }
    // Collect inherited types for extended/implemented interfaces
    for (NominalType superInterf : rawType.getInterfaces()) {
        checkState(superInterf.isFrozen());
        for (String pname : superInterf.getPropertyNames()) {
            nonInheritedPropNames.remove(pname);
            checkSuperProperty(rawType, superInterf, pname, superMethodTypes, superPropTypes);
        }
    }
    // Munge inherited types of methods
    for (String pname : superMethodTypes.keySet()) {
        Collection<DeclaredFunctionType> methodTypes = superMethodTypes.get(pname);
        checkState(!methodTypes.isEmpty());
        PropertyDef localPropDef = checkNotNull(propertyDefs.get(rawType, pname));
        // To find the declared type of a method, we must meet declared types
        // from all inherited methods.
        DeclaredFunctionType superMethodType = DeclaredFunctionType.meet(methodTypes);
        DeclaredFunctionType localMethodType = localPropDef.methodType;
        boolean getsTypeFromParent = getsTypeInfoFromParentMethod(localPropDef);
        if (superMethodType == null) {
            // If the inherited types are not compatible, pick one.
            superMethodType = methodTypes.iterator().next();
        } else if (getsTypeFromParent && localMethodType.getMaxArity() > superMethodType.getMaxArity()) {
            // When getsTypeFromParent is true, we miss the invalid override earlier, so we check here.
            warnings.add(JSError.make(localPropDef.defSite, INVALID_PROP_OVERRIDE, pname, superMethodType.toFunctionType().toString(), localMethodType.toFunctionType().toString()));
        }
        DeclaredFunctionType updatedMethodType = localMethodType.withTypeInfoFromSuper(superMethodType, getsTypeFromParent);
        localPropDef.updateMethodType(updatedMethodType);
        superPropTypes.put(pname, getCommonTypes().fromFunctionType(updatedMethodType.toFunctionType()));
    }
    // Check inherited types of all properties
    for (String pname : superPropTypes.keySet()) {
        Collection<JSType> defs = superPropTypes.get(pname);
        checkState(!defs.isEmpty());
        JSType inheritedPropType = getCommonTypes().TOP;
        for (JSType inheritedType : defs) {
            inheritedPropType = JSType.meet(inheritedPropType, inheritedType);
            if (inheritedPropType.isBottom()) {
                warnings.add(JSError.make(rawType.getDefSite(), ANCESTOR_TYPES_HAVE_INCOMPATIBLE_PROPERTIES, rawType.getName(), pname, defs.toString()));
                break;
            }
        }
        // Adjust the type of the local property definition based on the inherited type.
        PropertyDef localPropDef = propertyDefs.get(rawType, pname);
        if (localPropDef != null) {
            JSType updatedPropType;
            if (localPropDef.methodType == null) {
                JSType t = rawType.getInstancePropDeclaredType(pname);
                updatedPropType = t == null ? inheritedPropType : t.specialize(inheritedPropType);
            } else {
                // When the property is a method, the local type already includes the meet of the
                // inherited types, from the earlier loop. We don't use inheritedPropType in this branch,
                // because if the inherited method type has static properties (e.g., framework-specific
                // passes can add such properties), we don't want to inherit these.
                FunctionType ft = localPropDef.methodType.toFunctionType();
                updatedPropType = getCommonTypes().fromFunctionType(ft);
            }
            // TODO(dimvar): check if we can have @const props here
            rawType.addProtoProperty(pname, null, updatedPropType, false);
        }
    }
    // Warn when inheriting from incompatible IObject types
    if (rawType.inheritsFromIObject()) {
        JSType wrapped = rawType.getInstanceAsJSType();
        if (wrapped.getIndexType() == null) {
            warnings.add(JSError.make(rawType.getDefSite(), ANCESTOR_TYPES_HAVE_INCOMPATIBLE_PROPERTIES, rawType.getName(), "IObject<K,V>#index", "the keys K have types that can't be joined."));
        } else if (wrapped.getIndexedType() == null) {
            warnings.add(JSError.make(rawType.getDefSite(), ANCESTOR_TYPES_HAVE_INCOMPATIBLE_PROPERTIES, rawType.getName(), "IObject<K,V>#index", "the values V should have a common subtype."));
        }
    }
    // Warn for a prop declared with @override that isn't overriding anything.
    for (String pname : nonInheritedPropNames) {
        PropertyDef propDef = propertyDefs.get(rawType, pname);
        if (propDef != null) {
            Node propDefsite = propDef.defSite;
            JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(propDefsite);
            if (jsdoc != null && jsdoc.isOverride()) {
                warnings.add(JSError.make(propDefsite, UNKNOWN_OVERRIDE, pname, rawType.getName()));
            }
        }
    }
    // Freeze nominal type once all properties are added.
    rawType.freeze();
    if (rawType.isBuiltinObject()) {
        NominalType literalObj = getCommonTypes().getLiteralObjNominalType();
        if (!literalObj.isFrozen()) {
            literalObj.getRawNominalType().freeze();
        }
    }
    inProgressFreezes.remove(rawType);
}
Also used : JSType(com.google.javascript.jscomp.newtypes.JSType) RawNominalType(com.google.javascript.jscomp.newtypes.RawNominalType) NominalType(com.google.javascript.jscomp.newtypes.NominalType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) FunctionType(com.google.javascript.jscomp.newtypes.FunctionType) DeclaredFunctionType(com.google.javascript.jscomp.newtypes.DeclaredFunctionType) Node(com.google.javascript.rhino.Node) JSDocInfo(com.google.javascript.rhino.JSDocInfo)

Example 4 with RawNominalType

use of com.google.javascript.jscomp.newtypes.RawNominalType in project closure-compiler by google.

the class SimpleInference method inferPrototypeProperty.

private JSType inferPrototypeProperty(Node recv, String pname, NTIScope scope) {
    QualifiedName recvQname = QualifiedName.fromNode(recv);
    Declaration decl = scope.getDeclaration(recvQname, false);
    if (decl != null) {
        Namespace ns = decl.getNamespace();
        if (ns instanceof RawNominalType) {
            return ((RawNominalType) ns).getProtoPropDeclaredType(pname);
        }
    }
    return null;
}
Also used : QualifiedName(com.google.javascript.jscomp.newtypes.QualifiedName) RawNominalType(com.google.javascript.jscomp.newtypes.RawNominalType) Declaration(com.google.javascript.jscomp.newtypes.Declaration) FunctionNamespace(com.google.javascript.jscomp.newtypes.FunctionNamespace) Namespace(com.google.javascript.jscomp.newtypes.Namespace)

Aggregations

RawNominalType (com.google.javascript.jscomp.newtypes.RawNominalType)4 Declaration (com.google.javascript.jscomp.newtypes.Declaration)2 FunctionNamespace (com.google.javascript.jscomp.newtypes.FunctionNamespace)2 JSType (com.google.javascript.jscomp.newtypes.JSType)2 Namespace (com.google.javascript.jscomp.newtypes.Namespace)2 Node (com.google.javascript.rhino.Node)2 AbstractShallowCallback (com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback)1 DeclaredFunctionType (com.google.javascript.jscomp.newtypes.DeclaredFunctionType)1 FunctionType (com.google.javascript.jscomp.newtypes.FunctionType)1 FunctionTypeBuilder (com.google.javascript.jscomp.newtypes.FunctionTypeBuilder)1 JSTypes (com.google.javascript.jscomp.newtypes.JSTypes)1 NamespaceLit (com.google.javascript.jscomp.newtypes.NamespaceLit)1 NominalType (com.google.javascript.jscomp.newtypes.NominalType)1 QualifiedName (com.google.javascript.jscomp.newtypes.QualifiedName)1 JSDocInfo (com.google.javascript.rhino.JSDocInfo)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 LinkedHashMap (java.util.LinkedHashMap)1 Map (java.util.Map)1