private Map<ClassElement, String> makeBuiltinTypes(CoreTypeProvider typeProvider) { Map<ClassElement, String> builtinTypes = Maps.newHashMap(); builtinTypes.put(typeProvider.getBoolType().getElement(), "$isBool"); builtinTypes.put(typeProvider.getIntType().getElement(), "$isNum"); builtinTypes.put(typeProvider.getDoubleType().getElement(), "$isNum"); builtinTypes.put(typeProvider.getStringType().getElement(), "$isString"); return builtinTypes; }
private boolean isSubtypeOfInterface(Type t, InterfaceType s) { // Special handling for union. if (t instanceof InterfaceTypeUnion) { InterfaceTypeUnion tUnion = (InterfaceTypeUnion) t; for (InterfaceType unionPart : tUnion.getTypes()) { if (isSubtype(unionPart, s)) { return true; } } return false; } // class "t" implements call() and "s" is Function if (TypeKind.of(t) == TypeKind.INTERFACE && typeProvider != null && s == typeProvider.getFunctionType()) { InterfaceType ti = (InterfaceType) t; if (ti.lookupMember("call") != null) { return true; } } // Try to cast "t" to "s". final Type sup = asInstanceOf(t, s.getElement()); if (TypeKind.of(sup) == TypeKind.INTERFACE) { InterfaceType ti = (InterfaceType) sup; assert ti.getElement().equals(s.getElement()); if (ti.isRaw() || s.isRaw()) { return true; } // Type arguments are covariant. return areSubtypes(ti.getArguments().iterator(), s.getArguments().iterator()); } return false; }
private JsExpression generateRawInterfaceTypeComparison( JsExpression lhs, DartTypeNode typeNode, SourceInfo src) { ClassElement element = (ClassElement) typeNode.getType().getElement(); if (element.equals(typeProvider.getObjectType().getElement())) { // Everything is an object, including null return this.translationContext.getProgram().getTrueLiteral(); } String builtin = builtInTypeChecks.get(element); if (builtin != null) { return call(src, nameref(src, builtin), lhs); } // Due to implementing implied interfaces of classes, we always have to // use $implements$ rather than using JS instanceof for classes. // Inject: !!(tmp = target, tmp && tmp.$implements$type) JsProgram program = translationContext.getProgram(); JsName tmp = context.createTemporary(); String mangledClass = translationContext.getMangler().mangleClassName(element); return not( src, not( src, comma( src, assign(src, tmp.makeRef(), lhs), and( src, neq(src, tmp.makeRef().setSourceRef(src), program.getNullLiteral()), nameref(src, tmp, "$implements$" + mangledClass))))); }
/** Add runtime type information to a "new" expression, if necessary. */ void mayAddRuntimeTypeToConstrutorOrFactoryCall( ClassElement enclosingClass, DartNewExpression x, JsInvocation invoke) { // TODO(johnlenz):in optimized mode, only add this where it is needed. InterfaceType instanceType = Types.constructorType(x); if (instanceType == null) { // TODO(johnlenz): HackHack. Currently the "new FallThroughError" injected by the // Normalizer does not have the instance type attached. But in this // case we know it does not have any type parameters. // reportError(x.getParent().getParent(), new AssertionError("missing type information")); assert typeProvider .getFallThroughError() .getElement() .lookupConstructor("") .equals(x.getSymbol()); } else if (constructorHasTypeParameters(x)) { ConstructorElement constructor = x.getSymbol(); ClassElement containingClassElement = enclosingClass; if (constructor.getModifiers().isFactory()) { // We are calling a factory, this is either in a class FunctionType functionType = (FunctionType) constructor.getType(); JsExpression typeArgs = generateTypeArgsArrayForFactory(functionType, instanceType, containingClassElement); assert typeArgs != null; invoke.getArguments().add(0, typeArgs); } else { ClassElement constructorClassElement = constructor.getConstructorType(); invoke .getArguments() .add( 0, generateRTTLookup(constructorClassElement, instanceType, containingClassElement)); } } }
/** Add a runtime type information to a array literal, if necessary. */ JsExpression maybeAddRuntimeTypeForArrayLiteral( ClassElement enclosingClass, DartArrayLiteral x, JsArrayLiteral jsArray) { // TODO(johnlenz):in optimized mode, only add this where it is needed. InterfaceType instanceType = typeProvider.getArrayLiteralType(x.getType().getArguments().get(0)); JsExpression rtt = generateRTTLookup(instanceType, enclosingClass); // Bind the runtime type information to the native array expression return call(x, newQualifiedNameRef("RTT.setTypeInfo"), jsArray, rtt); }
/** Add a runtime type information to a map literal, if necessary. */ void maybeAddRuntimeTypeToMapLiteralConstructor( ClassElement enclosingClass, DartMapLiteral x, JsInvocation invoke) { // TODO(johnlenz):in optimized mode, only add this where it is needed. // Fixup the type for map literal type to be the implementing type. List<? extends Type> typeArgs = x.getType().getArguments(); InterfaceType instanceType = typeProvider.getMapLiteralType(typeArgs.get(0), typeArgs.get(1)); JsExpression rtt = generateRTTLookup(instanceType, enclosingClass); invoke.getArguments().add(rtt); }
/** * Return an interface type representing the given interface, function or variable type. * * @return An interface type or null if the argument is neither an interface function or variable * type. */ public InterfaceType getInterfaceType(Type type) { switch (TypeKind.of(type)) { case VARIABLE: { TypeVariableElement element = ((TypeVariable) type).getTypeVariableElement(); if (element.getBound() == null) { return typeProvider.getObjectType(); } else { return getInterfaceType(element.getBound()); } } case FUNCTION: case FUNCTION_ALIAS: return typeProvider.getFunctionType(); case INTERFACE: return (InterfaceType) type; case DYNAMIC: case NONE: case VOID: default: return null; } }
public Type leastUpperBound(Type t, Type s) { if (isSubtype(t, s)) { return s; } else if (isSubtype(s, t)) { return t; } else { List<InterfaceType> tTypes = getSuperTypes(t); List<InterfaceType> sTypes = getSuperTypes(s); for (InterfaceType tType : tTypes) { if (sTypes.contains(tType)) { return tType; } } return typeProvider.getObjectType(); } }
public Type intersection(List<Type> types) { // exclude 'dynamic' type { List<Type> newTypes = Lists.newArrayList(); for (Type type : types) { if (TypeKind.of(type) != TypeKind.DYNAMIC) { newTypes.add(type); } } types = newTypes; } // no types, so Dynamic if (types.isEmpty()) { return typeProvider.getDynamicType(); } // prepare all super types List<List<InterfaceType>> superTypesLists = Lists.newArrayList(); List<Map<InterfaceType, InterfaceType>> superTypesMaps = Lists.newArrayList(); for (Type type : types) { List<InterfaceType> superTypes = getSuperTypes(type); superTypesLists.add(superTypes); Map<InterfaceType, InterfaceType> superTypesMap = Maps.newHashMap(); for (InterfaceType superType : superTypes) { superTypesMap.put(superType.asRawType(), superType); } superTypesMaps.add(superTypesMap); } // find intersection of super types LinkedList<InterfaceType> interTypes = Lists.newLinkedList(); if (superTypesLists.size() > 0) { for (InterfaceType superType : superTypesLists.get(0)) { boolean inAll = true; for (Map<InterfaceType, InterfaceType> otherTypesMap : superTypesMaps) { InterfaceType superTypeRaw = superType.asRawType(); InterfaceType otherType = otherTypesMap.get(superTypeRaw); // no such raw type, exclude from intersection if (otherType == null) { inAll = false; break; } // if not raw, choose type arguments if (!superType.getArguments().isEmpty()) { InterfaceType t0 = superType; InterfaceType t1 = otherType; // if two-way sub-type, then has Dynamic(s), choose with least number if (isSubtype(t0, t1) && isSubtype(t1, t0)) { int dynamics0 = getDynamicArgumentsCount(t0); int dynamics1 = getDynamicArgumentsCount(t1); if (dynamics0 < dynamics1) { superType = t0; } else { superType = t1; } continue; } // use super-type of t0 and t1 if (isSubtype(t0, t1)) { superType = t1; } if (isSubtype(t1, t0)) { superType = t0; } } } if (inAll && !interTypes.contains(superType)) { interTypes.add(superType); } } } // try to remove sub-types already covered by existing types for (Iterator<InterfaceType> i = interTypes.descendingIterator(); i.hasNext(); ) { InterfaceType subType = i.next(); boolean hasSuperType = false; for (InterfaceType superType : interTypes) { if (superType != subType && isSubtype(superType, subType)) { hasSuperType = true; break; } } if (hasSuperType) { i.remove(); } } // use single type if (interTypes.size() == 0) { return typeProvider.getObjectType(); } if (interTypes.size() == 1) { return interTypes.get(0); } // create union return unionTypes(interTypes); }
private InterfaceType checkedAsInstanceOf( Type t, ClassElement element, Set<TypeVariable> variablesReferenced, Set<Type> checkedTypes) { // check for recursion if (checkedTypes.contains(t)) { return null; } checkedTypes.add(t); // check current Type switch (TypeKind.of(t)) { case FUNCTION_ALIAS: case INTERFACE: { if (t.getElement().equals(element)) { return (InterfaceType) t; } InterfaceType ti = (InterfaceType) t; ClassElement tElement = ti.getElement(); InterfaceType supertype = tElement.getSupertype(); // super type if (supertype != null) { InterfaceType result = checkedAsInstanceOf( asSupertype(ti, supertype), element, variablesReferenced, checkedTypes); if (result != null) { return result; } } // interfaces for (InterfaceType intf : tElement.getInterfaces()) { InterfaceType result = checkedAsInstanceOf( asSupertype(ti, intf), element, variablesReferenced, checkedTypes); if (result != null) { return result; } } // mixins for (InterfaceType mixin : tElement.getMixins()) { if (mixin.getElement().equals(element)) { return asSupertype(ti, mixin); } } // no return null; } case FUNCTION: { Element e = t.getElement(); switch (e.getKind()) { case CLASS: // e should be the interface Function in the core library. See the // documentation comment on FunctionType. InterfaceType ti = (InterfaceType) e.getType(); return checkedAsInstanceOf(ti, element, variablesReferenced, checkedTypes); default: return null; } } case VARIABLE: { TypeVariable v = (TypeVariable) t; Type bound = v.getTypeVariableElement().getBound(); // Check for previously encountered variables to avoid getting stuck in an infinite loop. if (variablesReferenced.contains(v)) { if (bound instanceof InterfaceType) { return (InterfaceType) bound; } return typeProvider.getObjectType(); } variablesReferenced.add(v); return checkedAsInstanceOf(bound, element, variablesReferenced, checkedTypes); } default: return null; } }
/** * Implement the Dart function subtype rule. Unlike the classic arrow rule (return type is * covariant, and parameter types are contravariant), in Dart they must just be assignable. */ private boolean isSubtypeOfFunction(FunctionType t, FunctionType s) { if (s.getKind() == TypeKind.DYNAMIC || t.getKind() == TypeKind.DYNAMIC) { return true; } // Classic: return type is covariant; Dart: assignable. if (!isAssignable(t.getReturnType(), s.getReturnType())) { // A function that returns a value can be used as a function where you ignore the value. if (!s.getReturnType().equals(typeProvider.getVoidType())) { return false; } } Type tRest = t.getRest(); Type sRest = s.getRest(); if ((tRest == null) != (sRest == null)) { return false; } if (tRest != null) { // Classic: parameter types are contravariant; Dart: assignable. if (!isAssignable(sRest, tRest)) { return false; } } { Map<String, Type> sOpti = s.getOptionalParameterTypes(); Map<String, Type> tOpti = t.getOptionalParameterTypes(); if (tOpti.size() < sOpti.size()) { return false; } Iterator<Entry<String, Type>> tList = tOpti.entrySet().iterator(); Iterator<Entry<String, Type>> sList = sOpti.entrySet().iterator(); while (sList.hasNext()) { if (!tList.hasNext()) { return false; } Entry<String, Type> sEntry = sList.next(); Entry<String, Type> tEntry = tList.next(); if (!isAssignable(tEntry.getValue(), sEntry.getValue())) { return false; } } } Map<String, Type> tNamed = t.getNamedParameterTypes(); Map<String, Type> sNamed = s.getNamedParameterTypes(); if (tNamed.isEmpty() && !sNamed.isEmpty()) { return false; } // T's named parameters must be in the same order and assignable to S's but // maybe a superset. if (!sNamed.isEmpty()) { LinkedHashMap<String, Type> tMap = (LinkedHashMap<String, Type>) (tNamed); LinkedHashMap<String, Type> sMap = (LinkedHashMap<String, Type>) (sNamed); if (!tMap.keySet().containsAll(sMap.keySet())) { return false; } for (Entry<String, Type> entry : sMap.entrySet()) { String name = entry.getKey(); Type sType = sMap.get(name); Type tType = tMap.get(name); if (!isAssignable(tType, sType)) { return false; } } // Iterator<Entry<String, Type>> tList = tMap.entrySet().iterator(); // Iterator<Entry<String, Type>> sList = sMap.entrySet().iterator(); // // t named parameters must start with the named parameters of s // while (sList.hasNext()) { // if (!tList.hasNext()) { // return false; // } // Entry<String, Type> sEntry = sList.next(); // Entry<String, Type> tEntry = tList.next(); // if (!sEntry.getKey().equals(tEntry.getKey())) { // return false; // } // // Classic: parameter types are contravariant; Dart: assignable. // if (!isAssignable(tEntry.getValue(), sEntry.getValue())) { // return false; // } // } } // Classic: parameter types are contravariant; Dart: assignable. return areAssignable(s.getParameterTypes().iterator(), t.getParameterTypes().iterator()); }