private void putNewPropInPropertyMap(Map<String, JSType> props, String newPropName, JSType 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 // together // Three cases: // (i) If the key does not exist then add it to the map with the new value // (ii) If the key to be added already exists in the map and the new value // is not a record type then the current value is replaced with the new one // (iii) If the new value is a record type and the current is not then // the current value is replaced with the new one if (!props.containsKey(newPropName) || !newPropValue.isRecordType() || !props.get(newPropName).isRecordType()) { props.put(newPropName, newPropValue); return; } // Otherwise join the current value with the new one since both are records props.put(newPropName, joinRecordTypes(ImmutableList.of( (ObjectType) props.get(newPropName), (ObjectType) newPropValue))); }
private void putNewPropInPropertyMap(Map<String, JSType> props, String newPropName, JSType 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 // together // Three cases: // (i) If the key does not exist then add it to the map with the new value // (ii) If the key to be added already exists in the map and the new value // is not a record type then the current value is replaced with the new one // (iii) If the new value is a record type and the current is not then // the current value is replaced with the new one if (!props.containsKey(newPropName) || !newPropValue.isRecordType() || !props.get(newPropName).isRecordType()) { props.put(newPropName, newPropValue); return; } // Otherwise join the current value with the new one since both are records props.put(newPropName, joinRecordTypes(ImmutableList.<RecordType>of( (RecordType) props.get(newPropName), (RecordType) newPropValue))); }
private static boolean isObjectLiteralThatCanBeSkipped(JSType t) { t = t.restrictByNotNullOrUndefined(); return t.isRecordType() || t.isLiteralObject(); }
private static String toTypeString(JSType jsType) { String type = jsType.toString(); if (jsType.isFunctionType()) { return "Function /* " + type + " */"; } else if (jsType.isRecordType()) { return "Object /* " + type + " */"; } else if (type.equals("None")) { return "* /* " + type + " */"; } else { if (type.indexOf("Array<") == 0) { return "Array"; } else if (type.indexOf("Object<") == 0) { return "Object"; } } return type; }
private static String toTypeString(JSType jsType) { String type = jsType.toString(); if (jsType.isFunctionType()) { return "Function /* " + type + " */"; } else if (jsType.isRecordType()) { return "Object /* " + type + " */"; } else if (type.equals("None")) { return "* /* " + type + " */"; } else { if (type.indexOf("Array<") == 0) { return "Array"; } else if (type.indexOf("Object<") == 0) { return "Object"; } } return type; }
private static boolean isInstanceOfObject(JSType type) { // Some type whose class is Object ObjectType obj = type.toMaybeObjectType(); if (obj != null && obj.isNativeObjectType() && "Object".equals(obj.getReferenceName())) { return true; } return type.isRecordType() || type.isLiteralObject(); }
/** * Whether the typedef should be emitted by name or by the type it is defining. * * <p>Because, we do not access the original type signature, the replacement is done for all * references of the type (through the typedef or direct). Thus it is undesirable to always emit * by name. For example: * * <pre> * \@constructor A; * \@typedef {A} B; * \@const {A} var a; * </pre> * * If we emit the name, we would emit `var a: B;`, which is undesirable (consider A being string). * * <p>For now, only emit by name typedefs that are unlikely to have more than one name referring * to them - record types, templatized types and function types. */ private boolean shouldEmitTypedefByName(JSType realType) { return realType.isRecordType() || realType.isTemplatizedType() || realType.isFunctionType(); }
else if (jsType.isRecordType())
public final String toNonNullAnnotationString() { return !isUnknownType() && !isTemplateType() && !isRecordType() && isObject() ? "!" + toAnnotationString() : toAnnotationString(); }
final StringBuilder appendAsNonNull(StringBuilder sb, boolean forAnnotations) { if (forAnnotations && isObject() && !isUnknownType() && !isTemplateType() && !isRecordType() && !isFunctionType() && !isUnionType() && !isLiteralObject()) { sb.append("!"); } return appendTo(sb, forAnnotations); }
@Override public void matchConstraint(JSType constraint) { // We only want to match constraints on anonymous types. if (hasReferenceName()) { return; } // Handle the case where the constraint object is a record type. // // param constraint {{prop: (number|undefined)}} // function f(constraint) {} // f({}); // // We want to modify the object literal to match the constraint, by // taking any each property on the record and trying to match // properties on this object. if (constraint.isRecordType()) { matchRecordTypeConstraint(constraint.toObjectType()); } else if (constraint.isUnionType()) { for (JSType alt : constraint.toMaybeUnionType().getAlternates()) { if (alt.isRecordType()) { matchRecordTypeConstraint(alt.toObjectType()); } } } }
@Override public void matchConstraint(JSType constraint) { // We only want to match constraints on anonymous types. if (hasReferenceName()) { return; } // Handle the case where the constraint object is a record type. // // param constraint {{prop: (number|undefined)}} // function f(constraint) {} // f({}); // // We want to modify the object literal to match the constraint, by // taking any each property on the record and trying to match // properties on this object. if (constraint.isRecordType()) { matchRecordTypeConstraint(constraint.toObjectType()); } else if (constraint.isUnionType()) { for (JSType alt : constraint.toMaybeUnionType().getAlternates()) { if (alt.isRecordType()) { matchRecordTypeConstraint(alt.toObjectType()); } } } }
@Override protected boolean isSubtype(JSType that, ImplCache implicitImplCache, SubtypingMode subtypingMode) { if (JSType.isSubtypeHelper(this, that, implicitImplCache, subtypingMode)) { return true; } // Top of the record types is the empty record, or OBJECT_TYPE. if (registry.getNativeObjectType( JSTypeNative.OBJECT_TYPE).isSubtype(that, implicitImplCache, subtypingMode)) { return true; } // A type is a subtype of a record type if it itself is a record // type and it has at least the same members as the parent record type // with the same types. if (!that.isRecordType()) { return false; } return this.isStructuralSubtype(that.toMaybeRecordType(), implicitImplCache, subtypingMode); } }
@Override protected boolean isSubtype(JSType that, ImplCache implicitImplCache) { if (JSType.isSubtypeHelper(this, that, implicitImplCache)) { return true; } // Top of the record types is the empty record, or OBJECT_TYPE. if (registry.getNativeObjectType( JSTypeNative.OBJECT_TYPE).isSubtype(that, implicitImplCache)) { return true; } // A type is a subtype of a record type if it itself is a record // type and it has at least the same members as the parent record type // with the same types. if (!that.isRecordType()) { return false; } return this.isStructuralSubtype(that.toMaybeRecordType(), implicitImplCache); } }
private boolean evalTypePredicate(Node ttlAst, NameResolver nameResolver) { JSType[] params = evalTypeParams(ttlAst, nameResolver); String name = getCallName(ttlAst); Keywords keyword = nameToKeyword(name); switch (keyword) { case EQ: return params[0].isEquivalentTo(params[1]); case SUB: return params[0].isSubtype(params[1]); case ISCTOR: return params[0].isConstructor(); case ISTEMPLATIZED: return params[0].isTemplatizedType(); case ISRECORD: return params[0].isRecordType(); case ISUNKNOWN: return params[0].isUnknownType() || params[0].isCheckedUnknownType(); default: throw new IllegalStateException( "Invalid type predicate in the type transformation"); } }
private boolean evalTypePredicate(Node ttlAst, NameResolver nameResolver) { JSType[] params = evalTypeParams(ttlAst, nameResolver); String name = getCallName(ttlAst); Keywords keyword = nameToKeyword(name); JSType type = params[0]; switch (keyword) { case EQ: return type.isEquivalentTo(params[1]); case SUB: return type.isSubtypeOf(params[1]); case ISCTOR: return type.isConstructor(); case ISTEMPLATIZED: return type.isObjectType() && type.toMaybeObjectType().isGenericObjectType() && type.isPartiallyInstantiated(); case ISRECORD: return type.isRecordType(); case ISUNKNOWN: return type.isUnknownType(); default: throw new IllegalStateException( "Invalid type predicate in the type transformation"); } }
private JSType lookupQualifiedName(Node n) { String name = n.getQualifiedName(); TypedVar slot = scope.getVar(name); if (slot != null && !slot.isTypeInferred()) { JSType type = slot.getType(); if (type != null && !type.isUnknownType()) { return type; } } else if (n.isGetProp()) { JSType type = lookupQualifiedName(n.getFirstChild()); if (type != null && type.isRecordType()) { JSType propType = type.findPropertyType( n.getLastChild().getString()); return propType; } } return null; }
Boolean castCastToHelper(JSType thisType, JSType thatType) { if (thatType.isUnknownType() || thatType.isAllType() || thatType.isNoObjectType() // TODO(johnlenz): restrict to objects || thatType.isNoType()) { return true; } else if (thisType.isRecordType() || thatType.isRecordType()) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (isInterface(thisType) || isInterface(thatType)) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (thatType.isEnumElementType()) { return thisType.visit(this, thatType.toMaybeEnumElementType().getPrimitiveType()); } else if (thatType.isUnionType()) { return canCastToUnion(thisType, thatType.toMaybeUnionType()); } else if (thatType.isFunctionType()) { return canCastToFunction(thisType, thatType.toMaybeFunctionType()); } else if (thatType.isTemplatizedType()) { // TODO(johnlenz): once the templated type work is finished, // restrict the type parameters. return thisType.visit(this, thatType.toMaybeTemplatizedType().getReferencedTypeInternal()); } return thisType.isSubtypeOf(thatType) || thatType.isSubtypeOf(thisType); }
Boolean castCastToHelper(JSType thisType, JSType thatType) { if (thatType.isUnknownType() || thatType.isAllType() || thatType.isNoObjectType() // TODO(johnlenz): restrict to objects || thatType.isNoType()) { return true; } else if (thisType.isRecordType() || thatType.isRecordType()) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (isInterface(thisType) || isInterface(thatType)) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (thatType.isEnumElementType()) { return thisType.visit(this, thatType.toMaybeEnumElementType().getPrimitiveType()); } else if (thatType.isUnionType()) { return canCastToUnion(thisType, thatType.toMaybeUnionType()); } else if (thatType.isFunctionType()) { return canCastToFunction(thisType, thatType.toMaybeFunctionType()); } else if (thatType.isTemplatizedType()) { // TODO(johnlenz): once the templated type work is finished, // restrict the type parameters. return thisType.visit(this, thatType.toMaybeTemplatizedType().getReferencedTypeInternal()); } return thisType.isSubtype(thatType) || thatType.isSubtype(thisType); }
@Test public void test_types() throws IOException { compile("types_param.js"); ClassReference reference = model.getClassReference("Foo"); JSType jsType1 = getJSType("test1", true, "arg1"); JSType jsType2 = getJSType("test2", true, "arg1"); JSType jsType3 = getJSType("test3", true, "arg1"); JSType jsType4 = getJSType("test4", true, "arg1"); JSType jsType5 = getJSType("test5", true, "arg1"); JSType jsType6 = getJSType("test6", true, "arg1"); assertTrue(jsType1.isString()); assertTrue(jsType2.isUnionType()); assertTrue(jsType3.isRecordType()); assertTrue(jsType4.isUnionType()); assertTrue(jsType5.isInstanceType()); assertTrue(jsType6.isFunctionType()); assertEquals("String", JSTypeUtils.toParamTypeString(reference.getStaticMethod("test1"), "arg1")); assertEquals("foo.bar.Baz", JSTypeUtils.toParamTypeString(reference.getStaticMethod("test2"), "arg1")); assertEquals("Object /* {myNum: number, myObject: ?} */", JSTypeUtils.toParamTypeString(reference.getStaticMethod("test3"), "arg1")); assertEquals("Number", JSTypeUtils.toParamTypeString(reference.getStaticMethod("test4"), "arg1")); assertEquals("Object", JSTypeUtils.toParamTypeString(reference.getStaticMethod("test5"), "arg1")); assertEquals("Function /* function(string, boolean): ? */", JSTypeUtils.toParamTypeString(reference.getStaticMethod("test6"), "arg1")); }