/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** \* */ // $Id$ package scalac.transformer; import scalac.Global; import scalac.Phase; import scalac.PhaseDescriptor; import scalac.CompilationUnit; import scalac.symtab.Definitions; import scalac.symtab.Scope; import scalac.symtab.Symbol; import scalac.symtab.Type; import scalac.symtab.Modifiers; import scalac.atree.AConstant; import scalac.ast.Transformer; import scalac.ast.GenTransformer; import scalac.ast.Tree; import scalac.ast.TreeGen; import scalac.ast.TreeList; import scalac.backend.Primitives; import scalac.util.Name; import scalac.util.Names; import scalac.util.Debug; import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Arrays; import java.util.Collections; /** * Turn types into values by applying the following transformations: * * - For all type member T of all classes, add an accessor method * T$type returnining the type as a value (the accessor is abstract * if the type member is abstract). * * - For all polymorphic methods/constructors, add a value parameter * for each type parameter. * * - Add a method getType to every class, to obtain its type as a * value. * * - Transform all type expressions into value expressions: type * application is turned into value application, type selection into * value selection, and so on. * * @author Michel Schinz * @version 1.0 */ // TODO use a constant instead of generating empty arrays all the // time. public class TypesAsValuesPhase extends Phase { private final GenTransformer transformer; /** * The list of members to add to a given class (either type * accessors or instantiation methods). */ private final HashMap/**/ membersToAdd = new HashMap(); /** The list of parameters to add to a given method. */ private final HashMap/*>*/ paramsToAdd = new HashMap(); /** The accessor method corresponding to a given type (or class) member. */ private final HashMap/**/ typeAccessor = new HashMap(); /** The instanciation method corresponding to a given class. */ private final HashMap/**/ instantiator = new HashMap(); /** The class constructor corresponding to a given class. */ private final HashMap/**/ classInitialiser = new HashMap(); private final HashMap/**/ tConstructor = new HashMap(); private final Definitions defs = global.definitions; private final Primitives prims = global.primitives; private final Type.MethodType typeAccessorType = new Type.MethodType(new Symbol[]{}, defs.TYPE_TYPE()); private final Symbol ARRAY_CONSTRUCTOR = defs.ARRAY_CLASS.primaryConstructor(); private final TEnv EENV = new TEnv(); private final Map/**/ predefTypes; private HashMap/**/ ancestorCache = new HashMap(); public TypesAsValuesPhase(Global global, PhaseDescriptor descriptor) { super(global, descriptor); // If RTT are disabled, use a minimal transformer which simply // replaces [ia]sInstanceOf with their erased counterpart. if (global.runTimeTypes) transformer = new TV_Transformer(global); else transformer = new TV_MiniTransformer(global); predefTypes = new HashMap(); predefTypes.put(defs.DOUBLE_CLASS, defs.RTT_DOUBLE()); predefTypes.put(defs.FLOAT_CLASS, defs.RTT_FLOAT()); predefTypes.put(defs.LONG_CLASS, defs.RTT_LONG()); predefTypes.put(defs.INT_CLASS, defs.RTT_INT()); predefTypes.put(defs.SHORT_CLASS, defs.RTT_SHORT()); predefTypes.put(defs.CHAR_CLASS, defs.RTT_CHAR()); predefTypes.put(defs.BYTE_CLASS, defs.RTT_BYTE()); predefTypes.put(defs.BOOLEAN_CLASS, defs.RTT_BOOLEAN()); predefTypes.put(defs.UNIT_CLASS, defs.RTT_UNIT()); predefTypes.put(defs.ANY_CLASS, defs.RTT_ANY()); predefTypes.put(defs.ANYVAL_CLASS, defs.RTT_ANYVAL()); predefTypes.put(defs.ALLREF_CLASS, defs.RTT_ALLREF()); predefTypes.put(defs.ALL_CLASS, defs.RTT_ALL()); membersToAdd.put(defs.ARRAY_CLASS, new Symbol[0]); paramsToAdd.put(ARRAY_CONSTRUCTOR, new Symbol[0]); ancestorCache.put(defs.OBJECT_CLASS, new Ancestor[0][]); } /** * Return the symbol of the accessor for the given type symbol. */ private Symbol getAccessorSym(Symbol typeSym) { assert typeSym.isType(); Symbol accessorSym = (Symbol)typeAccessor.get(typeSym); if (accessorSym == null) { accessorSym = typeSym.owner().newVariable(typeSym.pos, typeSym.flags, Names.TYPE(typeSym)); accessorSym.setInfo(defs.TYPE_TYPE()); typeAccessor.put(typeSym, accessorSym); } return accessorSym; } private Symbol getInstMethSym(Symbol classSym) { Symbol imSym = (Symbol)instantiator.get(classSym); if (imSym == null) { int pos = classSym.pos; boolean isStatic = !isNestedClass(classSym); Name imName = Names.INSTANTIATE(classSym, isStatic); int flags = classSym.isAbstractType() ? Modifiers.DEFERRED : 0; imSym = isStatic ? classSym.newStaticMethod(pos, 0, imName) : classSym.owner().newMethodOrFunction(pos, flags, imName); // TODO special case for monomorphic instantiations Symbol[] argTypes; if (true || classSym.typeParams().length > 0) { Symbol typesP = imSym.newVParam(pos, 0, Name.fromString("types")); typesP.setInfo(defs.ARRAY_TYPE(defs.TYPE_TYPE())); argTypes = new Symbol[]{ typesP }; } else argTypes = Symbol.EMPTY_ARRAY; imSym.setInfo(new Type.MethodType(argTypes, isStatic ? defs.SCALACLASSTYPE_TYPE() : defs.TYPE_TYPE())); instantiator.put(classSym, imSym); } return imSym; } private Symbol getTConstructorSym(Symbol classSym) { Symbol tcSym = (Symbol)tConstructor.get(classSym); if (tcSym == null) { int pos = classSym.pos; boolean isStatic = !isNestedClass(classSym); Name tcName = Names.TYPECONSTRUCTOR(classSym, isStatic); tcSym = isStatic ? classSym.newStaticField(pos, 0, tcName) : classSym.owner().newFieldOrVariable(pos, 0, tcName); tcSym.setInfo(defs.TYPECONSTRUCTOR_TYPE()); tConstructor.put(classSym, tcSym); } return tcSym; } private Symbol getClassInitSym(Symbol classSym) { Symbol ciSym = (Symbol)classInitialiser.get(classSym); if (ciSym == null) { int pos = classSym.pos; ciSym = classSym.newStaticMethod(pos, 0, Names.CLASS_CONSTRUCTOR); ciSym.setInfo(new Type.MethodType(Symbol.EMPTY_ARRAY, defs.UNIT_TYPE())); classInitialiser.put(classSym, ciSym); } return ciSym; } private Symbol[] membersToAdd(Symbol classSym) { Symbol[] toAdd = (Symbol[])membersToAdd.get(classSym); if (toAdd == null) { HashSet seenMembers = new HashSet(); // [HACK] ArrayList toAddL = new ArrayList(); Scope.SymbolIterator membersIt = classSym.members().iterator(); while (membersIt.hasNext()) { Symbol member = membersIt.next(); // [HACK] work around a bug in the analyzer, which // doesn't add the module class to its owner's members if (member.isModule()) member = member.moduleClass(); if (!seenMembers.add(member)) continue; if (member.isClass()) { toAddL.add(getTConstructorSym(member)); toAddL.add(getInstMethSym(member)); } else if (member.isType()) toAddL.add(getInstMethSym(member)); } if (!isNestedClass(classSym)) { toAddL.add(getTConstructorSym(classSym)); toAddL.add(getClassInitSym(classSym)); toAddL.add(getInstMethSym(classSym)); } toAdd = (Symbol[])toAddL.toArray(new Symbol[toAddL.size()]); membersToAdd.put(classSym, toAdd); } return toAdd; } private Symbol[] paramsToAdd(Symbol methSym) { Symbol[] toAdd = (Symbol[])paramsToAdd.get(methSym); if (toAdd == null) { Symbol[] tparams = methSym.typeParams(); ArrayList toAddL = new ArrayList(); for (int i = 0; i < tparams.length; ++i) toAddL.add(getAccessorSym(tparams[i])); toAdd = (Symbol[])toAddL.toArray(new Symbol[toAddL.size()]); paramsToAdd.put(methSym, toAdd); } return toAdd; } /** * Return true iff the given symbol is not a polymorphic * primitive, which shouldn't get type parameters as value * parameters. */ private boolean monoPrimitive(Symbol sym) { return sym.isJava() || sym == ARRAY_CONSTRUCTOR || sym == defs.OBJECT_SYNCHRONIZED || sym == defs.ANY_IS || sym == defs.ANY_IS_ERASED || sym == defs.ANY_AS || sym == defs.ANY_AS_ERASED; } public Type transformInfo(Symbol symbol, Type type) { if (!global.runTimeTypes) return type; if (symbol.isClass()) { Symbol[] toAdd = membersToAdd(symbol); if (toAdd.length == 0) return type; else { Scope newMembers = new Scope(symbol.members()); for (int i = 0; i < toAdd.length; ++i) newMembers.enterOrOverload(toAdd[i]); return Type.compoundType(type.parents(), newMembers, symbol); } } else if (type.typeParams().length > 0 && !monoPrimitive(symbol)) { // Polymorphic method/constructor: // - add a value parameter for every type parameter. switch (type) { case PolyType(Symbol[] tparams, // : Type.MethodType(Symbol[] vparams, Type result)): List newVParams = new LinkedList(Arrays.asList(paramsToAdd(symbol))); newVParams.addAll(Arrays.asList(vparams)); Symbol[] newVParamsA = (Symbol[]) newVParams.toArray(new Symbol[newVParams.size()]); return new Type.PolyType(tparams, new Type.MethodType(newVParamsA, result)); default: throw Debug.abort("unexpected type: ", type); } } else return type; } private boolean isNestedClass(Symbol classSym) { return !classSym.owner().isPackageClass(); } public void apply(CompilationUnit unit) { transformer.apply(unit); } private class TV_Transformer extends TV_MiniTransformer { private Symbol currentOwner; public TV_Transformer(Global global) { super(global); } public Tree transform(Tree tree) { switch (tree) { case ClassDef(_, _, _, _, _, Tree.Template impl): Symbol clsSym = tree.symbol(); TreeList newBody = new TreeList(); if (!isNestedClass(clsSym)) { Symbol tcSym = getTConstructorSym(clsSym); newBody.append(tConstructorVal(clsSym, tcSym)); Symbol ciSym = getClassInitSym(clsSym); newBody.append(classInitialiser(clsSym, ciSym, tcSym)); Symbol imSym = getInstMethSym(clsSym); newBody.append(instantiatorBody(clsSym, imSym)); } newBody.append(transformStatements(impl.body, impl.symbol())); return gen.ClassDef(clsSym, transform(impl.parents, clsSym.primaryConstructor()), impl.symbol(), newBody.toArray()); case DefDef(_, _, _, _, _, Tree rhs): Symbol symbol = getSymbolFor(tree); if (symbol.name == Names.getType && symbol.isSynthetic()) { // Correct the body of the getType method which, // until now, was a placeholder (introduced by // RefCheck). return gen.DefDef(symbol, scalaClassType(symbol.pos, symbol.owner().type(), symbol, EENV)); } else return gen.DefDef(symbol, transform(rhs, symbol)); case Block(Tree[] stats, Tree value): return gen.Block(tree.pos, transformStatements(stats), transform(value)); case ValDef(_, _, Tree tpe, Literal(AConstant.ZERO)): // transform default values: // val x: T = _ // becomes // val x: T = asValue(T).defaultValue() Symbol symbol = getSymbolFor(tree); Tree defaultValue = gen.mkRef(tree.pos, typeAsValue(tree.pos, tpe.type, currentOwner, EENV), defs.TYPE_DEFAULTVALUE()); Tree rhs = gen.mkApply__(tree.pos, defaultValue); return gen.ValDef(symbol, rhs); case ValDef(_, _, _, Tree rhs): Symbol symbol = getSymbolFor(tree); return gen.ValDef(symbol, transform(rhs, symbol)); case New(Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs)): // TODO still needed? if (fun.symbol() == ARRAY_CONSTRUCTOR && false) { // Transform array creations: // new Array[T](size) // becomes // asValue(T).newArray[T](size) assert targs.length == 1; assert vargs.length == 1; Tree newArrayfun = gen.mkRef(tree.pos, typeAsValue(targs[0].pos, targs[0].type, currentOwner, EENV), defs.TYPE_NEWARRAY()); return gen.mkApplyTV(newArrayfun, targs, vargs); } else return super.transform(tree); case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs): Symbol funSym = fun.symbol(); if (funSym == defs.ANY_IS) { assert targs.length == 1 && vargs.length == 0; Type type = targs[0].type; Tree expr = transform(qualifierOf(fun)); return isTrivial(type) ? super.transform(tree) : genInstanceTest(tree.pos, expr, type); } else if (funSym == defs.ANY_AS) { // Transform instance tests: // e.asInstanceOf[T] // becomes: // asValue(T).cast(e).asInstanceOf[T] // unless T is a trivial type for which a Java // instance test is sufficient, in which case the // expression is left as is. assert targs.length == 1 && vargs.length == 0; Type type = targs[0].type; Tree expr = transform(qualifierOf(fun)); return isTrivial(type) ? super.transform(tree) : genTypeCast(tree.pos, expr, type); } else if (!monoPrimitive(funSym)) { // Transform applications to pass types as values: // f[T1, ...](v1, ...) // becomes // f[T1, ...](asValue(T1), ..., v1, ...) Tree[] newVArgs = transform(vargs); Tree[] finalVArgs = new Tree[newVArgs.length + targs.length]; for (int i = 0; i < targs.length; ++i) finalVArgs[i] = typeAsValue(targs[i].pos, targs[i].type, currentOwner, EENV); System.arraycopy(newVArgs, 0, finalVArgs, targs.length, newVArgs.length); return gen.mkApplyTV(tree.pos, transform(fun), targs, finalVArgs); } else return super.transform(tree); default: return super.transform(tree); } } private Tree[] transformStatements(Tree[] stats) { ArrayList newStats = new ArrayList(); int beginIdx = 0; for (int i = 0; i < stats.length; ++i) { Tree stat = stats[i]; switch (stat) { case ClassDef(_, _, _, _, _, Tree.Template impl): Symbol clsSym = stat.symbol(); Symbol tcSym = getTConstructorSym(clsSym); newStats.add(beginIdx++, tConstructorVal(clsSym, tcSym)); Symbol insSym = getInstMethSym(clsSym); newStats.add(instantiatorBody(clsSym, insSym)); break; case AbsTypeDef(_, _, _, _): case AliasTypeDef(_, _, _, _): Symbol tpSym = stat.symbol(); Symbol insSym = getInstMethSym(tpSym); newStats.add(typeAccessorBody(tpSym, insSym)); break; } newStats.add(transform(stat)); } return (Tree[])newStats.toArray(new Tree[newStats.size()]); } private Tree[] transformStatements(Tree[] stats, Symbol currentOwner) { Symbol bkpOwner = this.currentOwner; this.currentOwner = currentOwner; Tree[] newStats = transformStatements(stats); this.currentOwner = bkpOwner; return newStats; } private Tree transform(Tree tree, Symbol currentOwner) { Symbol bkpOwner = this.currentOwner; this.currentOwner = currentOwner; Tree newTree = transform(tree); this.currentOwner = bkpOwner; return newTree; } private Tree[] transform(Tree[] trees, Symbol currentOwner) { Symbol bkpOwner = this.currentOwner; this.currentOwner = currentOwner; Tree[] newTrees = transform(trees); this.currentOwner = bkpOwner; return newTrees; } private int level(Symbol sym) { Symbol superClass = sym.parents()[0].symbol(); assert superClass != Symbol.NONE : sym; if (superClass == defs.ANY_CLASS) return 0; else return 1 + level(superClass); } /** * Return a method giving access to the given type, as a * value. */ private Tree.DefDef typeAccessorBody(Symbol typSym, Symbol accSym) { Tree rhs; if (typSym.isAbstractType()) rhs = Tree.Empty; else if (typSym.isClass()) rhs = scalaClassType(typSym.pos, typSym.type(), accSym, EENV); else { final Symbol[] vparams = accSym.valueParams(); final int pos = accSym.pos; final HashMap varMap = new HashMap(); Symbol[] tparams = typSym.typeParams(); for (int i = 0; i < tparams.length; ++i) varMap.put(tparams[i], new Integer(i)); TEnv tEnv = new TEnv() { public boolean definesVar(Symbol sym) { return varMap.containsKey(sym); } public Tree treeForVar(Symbol sym) { int idx = ((Integer)varMap.get(sym)).intValue(); Tree array = gen.mkLocalRef(pos, vparams[0]); return gen.mkArrayGet(pos, array, idx); } }; rhs = typeAsValue(typSym.pos, typSym.type(), accSym, tEnv); } return gen.DefDef(accSym, rhs); } private Tree tConstructorVal(Symbol clsSym, Symbol tcSym) { return gen.ValDef(tcSym, tcSym.isStatic() ? Tree.Empty : tConstructorRHS(tcSym.pos, clsSym, tcSym)); } private Tree classInitialiser(Symbol clsSym, Symbol ciSym, Symbol tcSym) { if (tcSym.isStatic()) { int pos = tcSym.pos; Tree rhs = tConstructorRHS(pos, clsSym, ciSym); Tree assign = gen.Assign(pos, gen.Ident(pos, tcSym), rhs); return gen.DefDef(ciSym, assign); } else return Tree.Empty; } private Tree tConstructorRHS(int pos, Symbol clsSym, Symbol owner) { int zCount = 0, mCount = 0, pCount = 0; Symbol[] tparams = clsSym.typeParams(); for (int i = 0; i < tparams.length; ++i) { if ((tparams[i].flags & Modifiers.COVARIANT) != 0) ++pCount; else if ((tparams[i].flags & Modifiers.CONTRAVARIANT) != 0) ++mCount; else ++zCount; } Ancestor[][] ancestors = computeAncestors(clsSym); int[] ancestorCode = getAncestorCode(ancestors); Tree outer = isNestedClass(clsSym) ? (clsSym.owner().isClass() ? gen.This(pos, clsSym.owner()) : gen.mkGlobalRef(pos, defs.TYPECONSTRUCTOR_FUNCTIONOUTER())) : gen.mkNullLit(pos); Tree[] tcArgs = new Tree[] { gen.mkIntLit(pos, level(clsSym)), gen.mkSymbolNameLit(pos, clsSym), outer, gen.mkIntLit(pos, zCount), gen.mkIntLit(pos, mCount), gen.mkIntLit(pos, pCount), gen.mkIntLit(pos, ancestors.length), ancestorCode.length == 0 ? gen.mkNullLit(pos) : mkNewIntLitArray(pos, getAncestorCode(ancestors), owner) }; Symbol tcConst = defs.TYPECONSTRUCTOR_CLASS.primaryConstructor(); Tree tcCall = gen.mkApply_V(pos, gen.mkGlobalRef(pos, tcConst), tcArgs); return gen.New(pos, tcCall); } private Tree mkNewIntLitArray(int pos, int[] values, Symbol owner) { Tree[] intLits = new Tree[values.length]; for (int i = 0; i < values.length; ++i) intLits[i] = gen.mkIntLit(pos, values[i]); return gen.mkNewArray(pos, defs.INT_TYPE(), intLits, owner); } /** * Return a method to instantiate the given type. */ private Tree.DefDef instantiatorBody(Symbol clsSym, Symbol insSym) { // TODO fix flags for all symbols below final int pos = clsSym.pos; final Symbol[] vparams = insSym.valueParams(); Tree[] body = new Tree[2]; // Generate call to "getInstantiation" method of // constructor. Tree getInstFun = gen.Select(pos, gen.mkLocalRef(pos, getTConstructorSym(clsSym)), defs.TYPECONSTRUCTOR_GETINSTANTIATION()); Tree[] getInstArgs = new Tree[]{ gen.mkLocalRef(pos, vparams[0]) }; Symbol instVal = insSym.newVariable(pos, 0, Name.fromString("inst")); instVal.setInfo(defs.SCALACLASSTYPE_TYPE()); Tree instValDef = gen.ValDef(instVal, gen.mkApply_V(pos, getInstFun, getInstArgs)); // Generate test to see if a call to "instantiate" is // necessary. Tree cond = gen.mkApply_V(pos, gen.Select(pos, gen.mkLocalRef(pos, instVal), defs.ANY_BANGEQ), new Tree[] { gen.mkNullLit(pos) }); Tree thenP = gen.mkLocalRef(pos, instVal); final HashMap varMap = new HashMap(); Symbol[] tparams = clsSym.typeParams(); for (int i = 0; i < tparams.length; ++i) varMap.put(tparams[i], new Integer(i)); // Type environment mapping the type parameters of the // class to their corresponding element in the "types" // array passed to this instantiator. TEnv tEnv = new TEnv() { public boolean definesVar(Symbol sym) { return varMap.containsKey(sym); } public Tree treeForVar(Symbol sym) { int idx = ((Integer)varMap.get(sym)).intValue(); Tree array = gen.mkLocalRef(pos, vparams[0]); return gen.mkArrayGet(pos, array, idx); } }; Type[] parents = clsSym.parents(); TreeList parentTypes = new TreeList(); for (int i = 0; i < parents.length; ++i) { Type parent = parents[i]; if (!isStronglyTrivial(parent)) parentTypes.append(typeAsValue(pos, parent, insSym, tEnv)); } boolean emptyParents = (parentTypes.length() == 0); Tree parentsArray = emptyParents ? gen.mkGlobalRef(pos, defs.SCALACLASSTYPE_EMPTYARRAY()) : gen.mkNewArray(pos, defs.SCALACLASSTYPE_TYPE(), parentTypes.toArray(), insSym); Tree instFun = gen.Select(pos, gen.mkLocalRef(pos, getTConstructorSym(clsSym)), defs.TYPECONSTRUCTOR_INSTANTIATE()); Tree[] instArgs = new Tree[] { gen.mkLocalRef(pos, vparams[0]), emptyParents ? parentsArray : gen.mkNullLit(pos) }; Tree instCall = gen.mkApply_V(pos, instFun, instArgs); Tree elseP; if (!emptyParents) { Tree setParentsFun = gen.Select(pos, instCall, defs.SCALACLASSTYPE_SETPARENTS()); elseP = gen.mkApply_V(pos, setParentsFun, new Tree[] { parentsArray }); } else elseP = instCall; Tree ifExpr = gen.If(pos, cond, thenP, elseP, defs.SCALACLASSTYPE_TYPE()); return gen.DefDef(insSym, gen.mkBlock(pos, instValDef, ifExpr)); } /** * Generate code to test if the given expression is an * instance of the given type. */ private Tree genInstanceTest(int pos, Tree expr, Type tp) { Tree tpVal = typeAsValue(pos, tp, currentOwner, EENV); if (isKnowClassType(tp) && isLocalIdent(expr)) { Symbol sym = expr.symbol(); Tree cheapTest = gen.mkIsInstanceOf(pos, gen.mkLocalRef(pos, sym), tp, true); Symbol weakIsInst = defs.SCALACLASSTYPE_WEAKISINSTANCE(); Tree scalaTpVal = gen.mkAsInstanceOf(pos, tpVal, defs.SCALACLASSTYPE_TYPE(), true); Tree expensiveTest = gen.mkApply_V(pos, gen.Select(pos, scalaTpVal, weakIsInst), new Tree[] { expr }); return gen.mkApply_V(pos, gen.Select(pos, cheapTest, defs.BOOLEAN_AND()), new Tree[] { expensiveTest }); } else { Tree fun = gen.Select(pos, tpVal, defs.TYPE_ISINSTANCE()); return gen.mkApply_V(pos, fun, new Tree[] { expr }); } } /** * Generate code to cast the given value to the given type. */ private Tree genTypeCast(int pos, Tree expr, Type tp) { Tree tpVal = typeAsValue(pos, tp, currentOwner, EENV); Tree fun = gen.Select(pos, tpVal, defs.TYPE_CAST()); Tree castCall = gen.mkApply_V(pos, fun, new Tree[] { expr }); return gen.mkAsInstanceOf(pos, castCall, tp); } /** * Return true iff the given type is trivial, that is if it * has neither a prefix, nor type parameters. */ private boolean isTrivial(Type tp) { switch (tp) { case TypeRef(_, Symbol sym, Type[] args): return sym.isStatic() && args.length == 0; case SingleType(_, _): case ThisType(_): case CompoundType(_, _): return false; default: throw Debug.abort("unexpected type", tp); } } /** * Return true iff the given type is strongly trivial, that is * if it and all its ancestors are trivial. */ private boolean isStronglyTrivial(Type tp) { if (isTrivial(tp)) { Type[] parents = tp.parents(); for (int i = 0; i < parents.length; ++i) { if (!isStronglyTrivial(parents[i])) return false; } return true; } else return false; } private boolean isKnowClassType(Type tp) { switch (tp) { case TypeRef(_, Symbol sym, _): return (sym != defs.ARRAY_CLASS) && !sym.isParameter(); default: return false; } } private boolean isLocalIdent(Tree tree) { switch (tree) { case Ident(_): return tree.symbol().isLocal(); default: return false; } } /** * Transform a type into a tree representing it. */ private Tree typeAsValue(int pos, Type tp, Symbol owner, TEnv env) { switch (tp) { case ConstantType(Type base, _): return typeAsValue(pos, base, owner, env); case TypeRef(Type pre, Symbol sym, Type[] args): if (env.definesVar(sym)) { assert args.length == 0; return env.treeForVar(sym); } else if (sym == defs.ARRAY_CLASS) { assert args.length == 1; return arrayType(pos, sym, args[0], owner, env); } else if (predefTypes.containsKey(sym)) { return gen.mkGlobalRef(pos, (Symbol)predefTypes.get(sym)); } else if (sym.isJava()) { assert args.length <= 1 : Debug.show(sym) + " " + args.length; return javaType(pos, sym); } else if (!sym.isParameter()) { // Reference to a "global" type. return scalaClassType(pos, tp, owner, env); } else { assert !isValuePrefix(pre) : tp; return gen.mkLocalRef(pos, getAccessorSym(sym)); } case SingleType(Type pre, Symbol sym): return singleType(pos, (Type.SingleType)tp); case ThisType(Symbol sym): return thisType(pos, sym); case CompoundType(Type[] parts, Scope members): return compoundType(pos, parts, members, owner, env); default: throw Debug.abortIllegalCase(tp); } } private Tree arrayType(int pos, Symbol sym, Type elemType, Symbol owner, TEnv env) { Tree constr = gen.mkPrimaryConstructorGlobalRef(pos, defs.JAVAREFARRAYTYPE_CLASS); Tree[] args = new Tree[]{ typeAsValue(pos, elemType, owner, env) }; return gen.New(pos, gen.mkApply_V(constr, args)); } private Tree javaType(int pos, Symbol sym) { Tree constr = gen.mkPrimaryConstructorGlobalRef(pos, defs.JAVACLASSTYPE_CLASS); Tree nameLit = gen.mkSymbolNameLit(pos, sym); Tree[] args = new Tree[] { nameLit }; return gen.New(pos, gen.mkApply_V(constr, args)); } private Tree thisType(int pos, Symbol sym) { Tree constr = gen.mkPrimaryConstructorGlobalRef(pos, defs.SINGLETYPE_CLASS); Tree[] args = new Tree[] { gen.This(pos, sym) }; return gen.New(pos, gen.mkApply_V(constr, args)); } private Tree singleType(int pos, Type.SingleType tp) { Tree constr = gen.mkPrimaryConstructorGlobalRef(pos, defs.SINGLETYPE_CLASS); Tree[] args = new Tree[] { gen.mkQualifier(pos, tp) }; return gen.New(pos, gen.mkApply_V(constr, args)); } private Tree compoundType(int pos, Type[] parts, Scope members, Symbol owner, TEnv env) { Tree[] partsT = new Tree[parts.length]; for (int i = 0; i < parts.length; ++i) partsT[i] = typeAsValue(pos, parts[i], owner, env); Tree[] constrArgs = new Tree[] { gen.mkNewArray(pos, defs.CLASSTYPE_TYPE(), partsT, owner), gen.mkBooleanLit(pos, members.isEmpty()) }; Tree constr = gen.mkPrimaryConstructorGlobalRef(pos, defs.COMPOUNDTYPE_CLASS); return gen.New(pos, gen.mkApply_V(constr, constrArgs)); } private Tree scalaClassType(int pos, Type tp, Symbol owner, TEnv env) { switch (tp) { case TypeRef(Type pre, Symbol sym, Type[] args): Symbol insSym = getInstMethSym(sym); Tree preFun = (isNestedClass(sym) && sym.owner().isClass()) ? gen.Select(pos, gen.mkQualifier(pos, pre), insSym) : gen.Ident(pos, insSym); // TODO special case for monomorphic cases Tree[] insArgs; if (true || args.length > 0) { Tree[] elems = new Tree[args.length]; int[] perm = typeParamsPermutation(sym.typeParams()); for (int i = 0; i < args.length; ++i) elems[i] = typeAsValue(pos, args[perm[i]], owner, env); insArgs = new Tree[] { gen.mkNewArray(pos, defs.TYPE_TYPE(), elems, owner) }; } else insArgs = Tree.EMPTY_ARRAY; return gen.mkApply_V(pos, preFun, insArgs); default: throw Debug.abort("unexpected type: ", tp); } } private final int VARIANT = Modifiers.COVARIANT | Modifiers.CONTRAVARIANT; /** * Compute the (unique) permutation which puts all invariant * type parameters first, followed by the contravariant ones, * then the covariants, preserving the relative ordering of * arguments with same variance. */ private int[] typeParamsPermutation(Symbol[] params) { int[] tparamsPerm = new int[params.length]; int permIdx = 0; for (int i = 0; i < params.length; ++i) if ((params[i].flags & VARIANT) == 0) tparamsPerm[permIdx++] = i; for (int i = 0; i < params.length; ++i) if ((params[i].flags & Modifiers.CONTRAVARIANT) != 0) tparamsPerm[permIdx++] = i; for (int i = 0; i < params.length; ++i) if ((params[i].flags & Modifiers.COVARIANT) != 0) tparamsPerm[permIdx++] = i; assert permIdx == tparamsPerm.length; return tparamsPerm; } private boolean isValuePrefix(Type pre) { switch (pre) { case ThisType(Symbol clazz): return !(clazz.isPackage() || clazz.isNone()); case NoPrefix: return false; default: return true; } } private Ancestor[][] computeAncestors0(Symbol classSym) { Symbol[] nstParents = notStronglyTrivialParents(classSym); int level = level(classSym); ArrayList/**/[] ancestor = new ArrayList[level + 1]; for (int l = 0; l < ancestor.length; ++l) ancestor[l] = new ArrayList(); if (!isTrivial(classSym.type())) ancestor[level].add(new Ancestor(classSym, -1, -1)); // Go over parents from left to right and add missing // ancestors to the set, remembering where they come from. for (int p = 0; p < nstParents.length; ++p) { Symbol parentSymbol = nstParents[p]; Ancestor[][] parentAncestors = computeAncestors(parentSymbol); assert parentAncestors.length <= ancestor.length; for (int l = 0; l < parentAncestors.length; ++l) { ArrayList/**/ myRow = ancestor[l]; Ancestor[] parentRow = parentAncestors[l]; parentRowLoop: for (int i = 0; i < parentRow.length; ++i) { Symbol sym = parentRow[i].symbol; assert !isTrivial(sym.type()) : sym; Iterator myRowIt = myRow.iterator(); while (myRowIt.hasNext()) { Ancestor myAncestor = (Ancestor)myRowIt.next(); if (myAncestor.symbol == sym) continue parentRowLoop; } myRow.add(new Ancestor(sym, p, i)); } } } int ancestorsLen = ancestor.length; while (ancestorsLen > 0 && ancestor[ancestorsLen - 1].isEmpty()) --ancestorsLen; Ancestor[][] finalAncestor = new Ancestor[ancestorsLen][]; for (int i = 0; i < finalAncestor.length; ++i) { finalAncestor[i] = (Ancestor[]) ancestor[i].toArray(new Ancestor[ancestor[i].size()]); } return finalAncestor; } /** Return the non-trivial ancestors of the class */ private Ancestor[][] computeAncestors(Symbol classSym) { Ancestor[][] ancestor = (Ancestor[][])ancestorCache.get(classSym); if (ancestor == null) { ancestor = computeAncestors0(classSym); ancestorCache.put(classSym, ancestor); // debugPrintAncestor(classSym, ancestor); } return ancestor; } /** Return the parents which are not strongly trivial. */ private Symbol[] notStronglyTrivialParents(Symbol classSym) { Type[] parentTypes = classSym.parents(); ArrayList nstParents = new ArrayList(parentTypes.length); for (int i = 0; i < parentTypes.length; ++i) { if (!isStronglyTrivial(parentTypes[i])) nstParents.add(parentTypes[i].symbol()); } return (Symbol[]) nstParents.toArray(new Symbol[nstParents.size()]); } private int[] getAncestorCode(Ancestor[][] ancestor) { ArrayList/**/[] prunedRows = new ArrayList[ancestor.length]; int totalSize = 0; for (int l = 0; l < ancestor.length; ++l) { Ancestor[] row = ancestor[l]; ArrayList/**/ prunedRow = new ArrayList(row.length); for (int i = 0; i < row.length; ++i) { if (row[i].parentIndex > 0) prunedRow.add(row[i]); } prunedRows[l] = prunedRow; if (!prunedRow.isEmpty()) totalSize += 2 + 2 * prunedRow.size(); } int[] res = new int[totalSize]; int i = 0; for (int l = 0; l < prunedRows.length; ++l) { ArrayList row = prunedRows[l]; if (!row.isEmpty()) { res[i++] = l; res[i++] = row.size(); Iterator ancIt = row.iterator(); while (ancIt.hasNext()) { Ancestor anc = (Ancestor)ancIt.next(); res[i++] = anc.parentIndex; res[i++] = anc.position; } } } assert i == totalSize; return res; } } /** * Minimalistic transformer, which simply transforms calls to * isInstanceOf/asInstanceOf by calls to their erased * counterparts. Used when full run time types are disabled. */ private class TV_MiniTransformer extends GenTransformer { private final Definitions defs; public TV_MiniTransformer(Global global) { super(global); defs = global.definitions; } public Tree transform(Tree tree) { switch (tree) { case Apply(TypeApply(Tree fun, Tree[] targs), Tree[] vargs): Symbol funSym = fun.symbol(); if (funSym == defs.ANY_IS || funSym == defs.ANY_AS) { Symbol erasedSym = (funSym == defs.ANY_AS) ? defs.ANY_AS_ERASED : defs.ANY_IS_ERASED; return gen.mkApplyTV(tree.pos, gen.Select(fun.pos, qualifierOf(fun), erasedSym), targs, vargs); } else return super.transform(tree); default: return super.transform(tree); } } } /** * Extract qualifier from a tree, which must be a Select node. */ private Tree qualifierOf(Tree tree) { switch (tree) { case Select(Tree qualifier, _): return qualifier; default: throw Debug.abort("cannot extract qualifier from ", tree); } } // Debugging function private void debugPrintAncestor(Symbol sym, Ancestor[][] ancestor) { System.out.println("ancestor for " + Debug.show(sym)); for (int l = 0; l < ancestor.length; ++l) { System.out.print(" [" + l + "] "); for (int i = 0; i < ancestor[l].length; ++i) { if (i > 0) System.out.print(" "); System.out.println(" " + Debug.show(ancestor[l][i].symbol) + "/par" + ancestor[l][i].parentIndex + "/pos" + ancestor[l][i].position); } if (ancestor[l].length == 0) System.out.println(""); } } ////////////////////////////////////////////////////////////////////// private static class TEnv { public boolean definesVar(Symbol sym) { return false; } public Tree treeForVar(Symbol sym) { throw Debug.abort("no tree for variable " + sym); } } private static class Ancestor { public static final Ancestor[] EMPTY_ARRAY = new Ancestor[0]; public final Symbol symbol; public final int parentIndex; public final int position; public Ancestor(Symbol symbol, int parentIndex, int position) { this.symbol = symbol; this.parentIndex = parentIndex; this.position = position; } } }