From 1f46cb7994ea89cc8b861160f48924bd5866efa4 Mon Sep 17 00:00:00 2001 From: dimvar Date: Fri, 21 Apr 2017 08:24:19 -0700 Subject: [PATCH] Convert the type-transformation handling to use TypeI. The tests are still only run with the old type checker. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=153829588 --- .../javascript/jscomp/ConformanceRules.java | 2 +- .../jscomp/DisambiguateProperties.java | 2 +- .../javascript/jscomp/GlobalTypeInfo.java | 23 ++ .../javascript/jscomp/TypeInference.java | 6 +- .../javascript/jscomp/TypeMismatch.java | 2 +- .../javascript/jscomp/TypeTransformation.java | 360 +++++++----------- .../google/javascript/jscomp/TypedScope.java | 19 +- .../javascript/jscomp/newtypes/JSType.java | 23 +- .../javascript/rhino/JSTypeExpression.java | 10 +- .../google/javascript/rhino/ObjectTypeI.java | 19 +- src/com/google/javascript/rhino/TypeI.java | 20 + src/com/google/javascript/rhino/TypeIEnv.java | 57 +++ .../javascript/rhino/TypeIRegistry.java | 17 + .../javascript/rhino/jstype/JSType.java | 9 +- .../rhino/jstype/JSTypeRegistry.java | 75 +++- .../javascript/rhino/jstype/ObjectType.java | 1 + .../rhino/jstype/StaticTypedScope.java | 3 + .../jscomp/TypeTransformationTest.java | 4 +- .../javascript/rhino/jstype/JSTypeTest.java | 16 - 19 files changed, 402 insertions(+), 266 deletions(-) create mode 100644 src/com/google/javascript/rhino/TypeIEnv.java diff --git a/src/com/google/javascript/jscomp/ConformanceRules.java b/src/com/google/javascript/jscomp/ConformanceRules.java index a6cae3bad9d..a779995ebfc 100644 --- a/src/com/google/javascript/jscomp/ConformanceRules.java +++ b/src/com/google/javascript/jscomp/ConformanceRules.java @@ -511,7 +511,7 @@ private ConformanceResult checkConformance(NodeTraversal t, Node n, Property pro if (ownerFun.isConstructor()) { foundType = ownerFun.getInstanceType(); } - } else if (foundObj.isGeneric()) { + } else if (foundObj.isGenericObjectType()) { foundType = foundObj.getRawType(); } } diff --git a/src/com/google/javascript/jscomp/DisambiguateProperties.java b/src/com/google/javascript/jscomp/DisambiguateProperties.java index 50063335933..51f8732122b 100644 --- a/src/com/google/javascript/jscomp/DisambiguateProperties.java +++ b/src/com/google/javascript/jscomp/DisambiguateProperties.java @@ -973,7 +973,7 @@ private ObjectTypeI getTypeWithProperty(String field, TypeI type) { } // Unwrap templatized types, they are not unique at runtime. - if (foundType != null && foundType.isGeneric()) { + if (foundType != null && foundType.isGenericObjectType()) { foundType = foundType.getRawType(); } diff --git a/src/com/google/javascript/jscomp/GlobalTypeInfo.java b/src/com/google/javascript/jscomp/GlobalTypeInfo.java index 5ea4cc9871a..51631b8d835 100644 --- a/src/com/google/javascript/jscomp/GlobalTypeInfo.java +++ b/src/com/google/javascript/jscomp/GlobalTypeInfo.java @@ -48,8 +48,10 @@ import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.ObjectTypeI; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TypeI; +import com.google.javascript.rhino.TypeIEnv; import com.google.javascript.rhino.TypeIRegistry; import com.google.javascript.rhino.jstype.JSTypeNative; import java.util.ArrayList; @@ -2883,8 +2885,29 @@ public TypeI createUnionType(List members) { return result; } + @Override + public TypeI evaluateTypeExpression(JSTypeExpression expr, TypeIEnv typeEnv) { + throw new UnsupportedOperationException(); + } + @Override public TypeI evaluateTypeExpressionInGlobalScope(JSTypeExpression expr) { return createTypeFromCommentNode(expr.getRoot()); } + + @Override + public TypeI createRecordType(Map props) { + throw new UnsupportedOperationException(); + } + + @Override + public TypeI instantiateGenericType( + ObjectTypeI genericType, ImmutableList typeArgs) { + throw new UnsupportedOperationException(); + } + + @Override + public TypeI buildRecordTypeFromObject(ObjectTypeI obj) { + throw new UnsupportedOperationException(); + } } diff --git a/src/com/google/javascript/jscomp/TypeInference.java b/src/com/google/javascript/jscomp/TypeInference.java index 5e0072446a2..fdefc58e96c 100644 --- a/src/com/google/javascript/jscomp/TypeInference.java +++ b/src/com/google/javascript/jscomp/TypeInference.java @@ -1386,8 +1386,10 @@ private Map evaluateTypeTransformations( } // Evaluate the type transformation expression using the current // known types for the template type variables - JSType transformedType = ttlObj.eval(type.getTypeTransformation(), - ImmutableMap.copyOf(typeVars)); + @SuppressWarnings({"unchecked", "rawtypes"}) + JSType transformedType = (JSType) ttlObj.eval( + type.getTypeTransformation(), + (ImmutableMap) ImmutableMap.copyOf(typeVars)); result.put(type, transformedType); // Add the transformed type to the type variables typeVars.put(type.getReferenceName(), transformedType); diff --git a/src/com/google/javascript/jscomp/TypeMismatch.java b/src/com/google/javascript/jscomp/TypeMismatch.java index dbf90f31cec..28d1e18ee2d 100644 --- a/src/com/google/javascript/jscomp/TypeMismatch.java +++ b/src/com/google/javascript/jscomp/TypeMismatch.java @@ -126,7 +126,7 @@ static void recordImplicitInterfaceUses( private static TypeI removeNullUndefinedAndTemplates(TypeI t) { TypeI result = t.restrictByNotNullOrUndefined(); ObjectTypeI obj = result.toMaybeObjectType(); - if (obj != null && obj.isGeneric()) { + if (obj != null && obj.isGenericObjectType()) { return obj.instantiateGenericsWithUnknown(); } return result; diff --git a/src/com/google/javascript/jscomp/TypeTransformation.java b/src/com/google/javascript/jscomp/TypeTransformation.java index fb8028be9f9..cd10ca1d8dd 100644 --- a/src/com/google/javascript/jscomp/TypeTransformation.java +++ b/src/com/google/javascript/jscomp/TypeTransformation.java @@ -24,23 +24,16 @@ import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; -import com.google.javascript.rhino.jstype.FunctionType; -import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.ObjectTypeI; +import com.google.javascript.rhino.TypeI; +import com.google.javascript.rhino.TypeIEnv; +import com.google.javascript.rhino.TypeIRegistry; import com.google.javascript.rhino.jstype.JSTypeNative; -import com.google.javascript.rhino.jstype.JSTypeRegistry; -import com.google.javascript.rhino.jstype.ObjectType; -import com.google.javascript.rhino.jstype.RecordType; -import com.google.javascript.rhino.jstype.RecordTypeBuilder; -import com.google.javascript.rhino.jstype.StaticTypedScope; -import com.google.javascript.rhino.jstype.StaticTypedSlot; -import com.google.javascript.rhino.jstype.TemplatizedType; -import com.google.javascript.rhino.jstype.UnionType; - +import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; /** * A class for processing type transformation expressions @@ -48,10 +41,6 @@ * @author lpino@google.com (Luis Fernando Pino Duque) */ class TypeTransformation { - private AbstractCompiler compiler; - private JSTypeRegistry typeRegistry; - private StaticTypedScope scope; - static final DiagnosticType UNKNOWN_TYPEVAR = DiagnosticType.warning("TYPEVAR_UNDEFINED", "Reference to an unknown type variable {0}"); @@ -97,25 +86,29 @@ class TypeTransformation { DiagnosticType.warning("PROPTYPE_INVALID", "Expected object type, found {0}"); + private final AbstractCompiler compiler; + private final TypeIRegistry registry; + private final TypeIEnv typeEnv; + /** * A helper class for holding the information about the type variables * and the name variables in maprecord expressions */ private static class NameResolver { - ImmutableMap typeVars; + ImmutableMap typeVars; ImmutableMap nameVars; - NameResolver(ImmutableMap typeVars, - ImmutableMap nameVars) { + NameResolver(ImmutableMap typeVars, ImmutableMap nameVars) { this.typeVars = typeVars; this.nameVars = nameVars; } } - TypeTransformation(AbstractCompiler compiler, StaticTypedScope scope) { + @SuppressWarnings("unchecked") + TypeTransformation(AbstractCompiler compiler, TypeIEnv typeEnv) { this.compiler = compiler; - this.typeRegistry = compiler.getTypeRegistry(); - this.scope = scope; + this.registry = compiler.getTypeRegistry(); + this.typeEnv = (TypeIEnv) typeEnv; } private boolean isTypeVar(Node n) { @@ -134,92 +127,52 @@ private Keywords nameToKeyword(String s) { return TypeTransformationParser.Keywords.valueOf(s.toUpperCase()); } - private StaticTypedScope getScope(StaticTypedScope scope, String name) { - StaticTypedSlot slot = scope.getOwnSlot(name); - if (slot != null) { - return scope; - } - return getScope(scope.getParentScope(), name); - } - - private JSType getType(String name) { - // Case template type names inside a class - // (borrowed from JSTypeRegistry#getType - JSType type = null; - JSType thisType = null; - if (scope != null && scope.getTypeOfThis() != null) { - thisType = scope.getTypeOfThis().toObjectType(); - } - if (thisType != null) { - type = thisType.getTemplateTypeMap().getTemplateTypeKeyByName(name); - if (type != null) { - Preconditions.checkState(type.isTemplateType(), - "Expected a template type, but found: %s", type); - return type; - } - } - + private TypeI getType(String typeName) { // Resolve the name and get the corresponding type - StaticTypedSlot slot = scope.getSlot(name); - if (slot != null) { - JSType rawType = slot.getType(); - if (rawType != null) { - // Case constructor, get the instance type - if ((rawType.isConstructor() || rawType.isInterface()) - && rawType.isFunctionType() && rawType.isNominalConstructor()) { - return rawType.toMaybeFunctionType().getInstanceType(); - } - // Case enum - if (rawType.isEnumType()) { - return rawType.toMaybeEnumType().getElementsType(); - } + TypeI type = typeEnv.getType(typeName); + JSDocInfo jsdoc = typeEnv.getJsdocOfTypeDeclaration(typeName); + if (type != null) { + // Case constructor, get the instance type + if (type.isConstructor() || type.isInterface()) { + return type.toMaybeFunctionType().getInstanceType(); } - // Case typedef - JSDocInfo info = slot.getJSDocInfo(); - if (info != null && info.hasTypedefType()) { - JSTypeExpression expr = info.getTypedefType(); - StaticTypedScope typedefScope = getScope(scope, name); - return expr.evaluate(typedefScope, typeRegistry); + // Case enum + if (type.isEnumElement()) { + return type.getEnumeratedTypeOfEnumElement(); } + } else if (jsdoc != null && jsdoc.hasTypedefType()) { + return this.registry.evaluateTypeExpression(jsdoc.getTypedefType(), typeEnv); } // Otherwise handle native types - return typeRegistry.getType(name); + return registry.getType(typeName); } - private boolean isTemplatizable(JSType type) { - return typeRegistry.isTemplatizable(type); + private TypeI getUnknownType() { + return registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } - private JSType getUnknownType() { - return typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); + private TypeI getNoType() { + return registry.getNativeObjectType(JSTypeNative.NO_TYPE); } - private JSType getNoType() { - return typeRegistry.getNativeObjectType(JSTypeNative.NO_TYPE); + private TypeI getAllType() { + return registry.getNativeType(JSTypeNative.ALL_TYPE); } - private JSType getAllType() { - return typeRegistry.getNativeType(JSTypeNative.ALL_TYPE); + private TypeI getObjectType() { + return registry.getNativeType(JSTypeNative.OBJECT_TYPE); } - private JSType getObjectType() { - return typeRegistry.getNativeType(JSTypeNative.OBJECT_TYPE); + private TypeI createUnionType(TypeI[] variants) { + return registry.createUnionType(Arrays.asList(variants)); } - private JSType createUnionType(JSType... variants) { - return typeRegistry.createUnionType(variants); + private TypeI createTemplatizedType(ObjectTypeI baseType, TypeI[] params) { + return registry.instantiateGenericType(baseType, ImmutableList.copyOf(params)); } - private JSType createTemplatizedType(ObjectType baseType, JSType[] params) { - return typeRegistry.createTemplatizedType(baseType, params); - } - - private JSType createRecordType(ImmutableMap props) { - RecordTypeBuilder builder = new RecordTypeBuilder(typeRegistry); - for (Entry e : props.entrySet()) { - builder.addProperty(e.getKey(), e.getValue(), null); - } - return builder.build(); + private TypeI createRecordType(ImmutableMap props) { + return this.registry.createRecordType(props); } private void reportWarning(Node n, DiagnosticType msg, String... param) { @@ -287,9 +240,9 @@ private String getComputedPropName(Node n) { * expression * @param typeVars The environment containing the information about * the type variables - * @return JSType The resulting type after the transformation + * @return TypeI The resulting type after the transformation */ - JSType eval(Node ttlAst, ImmutableMap typeVars) { + TypeI eval(Node ttlAst, ImmutableMap typeVars) { return eval(ttlAst, typeVars, ImmutableMap.of()); } @@ -302,14 +255,17 @@ JSType eval(Node ttlAst, ImmutableMap typeVars) { * the type variables * @param nameVars The environment containing the information about * the name variables - * @return JSType The resulting type after the transformation + * @return TypeI The resulting type after the transformation */ - JSType eval(Node ttlAst, ImmutableMap typeVars, + @SuppressWarnings("unchecked") + TypeI eval(Node ttlAst, ImmutableMap typeVars, ImmutableMap nameVars) { - return evalInternal(ttlAst, new NameResolver(typeVars, nameVars)); + return evalInternal( + ttlAst, + new NameResolver(typeVars, nameVars)); } - private JSType evalInternal(Node ttlAst, NameResolver nameResolver) { + private TypeI evalInternal(Node ttlAst, NameResolver nameResolver) { if (isTypeName(ttlAst)) { return evalTypeName(ttlAst); } @@ -329,7 +285,7 @@ private JSType evalInternal(Node ttlAst, NameResolver nameResolver) { } } - private JSType evalOperationExpression(Node ttlAst, NameResolver nameResolver) { + private TypeI evalOperationExpression(Node ttlAst, NameResolver nameResolver) { String name = getCallName(ttlAst); Keywords keyword = nameToKeyword(name); switch (keyword) { @@ -352,7 +308,7 @@ private JSType evalOperationExpression(Node ttlAst, NameResolver nameResolver) { } } - private JSType evalTypeExpression(Node ttlAst, NameResolver nameResolver) { + private TypeI evalTypeExpression(Node ttlAst, NameResolver nameResolver) { String name = getCallName(ttlAst); Keywords keyword = nameToKeyword(name); switch (keyword) { @@ -379,9 +335,9 @@ private JSType evalTypeExpression(Node ttlAst, NameResolver nameResolver) { } } - private JSType evalTypeName(Node ttlAst) { + private TypeI evalTypeName(Node ttlAst) { String typeName = ttlAst.getString(); - JSType resultingType = getType(typeName); + TypeI resultingType = getType(typeName); // If the type name is not defined then return UNKNOWN and report a warning if (resultingType == null) { reportWarning(ttlAst, UNKNOWN_TYPENAME, typeName); @@ -390,28 +346,28 @@ private JSType evalTypeName(Node ttlAst) { return resultingType; } - private JSType evalTemplatizedType(Node ttlAst, NameResolver nameResolver) { + private TypeI evalTemplatizedType(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); - JSType firstParam = evalInternal(params.get(0), nameResolver); - if (!isTemplatizable(firstParam)) { + TypeI firstParam = evalInternal(params.get(0), nameResolver); + if (!firstParam.hasUninstantiatedTypeVariables()) { reportWarning(ttlAst, BASETYPE_INVALID, firstParam.toString()); return getUnknownType(); } - ObjectType baseType = firstParam.toObjectType(); // TODO(lpino): Check that the number of parameters correspond with the // number of template types that the base type can take when creating // a templatized type. For instance, if the base type is Array then there // must be just one parameter. - JSType[] templatizedTypes = new JSType[params.size() - 1]; + TypeI[] templatizedTypes = new TypeI[params.size() - 1]; for (int i = 0; i < templatizedTypes.length; i++) { templatizedTypes[i] = evalInternal(params.get(i + 1), nameResolver); } + ObjectTypeI baseType = firstParam.toMaybeObjectType(); return createTemplatizedType(baseType, templatizedTypes); } - private JSType evalTypeVar(Node ttlAst, NameResolver nameResolver) { + private TypeI evalTypeVar(Node ttlAst, NameResolver nameResolver) { String typeVar = ttlAst.getString(); - JSType resultingType = nameResolver.typeVars.get(typeVar); + TypeI resultingType = nameResolver.typeVars.get(typeVar); // If the type variable is not defined then return UNKNOWN and report a warning if (resultingType == null) { reportWarning(ttlAst, UNKNOWN_TYPEVAR, typeVar); @@ -420,22 +376,22 @@ private JSType evalTypeVar(Node ttlAst, NameResolver nameResolver) { return resultingType; } - private JSType evalUnionType(Node ttlAst, NameResolver nameResolver) { + private TypeI evalUnionType(Node ttlAst, NameResolver nameResolver) { // Get the parameters of the union ImmutableList params = getCallParams(ttlAst); int paramCount = params.size(); // Create an array of types after evaluating each parameter - JSType[] basicTypes = new JSType[paramCount]; + TypeI[] basicTypes = new TypeI[paramCount]; for (int i = 0; i < paramCount; i++) { basicTypes[i] = evalInternal(params.get(i), nameResolver); } return createUnionType(basicTypes); } - private JSType[] evalTypeParams(Node ttlAst, NameResolver nameResolver) { + private TypeI[] evalTypeParams(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); int paramCount = params.size(); - JSType[] result = new JSType[paramCount]; + TypeI[] result = new TypeI[paramCount]; for (int i = 0; i < paramCount; i++) { result[i] = evalInternal(params.get(i), nameResolver); } @@ -464,24 +420,24 @@ private String[] evalStringParams(Node ttlAst, NameResolver nameResolver) { return result; } - private boolean evalTypePredicate(Node ttlAst, - NameResolver nameResolver) { - JSType[] params = evalTypeParams(ttlAst, nameResolver); + private boolean evalTypePredicate(Node ttlAst, NameResolver nameResolver) { + TypeI[] params = evalTypeParams(ttlAst, nameResolver); String name = getCallName(ttlAst); Keywords keyword = nameToKeyword(name); + TypeI type = params[0]; switch (keyword) { case EQ: - return params[0].isEquivalentTo(params[1]); + return type.isEquivalentTo(params[1]); case SUB: - return params[0].isSubtype(params[1]); + return type.isSubtypeOf(params[1]); case ISCTOR: - return params[0].isConstructor(); + return type.isConstructor(); case ISTEMPLATIZED: - return params[0].isTemplatizedType(); + return type.isObjectType() && type.toMaybeObjectType().isGenericObjectType(); case ISRECORD: - return params[0].isRecordType(); + return type.isRecordType(); case ISUNKNOWN: - return params[0].isUnknownType() || params[0].isCheckedUnknownType(); + return type.isSomeUnknownType(); default: throw new IllegalStateException( "Invalid type predicate in the type transformation"); @@ -555,7 +511,7 @@ private boolean evalBoolean(Node ttlAst, NameResolver nameResolver) { } } - private JSType evalConditional(Node ttlAst, NameResolver nameResolver) { + private TypeI evalConditional(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); if (evalBoolean(params.get(0), nameResolver)) { return evalInternal(params.get(1), nameResolver); @@ -564,7 +520,7 @@ private JSType evalConditional(Node ttlAst, NameResolver nameResolver) { } } - private JSType evalMapunion(Node ttlAst, NameResolver nameResolver) { + private TypeI evalMapunion(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); Node unionParam = params.get(0); Node mapFunction = params.get(1); @@ -577,7 +533,7 @@ private JSType evalMapunion(Node ttlAst, NameResolver nameResolver) { } Node mapFunctionBody = getFunctionBody(mapFunction); - JSType unionType = evalInternal(unionParam, nameResolver); + TypeI unionType = evalInternal(unionParam, nameResolver); // If the first parameter does not correspond to a union type then // consider it as a union with a single type and evaluate if (!unionType.isUnionType()) { @@ -589,12 +545,12 @@ private JSType evalMapunion(Node ttlAst, NameResolver nameResolver) { // Otherwise obtain the elements in the union type. Note that the block // above guarantees the casting to be safe - Collection unionElms = ((UnionType) unionType).getAlternates(); + Collection unionElms = ImmutableList.copyOf(unionType.getUnionMembers()); // Evaluate the map function body using each element in the union type int unionSize = unionElms.size(); - JSType[] newUnionElms = new JSType[unionSize]; + TypeI[] newUnionElms = new TypeI[unionSize]; int i = 0; - for (JSType elm : unionElms) { + for (TypeI elm : unionElms) { NameResolver newNameResolver = new NameResolver( addNewEntry(nameResolver.typeVars, paramName, elm), nameResolver.nameVars); @@ -605,26 +561,25 @@ private JSType evalMapunion(Node ttlAst, NameResolver nameResolver) { return createUnionType(newUnionElms); } - private JSType evalRawTypeOf(Node ttlAst, NameResolver nameResolver) { + private TypeI evalRawTypeOf(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); - JSType type = evalInternal(params.get(0), nameResolver); - if (!type.isTemplatizedType()) { + TypeI type = evalInternal(params.get(0), nameResolver); + if (!type.isGenericObjectType()) { reportWarning(ttlAst, TEMPTYPE_INVALID, "rawTypeOf", type.toString()); return getUnknownType(); } - return ((TemplatizedType) type).getReferencedType(); + return type.toMaybeObjectType().getRawType(); } - private JSType evalTemplateTypeOf(Node ttlAst, NameResolver nameResolver) { + private TypeI evalTemplateTypeOf(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); - JSType type = evalInternal(params.get(0), nameResolver); - if (!type.isTemplatizedType()) { + TypeI type = evalInternal(params.get(0), nameResolver); + if (!type.isGenericObjectType()) { reportWarning(ttlAst, TEMPTYPE_INVALID, "templateTypeOf", type.toString()); return getUnknownType(); } int index = (int) params.get(1).getDouble(); - ImmutableList templateTypes = - ((TemplatizedType) type).getTemplateTypes(); + ImmutableList templateTypes = type.toMaybeObjectType().getTemplateTypes(); if (index >= templateTypes.size()) { reportWarning(ttlAst, INDEX_OUTOFBOUNDS, Integer.toString(index), Integer.toString(templateTypes.size())); @@ -633,8 +588,8 @@ private JSType evalTemplateTypeOf(Node ttlAst, NameResolver nameResolver) { return templateTypes.get(index); } - private JSType evalRecord(Node record, NameResolver nameResolver) { - RecordTypeBuilder builder = new RecordTypeBuilder(typeRegistry); + private TypeI evalRecord(Node record, NameResolver nameResolver) { + Map props = new HashMap<>(); for (Node propNode : record.children()) { // If it is a computed property then find the property name using the resolver if (propNode.isComputedProp()) { @@ -647,19 +602,19 @@ private JSType evalRecord(Node record, NameResolver nameResolver) { // Otherwise add the property Node propValue = getComputedPropValue(propNode); String resolvedName = nameResolver.nameVars.get(compPropName); - JSType resultingType = evalInternal(propValue, nameResolver); - builder.addProperty(resolvedName, resultingType, null); + TypeI resultingType = evalInternal(propValue, nameResolver); + props.put(resolvedName, resultingType); } else { String propName = propNode.getString(); - JSType resultingType = evalInternal(propNode.getFirstChild(), + TypeI resultingType = evalInternal(propNode.getFirstChild(), nameResolver); - builder.addProperty(propName, resultingType, null); + props.put(propName, resultingType); } } - return builder.build(); + return this.registry.createRecordType(props); } - private JSType evalRecordParam(Node ttlAst, NameResolver nameResolver) { + private TypeI evalRecordParam(Node ttlAst, NameResolver nameResolver) { if (ttlAst.isObjectLit()) { return evalRecord(ttlAst, nameResolver); } @@ -667,47 +622,27 @@ private JSType evalRecordParam(Node ttlAst, NameResolver nameResolver) { return evalInternal(ttlAst, nameResolver); } - private JSType buildRecordTypeFromObject(ObjectType objType) { - RecordType recType = objType.toMaybeRecordType(); - // If it can be casted to a record type then return - if (recType != null) { - return recType; - } - // TODO(lpino): Handle inherited properties - Set propNames = objType.getOwnPropertyNames(); - // If the type has no properties then return Object - if (propNames.isEmpty()) { - return getObjectType(); - } - ImmutableMap.Builder props = new ImmutableMap.Builder<>(); - // Otherwise collect the properties and build a record type - for (String propName : propNames) { - props.put(propName, objType.getPropertyType(propName)); - } - return createRecordType(props.build()); - } - - private JSType evalRecordType(Node ttlAst, NameResolver nameResolver) { + private TypeI evalRecordType(Node ttlAst, NameResolver nameResolver) { int paramCount = getCallParamCount(ttlAst); - ImmutableList.Builder recTypesBuilder = new ImmutableList.Builder<>(); + ImmutableList.Builder recTypesBuilder = new ImmutableList.Builder<>(); for (int i = 0; i < paramCount; i++) { - JSType type = evalRecordParam(getCallArgument(ttlAst, i), nameResolver); + TypeI type = evalRecordParam(getCallArgument(ttlAst, i), nameResolver); // Check that each parameter evaluates to an object - ObjectType objType = type.toObjectType(); + ObjectTypeI objType = type.toMaybeObjectType(); if (objType == null || objType.isUnknownType()) { reportWarning(ttlAst, RECPARAM_INVALID, type.toString()); return getUnknownType(); } - JSType recType = buildRecordTypeFromObject(objType); + TypeI recType = this.registry.buildRecordTypeFromObject(objType); if (!recType.isEquivalentTo(getObjectType())) { - recTypesBuilder.add(recType.toMaybeRecordType()); + recTypesBuilder.add(recType.toMaybeObjectType()); } } return joinRecordTypes(recTypesBuilder.build()); } - private void putNewPropInPropertyMap(Map props, - String newPropName, JSType newPropValue) { + private void putNewPropInPropertyMap(Map props, + String newPropName, TypeI newPropValue) { // TODO(lpino): Decide if the best strategy is to collapse the properties // to a union type or not. So far, new values replace the old ones except // if they are two record types in which case the properties are joined @@ -727,18 +662,9 @@ private void putNewPropInPropertyMap(Map props, } // Otherwise join the current value with the new one since both are records props.put(newPropName, - joinRecordTypes(ImmutableList.of( - (RecordType) props.get(newPropName), - (RecordType) newPropValue))); - } - - private void addNewPropsFromRecordType(Map props, - RecordType recType) { - for (String newPropName : recType.getOwnPropertyNames()) { - JSType newPropValue = recType.getSlot(newPropName).getType(); - // Put the new property depending if it already exists in the map - putNewPropInPropertyMap(props, newPropName, newPropValue); - } + joinRecordTypes(ImmutableList.of( + (ObjectTypeI) props.get(newPropName), + (ObjectTypeI) newPropValue))); } /** @@ -747,17 +673,21 @@ private void addNewPropsFromRecordType(Map props, * {r:{s:string, n:number}} and {a:boolean} * is transformed into {r:{s:string, n:number}, a:boolean} */ - private JSType joinRecordTypes(ImmutableList recTypes) { - Map props = new LinkedHashMap<>(); - for (int i = 0; i < recTypes.size(); i++) { - addNewPropsFromRecordType(props, recTypes.get(i)); + private TypeI joinRecordTypes(ImmutableList recTypes) { + Map props = new LinkedHashMap<>(); + for (ObjectTypeI recType : recTypes) { + for (String newPropName : recType.getOwnPropertyNames()) { + TypeI newPropValue = recType.getPropertyType(newPropName); + // Put the new property depending if it already exists in the map + putNewPropInPropertyMap(props, newPropName, newPropValue); + } } return createRecordType(ImmutableMap.copyOf(props)); } - private JSType evalMaprecord(Node ttlAst, NameResolver nameResolver) { + private TypeI evalMaprecord(Node ttlAst, NameResolver nameResolver) { ImmutableList params = getCallParams(ttlAst); - JSType type = evalInternal(params.get(0), nameResolver); + TypeI type = evalInternal(params.get(0), nameResolver); // If it is an empty record type (Object) then return if (type.isEquivalentTo(getObjectType())) { @@ -771,11 +701,7 @@ private JSType evalMaprecord(Node ttlAst, NameResolver nameResolver) { return getUnknownType(); } - // Obtain the elements in the record type. Note that the block - // above guarantees the casting to be safe - RecordType recType = ((RecordType) type); - Set ownPropsNames = recType.getOwnPropertyNames(); - + ObjectTypeI objtype = type.toMaybeObjectType(); // Fetch the information of the map function Node mapFunction = params.get(1); String paramKey = getFunctionParameter(mapFunction, 0); @@ -793,17 +719,17 @@ private JSType evalMaprecord(Node ttlAst, NameResolver nameResolver) { // Compute the new properties using the map function Node mapFnBody = getFunctionBody(mapFunction); - Map newProps = new LinkedHashMap<>(); - for (String propName : ownPropsNames) { + Map newProps = new LinkedHashMap<>(); + for (String propName : objtype.getOwnPropertyNames()) { // The value of the current property - JSType propValue = recType.getSlot(propName).getType(); + TypeI propValue = objtype.getPropertyType(propName); // Evaluate the map function body with paramValue and paramKey replaced // by the values of the current property NameResolver newNameResolver = new NameResolver( addNewEntry(nameResolver.typeVars, paramValue, propValue), addNewEntry(nameResolver.nameVars, paramKey, propName)); - JSType body = evalInternal(mapFnBody, newNameResolver); + TypeI body = evalInternal(mapFnBody, newNameResolver); // If the body returns unknown then the whole expression returns unknown if (body.isUnknownType()) { @@ -812,7 +738,7 @@ private JSType evalMaprecord(Node ttlAst, NameResolver nameResolver) { // Skip the property when the body evaluates to NO_TYPE // or the empty record (Object) - if (body.isNoType() || body.isEquivalentTo(getObjectType())) { + if (body.isBottom() || body.isEquivalentTo(getObjectType())) { continue; } @@ -823,9 +749,9 @@ private JSType evalMaprecord(Node ttlAst, NameResolver nameResolver) { } // Add the properties of the resulting record type to the original one - RecordType bodyAsRecType = ((RecordType) body); - for (String newPropName : bodyAsRecType.getOwnPropertyNames()) { - JSType newPropValue = bodyAsRecType.getSlot(newPropName).getType(); + ObjectTypeI bodyAsObj = body.toMaybeObjectType(); + for (String newPropName : bodyAsObj.getOwnPropertyNames()) { + TypeI newPropValue = bodyAsObj.getPropertyType(newPropName); // If the key already exists then we have to mix it with the current // property value putNewPropInPropertyMap(newProps, newPropName, newPropValue); @@ -834,40 +760,40 @@ private JSType evalMaprecord(Node ttlAst, NameResolver nameResolver) { return createRecordType(ImmutableMap.copyOf(newProps)); } - private JSType evalTypeOfVar(Node ttlAst) { + private TypeI evalTypeOfVar(Node ttlAst) { String name = getCallArgument(ttlAst, 0).getString(); - StaticTypedSlot slot = scope.getSlot(name); - if (slot == null) { + TypeI type = typeEnv.getType(name); + if (type == null) { reportWarning(ttlAst, VAR_UNDEFINED, name); return getUnknownType(); } - return slot.getType(); + return type; } - private JSType evalInstanceOf(Node ttlAst, NameResolver nameResolver) { - JSType type = evalInternal(getCallArgument(ttlAst, 0), nameResolver); + private TypeI evalInstanceOf(Node ttlAst, NameResolver nameResolver) { + TypeI type = evalInternal(getCallArgument(ttlAst, 0), nameResolver); if (type.isUnknownType() || !type.isConstructor()) { reportWarning(ttlAst, INVALID_CTOR, type.getDisplayName()); return getUnknownType(); } - return ((FunctionType) type).getInstanceType(); + return type.toMaybeFunctionType().getInstanceType(); } - private JSType evalNativeTypeExpr(Node ttlAst) { - return new JSTypeExpression( - getCallArgument(ttlAst, 0), "").evaluate(scope, typeRegistry); + private TypeI evalNativeTypeExpr(Node ttlAst) { + JSTypeExpression expr = new JSTypeExpression(getCallArgument(ttlAst, 0), ""); + return this.registry.evaluateTypeExpression(expr, typeEnv); } - private JSType evalPrintType(Node ttlAst, NameResolver nameResolver) { - JSType type = evalInternal(getCallArgument(ttlAst, 1), nameResolver); + private TypeI evalPrintType(Node ttlAst, NameResolver nameResolver) { + TypeI type = evalInternal(getCallArgument(ttlAst, 1), nameResolver); String msg = getCallArgument(ttlAst, 0).getString() + type; System.out.println(msg); return type; } - private JSType evalPropType(Node ttlAst, NameResolver nameResolver) { - JSType type = evalInternal(getCallArgument(ttlAst, 1), nameResolver); - ObjectType objType = type.toObjectType(); + private TypeI evalPropType(Node ttlAst, NameResolver nameResolver) { + TypeI type = evalInternal(getCallArgument(ttlAst, 1), nameResolver); + ObjectTypeI objType = type.toMaybeObjectType(); if (objType == null) { reportWarning(ttlAst, PROPTYPE_INVALID, type.toString()); return getUnknownType(); diff --git a/src/com/google/javascript/jscomp/TypedScope.java b/src/com/google/javascript/jscomp/TypedScope.java index 9001737fa27..d11e876fbd4 100644 --- a/src/com/google/javascript/jscomp/TypedScope.java +++ b/src/com/google/javascript/jscomp/TypedScope.java @@ -19,11 +19,13 @@ import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; +import com.google.javascript.rhino.TypeIEnv; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticTypedScope; - +import com.google.javascript.rhino.jstype.StaticTypedSlot; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -45,7 +47,7 @@ * The reason for this is that we want to shadow methods from the parent class, to avoid calling * them accidentally. */ -public class TypedScope extends Scope implements StaticTypedScope { +public class TypedScope extends Scope implements StaticTypedScope, TypeIEnv { private final Map vars = new LinkedHashMap<>(); private final TypedScope parent; /** Whether this is a bottom scope for the purposes of type inference. */ @@ -277,4 +279,17 @@ public Scope getClosestHoistScope() { throw new IllegalStateException( "Method getClosestHoistScope cannot be called on typed scopes."); } + + @SuppressWarnings("unchecked") + @Override + public JSType getType(String typeName) { + StaticTypedSlot slot = getSlot(typeName); + return slot == null ? null : slot.getType(); + } + + @Override + public JSDocInfo getJsdocOfTypeDeclaration(String typeName) { + StaticTypedSlot slot = getSlot(typeName); + return slot == null ? null : slot.getJSDocInfo(); + } } diff --git a/src/com/google/javascript/jscomp/newtypes/JSType.java b/src/com/google/javascript/jscomp/newtypes/JSType.java index 77efd3033d9..bc617b548b1 100644 --- a/src/com/google/javascript/jscomp/newtypes/JSType.java +++ b/src/com/google/javascript/jscomp/newtypes/JSType.java @@ -18,6 +18,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; @@ -1968,7 +1969,7 @@ public final boolean isObjectType() { } @Override - public final boolean isGeneric() { + public final boolean isGenericObjectType() { NominalType nt = getNominalTypeIfSingletonObj(); return nt != null && nt.isGeneric(); } @@ -2098,6 +2099,26 @@ public final Iterable getParameterTypes() { return Preconditions.checkNotNull(getFunType()).getParameterTypes(); } + @Override + public TypeI getPropertyType(String propName) { + return isObjectType() ? getProp(new QualifiedName(propName)) : null; + } + + @Override + public boolean isRecordType() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasUninstantiatedTypeVariables() { + throw new UnsupportedOperationException(); + } + + @Override + public ImmutableList getTemplateTypes() { + throw new UnsupportedOperationException(); + } + // Note: concrete subclasses follow below. The above code in JSType // should not depend on any of these specific implementations, other // than instantiating them when appropriate (in the makeType methods). diff --git a/src/com/google/javascript/rhino/JSTypeExpression.java b/src/com/google/javascript/rhino/JSTypeExpression.java index b2886274fec..feb1ee5e174 100644 --- a/src/com/google/javascript/rhino/JSTypeExpression.java +++ b/src/com/google/javascript/rhino/JSTypeExpression.java @@ -45,8 +45,10 @@ import java.io.Serializable; /** - * Represents a type expression as a miniature Rhino AST, so that the - * type expression can be evaluated later. + * When parsing a jsdoc, a type-annotation string is parsed to a type AST. + * Somewhat confusingly, we use the Node class both for type ASTs and for the source-code AST. + * JSTypeExpression wraps a type AST. + * During type checking, type ASTs are evaluated to JavaScript types. * * @author nicksantos@google.com (Nick Santos) */ @@ -122,6 +124,10 @@ public Node getRoot() { return root; } + public String getSourceName() { + return this.sourceName; + } + @Override public String toString() { return "type: " + root.toStringTree(); diff --git a/src/com/google/javascript/rhino/ObjectTypeI.java b/src/com/google/javascript/rhino/ObjectTypeI.java index 371488cffc3..0145f542ac4 100644 --- a/src/com/google/javascript/rhino/ObjectTypeI.java +++ b/src/com/google/javascript/rhino/ObjectTypeI.java @@ -39,6 +39,7 @@ package com.google.javascript.rhino; +import com.google.common.collect.ImmutableList; import java.util.Collection; /** @@ -77,10 +78,19 @@ public interface ObjectTypeI extends TypeI { // true for "classy" objects only? boolean isInstanceType(); - boolean isGeneric(); - ObjectTypeI getRawType(); + /** + * For an instantiated generic type, return the types that the type variables are mapped to. + * + * TODO(dimvar): this could have a better name, but it's used by non-compiler code. + * Rename in a follow-up CL. + * + * TODO(dimvar): After deleting the old type checker, change the signature of this and other + * methods in TypeI to stop using wildcards. + */ + ImmutableList getTemplateTypes(); + ObjectTypeI instantiateGenericsWithUnknown(); boolean hasProperty(String propertyName); @@ -128,4 +138,9 @@ public interface ObjectTypeI extends TypeI { * If this type represents the object in a function's prototype property, return that function. */ FunctionTypeI getOwnerFunction(); + + /** + * Returns the type of property propName on this object, or null if the property doesn't exist. + */ + TypeI getPropertyType(String propName); } diff --git a/src/com/google/javascript/rhino/TypeI.java b/src/com/google/javascript/rhino/TypeI.java index 2f123a8b92c..cbaf4b34d3f 100644 --- a/src/com/google/javascript/rhino/TypeI.java +++ b/src/com/google/javascript/rhino/TypeI.java @@ -86,6 +86,11 @@ public interface TypeI { boolean isObjectType(); + /** + * True if this type represents a generic object (non function) type, instantiated or not. + */ + boolean isGenericObjectType(); + /** * True when the nominal type of this type is Object. The name is not great, because objects * with a different nominal type can flow to places that treat them as Object. But I'm not @@ -118,6 +123,12 @@ public interface TypeI { */ boolean isStringValueType(); + /** + * Whether this type represents an anonymous structural type, e.g., { a: number, b: string }. + * Returns false for named structural types (i.e., types defined using @record). + */ + boolean isRecordType(); + /** * Whether the type is a scalar number. In OTI, the isNumber method returns true for Number * objects as well. @@ -161,4 +172,13 @@ public interface TypeI { TypeI getEnumeratedTypeOfEnumElement(); boolean isEnumObject(); + + /** + * Returns true if this type is a generic object (non function) and some (or all) of its type + * variables are not instantiated. + * Note that in OTI it is possible to instantiate only some of the type variables of a + * generic type (bad implementation choice). + * In NTI, a generic type can only be uninstantiated or fully instantiated. + */ + boolean hasUninstantiatedTypeVariables(); } diff --git a/src/com/google/javascript/rhino/TypeIEnv.java b/src/com/google/javascript/rhino/TypeIEnv.java new file mode 100644 index 00000000000..329ce2a2417 --- /dev/null +++ b/src/com/google/javascript/rhino/TypeIEnv.java @@ -0,0 +1,57 @@ +/* + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dimitris Vardoulakis + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package com.google.javascript.rhino; + +/** + * A map from type names to types. + * In OTI, it is implemented by jscomp#TypedScope. + * In NTI, it will probably be implemented by NTIScope. + * TODO(dimvar): Remove the generic when OTI is gone. + */ +public interface TypeIEnv { + /** + * Returns the type represented by typeName + */ + T getType(String typeName); + + /** + * Returns the jsdoc at the definition site of the type represented by typeName. + */ + JSDocInfo getJsdocOfTypeDeclaration(String typeName); +} diff --git a/src/com/google/javascript/rhino/TypeIRegistry.java b/src/com/google/javascript/rhino/TypeIRegistry.java index 9ae0b73f971..b54f9820cea 100644 --- a/src/com/google/javascript/rhino/TypeIRegistry.java +++ b/src/com/google/javascript/rhino/TypeIRegistry.java @@ -39,8 +39,10 @@ package com.google.javascript.rhino; +import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.jstype.JSTypeNative; import java.util.List; +import java.util.Map; /** * @author blickly@google.com (Ben Lickly) @@ -51,6 +53,10 @@ public interface TypeIRegistry { // in compiler passes mixed with the old type system. // Polymorphism avoids the need for casting in many cases (fewer casts in java 8 than in java 7). // After all non-type-checking passes use TypeI, we should make these methods not polymorphic. + // + // We considered defining the interface as TypeIRegistry, but it would only + // help with getNativeType and getType, not with the other methods, and it would increase + // verbosity in all places that use the interface. TypeI createTypeFromCommentNode(Node n); @@ -66,5 +72,16 @@ public interface TypeIRegistry { TypeI createUnionType(List variants); + /** + * Creates an anonymous structural type with the given properties. + */ + TypeI createRecordType(Map props); + + TypeI instantiateGenericType(ObjectTypeI genericType, ImmutableList typeArgs); + TypeI evaluateTypeExpressionInGlobalScope(JSTypeExpression expr); + + TypeI evaluateTypeExpression(JSTypeExpression expr, TypeIEnv typeEnv); + + TypeI buildRecordTypeFromObject(ObjectTypeI obj); } diff --git a/src/com/google/javascript/rhino/jstype/JSType.java b/src/com/google/javascript/rhino/jstype/JSType.java index 8230d060222..de32212701a 100644 --- a/src/com/google/javascript/rhino/jstype/JSType.java +++ b/src/com/google/javascript/rhino/jstype/JSType.java @@ -291,6 +291,11 @@ public final boolean isUnionType() { return toMaybeUnionType() != null; } + @Override + public boolean hasUninstantiatedTypeVariables() { + return getTemplateTypeMap().hasUnfilledTemplateKeys(); + } + @Override public boolean containsArray() { // Check if this is itself an array @@ -474,6 +479,7 @@ public NamedType toMaybeNamedType() { return null; } + @Override public boolean isRecordType() { return toMaybeRecordType() != null; } @@ -499,7 +505,8 @@ public final boolean isTemplatizedType() { return toMaybeTemplatizedType() != null; } - public final boolean isGeneric() { + @Override + public final boolean isGenericObjectType() { return isTemplatizedType(); } diff --git a/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java b/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java index 8309030a4ab..f628645613b 100644 --- a/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java +++ b/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java @@ -48,6 +48,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; @@ -59,8 +60,8 @@ import com.google.javascript.rhino.SimpleErrorReporter; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TypeI; +import com.google.javascript.rhino.TypeIEnv; import com.google.javascript.rhino.TypeIRegistry; -import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -70,6 +71,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -1097,11 +1099,13 @@ public void removeType(String jsTypeName) { } /** - * Looks up a type by name. + * Looks up a native type by name. * * @param jsTypeName The name string. * @return the corresponding JSType object or {@code null} it cannot be found */ + // Unchecked conversion of the return type, from JSType to TypeI. + @SuppressWarnings("unchecked") @Override public JSType getType(String jsTypeName) { // TODO(user): Push every local type name out of namesToTypes so that @@ -1113,16 +1117,19 @@ public JSType getType(String jsTypeName) { return namesToTypes.get(jsTypeName); } + @SuppressWarnings("unchecked") @Override public JSType getNativeType(JSTypeNative typeId) { return nativeTypes[typeId.ordinal()]; } + @SuppressWarnings("unchecked") @Override public ObjectType getNativeObjectType(JSTypeNative typeId) { return (ObjectType) getNativeType(typeId); } + @SuppressWarnings("unchecked") @Override public FunctionType getNativeFunctionType(JSTypeNative typeId) { return (FunctionType) getNativeType(typeId); @@ -1486,11 +1493,37 @@ private FunctionType createNativeFunctionType( .build(); } - /** - * Creates a record type. - */ - public RecordType createRecordType(Map properties) { - return new RecordType(this, properties); + @Override + public JSType buildRecordTypeFromObject(ObjectTypeI obj) { + ObjectType objType = (ObjectType) obj; + RecordType recType = objType.toMaybeRecordType(); + // If it can be casted to a record type then return + if (recType != null) { + return recType; + } + // TODO(lpino): Handle inherited properties + Set propNames = objType.getOwnPropertyNames(); + // If the type has no properties then return Object + if (propNames.isEmpty()) { + return getNativeType(JSTypeNative.OBJECT_TYPE); + } + ImmutableMap.Builder props = new ImmutableMap.Builder<>(); + // Otherwise collect the properties and build a record type + for (String propName : propNames) { + props.put(propName, objType.getPropertyType(propName)); + } + return createRecordType(props.build()); + } + + @Override + public JSType createRecordType(Map props) { + @SuppressWarnings("unchecked") + Map propMap = (Map) props; + RecordTypeBuilder builder = new RecordTypeBuilder(this); + for (Entry e : propMap.entrySet()) { + builder.addProperty(e.getKey(), e.getValue(), null); + } + return builder.build(); } /** @@ -1620,6 +1653,13 @@ public ObjectTypeI instantiateGenericsWithUnknown(ObjectType obj) { return obj; } + @SuppressWarnings("unchecked") + @Override + public TypeI instantiateGenericType( + ObjectTypeI genericType, ImmutableList typeArgs) { + return createTemplatizedType((ObjectType) genericType, (ImmutableList) typeArgs); + } + /** * Creates a templatized instance of the specified type. Only ObjectTypes * can currently be templatized; extend the logic in this function when @@ -1664,10 +1704,8 @@ public TemplatizedType createTemplatizedType( * @param templatizedTypes a list of the template JSTypes. Will be matched by * list order to the template keys on the base type. */ - public TemplatizedType createTemplatizedType( - ObjectType baseType, JSType... templatizedTypes) { - return createTemplatizedType( - baseType, ImmutableList.copyOf(templatizedTypes)); + public TemplatizedType createTemplatizedType(ObjectType baseType, JSType... templatizedTypes) { + return createTemplatizedType(baseType, ImmutableList.copyOf(templatizedTypes)); } /** @@ -1691,6 +1729,13 @@ public void identifyNonNullableName(String name) { nonNullableTypeNames.add(name); } + @SuppressWarnings("unchecked") + @Override + public JSType evaluateTypeExpression(JSTypeExpression expr, TypeIEnv scope) { + return createTypeFromCommentNode( + expr.getRoot(), expr.getSourceName(), (StaticTypedScope) scope); + } + @Override public JSType createTypeFromCommentNode(Node n) { return createTypeFromCommentNode(n, "[internal]", null); @@ -1702,6 +1747,7 @@ public JSType createTypeFromCommentNode(Node n) { * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ + @SuppressWarnings("unchecked") public JSType createTypeFromCommentNode( Node n, String sourceName, StaticTypedScope scope) { return createFromTypeNodesInternal(n, sourceName, (StaticTypedScope) scope, true); @@ -1981,11 +2027,4 @@ public void setTemplateTypeNames(List keys) { public void clearTemplateTypeNames() { templateTypes.clear(); } - - /** - * @return Whether the type can be provided type arguements. - */ - public boolean isTemplatizable(JSType type) { - return type.getTemplateTypeMap().hasUnfilledTemplateKeys(); - } } diff --git a/src/com/google/javascript/rhino/jstype/ObjectType.java b/src/com/google/javascript/rhino/jstype/ObjectType.java index 2749cb2cf6c..74ea1de8526 100644 --- a/src/com/google/javascript/rhino/jstype/ObjectType.java +++ b/src/com/google/javascript/rhino/jstype/ObjectType.java @@ -493,6 +493,7 @@ public JSType findPropertyType(String propertyName) { * @return the property's type or {@link UnknownType}. This method never * returns {@code null}. */ + @Override public JSType getPropertyType(String propertyName) { StaticTypedSlot slot = getSlot(propertyName); if (slot == null) { diff --git a/src/com/google/javascript/rhino/jstype/StaticTypedScope.java b/src/com/google/javascript/rhino/jstype/StaticTypedScope.java index b70132f8ee5..d5f5c884234 100644 --- a/src/com/google/javascript/rhino/jstype/StaticTypedScope.java +++ b/src/com/google/javascript/rhino/jstype/StaticTypedScope.java @@ -51,6 +51,7 @@ */ public interface StaticTypedScope extends StaticScope { /** Returns the scope enclosing this one or null if none. */ + @Override StaticTypedScope getParentScope(); /** @@ -61,9 +62,11 @@ public interface StaticTypedScope extends StaticScope { * @return The defined slot for the variable, or {@code null} if no * definition exists. */ + @Override StaticTypedSlot getSlot(String name); /** Like {@code getSlot} but does not recurse into parent scopes. */ + @Override StaticTypedSlot getOwnSlot(String name); /** Returns the expected type of {@code this} in the current scope. */ diff --git a/test/com/google/javascript/jscomp/TypeTransformationTest.java b/test/com/google/javascript/jscomp/TypeTransformationTest.java index 51406787538..ccba114a9bf 100644 --- a/test/com/google/javascript/jscomp/TypeTransformationTest.java +++ b/test/com/google/javascript/jscomp/TypeTransformationTest.java @@ -23,7 +23,6 @@ import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.RecordTypeBuilder; import com.google.javascript.rhino.testing.TestErrorReporter; - import java.util.Map.Entry; public final class TypeTransformationTest extends CompilerTypeTestCase { @@ -960,7 +959,8 @@ private void testTTL(JSType expectedType, String ttlExp, // Evaluate the type transformation TypeTransformation typeTransformation = new TypeTransformation(compiler, scope); - JSType resultType = typeTransformation.eval(ast, typeVars, nameVars); + @SuppressWarnings({"rawtypes", "unchecked"}) + JSType resultType = (JSType) typeTransformation.eval(ast, (ImmutableMap) typeVars, nameVars); checkReportedWarningsHelper(expectedWarnings); assertTypeEquals(expectedType, resultType); } diff --git a/test/com/google/javascript/rhino/jstype/JSTypeTest.java b/test/com/google/javascript/rhino/jstype/JSTypeTest.java index 882644b2764..a752acd894c 100644 --- a/test/com/google/javascript/rhino/jstype/JSTypeTest.java +++ b/test/com/google/javascript/rhino/jstype/JSTypeTest.java @@ -52,16 +52,13 @@ import com.google.javascript.rhino.SimpleErrorReporter; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType.TypePair; -import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; import com.google.javascript.rhino.testing.AbstractStaticScope; import com.google.javascript.rhino.testing.Asserts; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; import com.google.javascript.rhino.testing.MapBasedScope; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; // TODO(nicksantos): Split some of this up into per-class unit tests. public class JSTypeTest extends BaseJSTypeTestCase { @@ -5946,19 +5943,6 @@ private void testGetTypesUnderInequality( assertTypeEquals(t2Eq, p21.typeA); } - - /** - * Tests the factory method - * {@link JSTypeRegistry#createRecordType}. - */ - public void testCreateRecordType() throws Exception { - Map properties = new HashMap<>(); - properties.put("hello", new RecordProperty(NUMBER_TYPE, null)); - - JSType recordType = registry.createRecordType(properties); - assertEquals("{hello: number}", recordType.toString()); - } - /** * Tests the factory method {@link JSTypeRegistry#createOptionalType(JSType)}. */