Java tutorial
/******************************************************************************* Copyright 2009,2011, Oracle and/or its affiliates. All rights reserved. Use is subject to license terms. This distribution may include materials developed by third parties. ******************************************************************************/ package com.sun.fortress.compiler; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import com.sun.fortress.compiler.OverloadSet.TaggedFunctionName; import com.sun.fortress.compiler.codegen.ClassNameBundle; import com.sun.fortress.compiler.codegen.CodeGen; import com.sun.fortress.compiler.codegen.CodeGenClassWriter; import com.sun.fortress.compiler.codegen.FnNameInfo; import com.sun.fortress.compiler.index.Constructor; import com.sun.fortress.compiler.index.DeclaredFunction; import com.sun.fortress.compiler.index.DeclaredMethod; import com.sun.fortress.compiler.index.FieldGetterOrSetterMethod; import com.sun.fortress.compiler.index.Functional; import com.sun.fortress.compiler.index.FunctionalMethod; import com.sun.fortress.compiler.index.HasTraitStaticParameters; import com.sun.fortress.compiler.index.Method; import com.sun.fortress.compiler.phases.CodeGenerationPhase; import com.sun.fortress.exceptions.CompilerBug; import com.sun.fortress.exceptions.CompilerError; import com.sun.fortress.nodes.APIName; import com.sun.fortress.nodes.AbbreviatedType; import com.sun.fortress.nodes.AnyType; import com.sun.fortress.nodes.ArrowType; import com.sun.fortress.nodes.BaseType; import com.sun.fortress.nodes.BottomType; import com.sun.fortress.nodes.FnDecl; import com.sun.fortress.nodes.Id; import com.sun.fortress.nodes.IdOrOp; import com.sun.fortress.nodes.IdOrOpOrAnonymousName; import com.sun.fortress.nodes.IntersectionType; import com.sun.fortress.nodes.NamedType; import com.sun.fortress.nodes.Op; import com.sun.fortress.nodes.OpArg; import com.sun.fortress.nodes.Param; import com.sun.fortress.nodes.StaticArg; import com.sun.fortress.nodes.StaticParam; import com.sun.fortress.nodes.TraitSelfType; import com.sun.fortress.nodes.TraitType; import com.sun.fortress.nodes.TupleType; import com.sun.fortress.nodes.Type; import com.sun.fortress.nodes.TypeArg; import com.sun.fortress.nodes.TypeOrPattern; import com.sun.fortress.nodes.UnionType; import com.sun.fortress.nodes.VarType; import com.sun.fortress.nodes_util.NodeFactory; import com.sun.fortress.nodes_util.NodeUtil; import com.sun.fortress.nodes_util.Span; import com.sun.fortress.repository.ProjectProperties; import com.sun.fortress.runtimeSystem.InitializedStaticField; import com.sun.fortress.runtimeSystem.InstantiatingClassloader; import com.sun.fortress.runtimeSystem.Naming; import com.sun.fortress.scala_src.overloading.OverloadingOracle; import com.sun.fortress.scala_src.types.TypeAnalyzer; import com.sun.fortress.scala_src.types.TypeSchemaAnalyzer; import com.sun.fortress.scala_src.useful.STypesUtil; import com.sun.fortress.useful.BASet; import com.sun.fortress.useful.BATree; import com.sun.fortress.useful.CycleInRelation; import com.sun.fortress.useful.DefaultComparator; import com.sun.fortress.useful.F; import com.sun.fortress.useful.MagicNumbers; import com.sun.fortress.useful.MultiMap; import com.sun.fortress.useful.TopSort; import com.sun.fortress.useful.TopSortItemImpl; import com.sun.fortress.useful.Useful; abstract public class OverloadSet implements Comparable<OverloadSet> { //partial order Type static class POType extends TopSortItemImpl<Type> { public POType(Type x) { super(x); } } //partial order tagged function static class POTFN extends TopSortItemImpl<TaggedFunctionName> { public POTFN(TaggedFunctionName x) { super(x); } } public static class TaggedFunctionName implements Comparable<TaggedFunctionName> { final private APIName tagA; final private Functional tagF; public TaggedFunctionName(APIName a, Functional f) { this.tagF = f; this.tagA = a; } public Functional getF() { return tagF; } public List<Param> getParameters() { return tagF.parameters(); } public Type getReturnType() { return tagF.getReturnType().unwrap(); } public int hashCode() { return tagF.hashCode() + MagicNumbers.a * tagA.hashCode(); } public boolean equals(Object o) { if (o instanceof TaggedFunctionName) { TaggedFunctionName tfn = (TaggedFunctionName) o; return tagF.equals(tfn.tagF) && tagA.equals(tfn.tagA); } return false; } public List<Type> getThrownTypes() { return tagF.thrownTypes(); } public String toString() { return tagA.toString() + ".." + tagF.toString(); } @Override public int compareTo(TaggedFunctionName o) { int i = tagF.toUndecoratedName().toString().compareTo(o.tagF.toUndecoratedName().toString()); if (i != 0) return i; i = tagA.toString().compareTo(o.tagA.toString()); if (i != 0) return i; List<Param> p = getParameters(); List<Param> q = o.getParameters(); if (p.size() < q.size()) return -1; if (p.size() > q.size()) return 1; for (int j = 0; j < p.size(); j++) { Param pp = p.get(j); Param qq = q.get(j); if (!(pp.getIdType().unwrap() instanceof Type && qq.getIdType().unwrap() instanceof Type)) throw new CompilerError("Types are expected."); Type tp = (Type) pp.getIdType().unwrap(); Type tq = (Type) qq.getIdType().unwrap(); Class cp = tp.getClass(); Class cq = tq.getClass(); if (cp.equals(cq)) { i = tp.toString().compareTo(tq.toString()); } else { i = cp.getName().compareTo(cq.getName()); } if (i != 0) return i; } return 0; } } /** * The set of functions that are less-specific-than-or-equal to the * parameters seen so far, so the parameter seen so far would be legal * for any of them. The goal is to thin this set as more parameters * are found, and ultimately to choose the most specific one that * remains. */ final Set<TaggedFunctionName> lessSpecificThanSoFar; final IdOrOpOrAnonymousName name; /** * The functions, ordered from most to least specific order. * This is used to obtain a dispatch order in the presence * of generics (it also works when there are no generics). */ TaggedFunctionName[] specificDispatchOrder; /** * This overloaded function may have a member whose signature matches * the overloaded function's signature. If so, the principalMember is not * null. */ TaggedFunctionName principalMember; public String genericSchema = ""; /* Need to stash this for generic-tagged overloads. */ /** * Every function in the set of overloads is a principal member of a smaller * (possibly singleton) overload set. Overloaded functions need to be * compiled for the non-singleton subsets if the overloaded function is * exported (i.e., is not specific to a use site in a component). * * Note that if the entire set has a principal member, then it also appears * in this subset, because that indicates that the principal member * (a single function that may be dispatched to) needs a local-mangled name. * * Thus, when iterating over the values in this set to generate code, * it is necessary to guard against generating code for the outermost * overloaded set. * * Uses a comparator that takes parameter lists into account. */ private BATree<String, OverloadSet> overloadSubsets = new BATree<String, OverloadSet>( DefaultComparator.<String>normal()); protected BASet<String> otherOverloadKeys = new BASet<String>(DefaultComparator.<String>normal()); /** * Used to answer subtype questions. */ final TypeAnalyzer ta; final OverloadingOracle oa; /** * Assuming we have a parent (are not the root of a tree of OverloadSets), * what is that parent. */ final OverloadSet parent; final int paramCount; final APIName ifNone; final String packageAndClassName; /** * Which parameter is used to split this set into subsets? */ OverloadSet[] children; boolean splitDone; /* in the event that the overloaded function is generic, these will be non-null after "split" */ List<StaticParam> static_parameters = null; Naming.XlationData xldata = null; // String PCN = null; protected OverloadSet(APIName ifNone, IdOrOpOrAnonymousName name, TypeAnalyzer ta, OverloadingOracle oa, Set<OverloadSet.TaggedFunctionName> lessSpecificThanSoFar, OverloadSet parent, int paramCount) { this.ifNone = ifNone; this.packageAndClassName = NamingCzar.javaPackageClassForApi(ifNone); this.name = name; this.ta = ta; this.oa = oa; this.lessSpecificThanSoFar = lessSpecificThanSoFar; this.parent = parent; this.paramCount = paramCount; } protected OverloadSet(IdOrOpOrAnonymousName name, TypeAnalyzer ta, OverloadingOracle oa, Set<OverloadSet.TaggedFunctionName> lessSpecificThanSoFar, int paramCount, APIName ifNone) { this(ifNone, name, ta, oa, lessSpecificThanSoFar, null, paramCount); } protected OverloadSet(final APIName apiname, IdOrOpOrAnonymousName name, TypeAnalyzer ta, OverloadingOracle oa, Set<Functional> defs, int n) { this(name, ta, oa, Useful.applyToAll(defs, new F<Functional, TaggedFunctionName>() { @Override public TaggedFunctionName apply(Functional f) { return new TaggedFunctionName(apiname, f); } }), n, apiname); // Ensure that they are all the same size. for (TaggedFunctionName f : lessSpecificThanSoFar) { if (CodeGenerationPhase.debugOverloading >= 2) System.err.println("Overload: " + f); List<Param> parameters = f.getParameters(); int this_size = parameters.size(); if (this_size != paramCount) throw new CompilerError("Need to handle variable arg dispatch elsewhere " + name); } } abstract protected void invokeParticularMethod(MethodVisitor mv, TaggedFunctionName f, String sig); /** * Creates a subset overload set; one that can be named independently as an overloaded function. * * @param childLSTSF - "less specific than so far" * @param parent TODO * @param principalMember * @return */ abstract protected OverloadSet makeSubset(Set<TaggedFunctionName> childLSTSF, TaggedFunctionName _principalMember, OverloadSet parent); public void split(boolean computeSubsets) { /* First determine if there are any overload subsets. This matters because it may affect naming of the leaf (single) functions. */ if (CodeGenerationPhase.debugOverloading >= 1) System.err.println(Useful.listInDelimiters("Split [", lessSpecificThanSoFar, "]", "\n ")); TopSortItemImpl<TaggedFunctionName>[] pofuns = new OverloadSet.POTFN[lessSpecificThanSoFar.size()]; /* Convert set of dispatch types into something that can be * (topologically) sorted. */ { int i = 0; for (TaggedFunctionName f : lessSpecificThanSoFar) { pofuns[i] = new POTFN(f); i++; } } for (int fi = 0; fi < pofuns.length; fi++) { TaggedFunctionName f = pofuns[fi].x; int i = 1; // for self /* Count the number of functions in LSTSF that are more * specific than or equal to f. * Also record any more-specific-than relationship encountered. */ for (int gi = 0; gi < pofuns.length; gi++) { TaggedFunctionName g = pofuns[gi].x; if (!(f == g)) { if (fLessSpecThanG(f, g)) { if (CodeGenerationPhase.debugOverloading >= 3) System.err.println(g.toString() + " is <= " + f.toString()); i++; pofuns[gi].edgeTo(pofuns[fi]); } } } if (computeSubsets) { if (i > 1) { /* There's at least one function more specific than f. */ if (i == lessSpecificThanSoFar.size()) { /* * If f is more specific than or equal to every member * of LSTSF, then it is the principal member. */ principalMember = f; } else { /* * There are SOME members of the subset that are more * specific than f; identify those, and create that * subset. f will be the principal member of the * overloaded function that results. External (through * API) references to f, must invoke the overloaded * function. */ HashSet<TaggedFunctionName> subLSTSF = new HashSet<TaggedFunctionName>(); subLSTSF.add(f); for (TaggedFunctionName g : lessSpecificThanSoFar) { if (!(f == g)) { if (fLessSpecThanG(f, g)) { subLSTSF.add(g); } } } OverloadSet subset = makeSubset(subLSTSF, f, this); subset.overloadSubsets = overloadSubsets; String overload_name = subset.compute_overload_name(f); // I don't think this key is right for generics. overloadSubsets.put(overload_name, subset); subset.addExtraKeys(f, otherOverloadKeys); } } } } TaggedFunctionName nameTemp = pofuns[0].x; List<TopSortItemImpl<TaggedFunctionName>> specificFirst; try { specificFirst = TopSort.depthFirstArray(pofuns); } catch (CycleInRelation ex) { for (int fi = 0; fi < pofuns.length; fi++) { System.out.println("# " + pofuns[fi].x); } throw new CompilerBug( "Likely bug in static analysis, apparently malformed overload set not rejected for\n" + nameTemp + "\n" + "Probable cause is a functional method and top level function with same signature.", ex); } if (computeSubsets) { /* * After identifying all the subsets (which have principal * members), generate their structure. */ for (OverloadSet subset : overloadSubsets.values()) { subset.splitInternal(specificFirst); } } // I don't think this key is right for generics. if (principalMember != null) { String overload_name = compute_overload_name(principalMember); overloadSubsets.put(overload_name, this); addExtraKeys(principalMember, otherOverloadKeys); } /* Split set into dispatch tree. */ splitInternal(specificFirst); } protected String compute_overload_name(TaggedFunctionName f) { // Think that the name needs to be different for generic methods (?) String filtered_name = chooseName(name.stringName(), NodeUtil.nameSuffixString(name)); static_parameters = staticParametersOf(f.tagF); if (static_parameters != null) { xldata = CodeGen.xlationData(Naming.FUNCTION_GENERIC_TAG); String sparamsType = NamingCzar.genericDecoration(static_parameters, xldata, ifNone); //KBN - testing - test this later - is temp the same as genericSchema? - fnNameInfo not ready for functions yet //FnNameInfo methodName = new FnNameInfo(f.tagF,ifNone); //ArrowType methodArrow = methodName.methodArrowType(Naming.NO_SELF); //String temp = NamingCzar.jvmTypeDesc(methodArrow, ifNone, false); genericSchema = //KBN - think that this is wrong for generic methods - need to add the self type to the arrow descriptor // see CodeGen.nonCollidingClosureName - DRC: need a version for the ForTraitOrObject Overload set instanceArrowSchema(f); String packageAndClassName = NamingCzar.javaPackageClassForApi(ifNone); String PCNOuter = // PCNOuter??? Naming.genericFunctionPkgClass(packageAndClassName, filtered_name, // Naming.makeTemplateSParams(sparamsType), sparamsType, genericSchema); return PCNOuter; } return name.stringName() + jvmSignatureFor(f); } private void addExtraKeys(TaggedFunctionName f, Set<String> s) { String filtered_name = chooseName(name.stringName(), NodeUtil.nameSuffixString(name)); static_parameters = staticParametersOf(f.tagF); if (static_parameters != null) { xldata = CodeGen.xlationData(Naming.FUNCTION_GENERIC_TAG); String sparamsType = NamingCzar.genericDecoration(static_parameters, xldata, ifNone); genericSchema = NamingCzar.makeArrowDescriptor(ifNone, overloadedDomain(), getRange()); String packageAndClassName = NamingCzar.javaPackageClassForApi(ifNone); // Believe that this will matter for the closures (generic functions) // that are generated for generic methods, hence okay to treat // as a generic function (instead of as a method). String PCNOuter = // PCNOuter??? Naming.genericFunctionPkgClass(packageAndClassName, filtered_name, Naming.makeTemplateSParams(sparamsType), // sparamsType, genericSchema); s.add(PCNOuter); } } private void splitInternal(List<TopSortItemImpl<TaggedFunctionName>> funsInSpecificOrder) { if (splitDone) return; int l = lessSpecificThanSoFar.size(); specificDispatchOrder = new TaggedFunctionName[l]; { int i = 0; for (TopSortItemImpl<TaggedFunctionName> tsii_f : funsInSpecificOrder) { TaggedFunctionName f = tsii_f.x; if (lessSpecificThanSoFar.contains(f)) specificDispatchOrder[i++] = f; } } if (l == 1) { splitDone = true; return; // If there are no other alternatives, then we are done. } splitDone = true; } /** * Returns true if the parameter list of g is more specific than or equal * to the parameter list of f. If any of g's parameters fails to be more * specific than the corresponding parameter of f (using the * "tweakedSubtypeTest"), then returns false. * * @param f * @param g * @return */ private boolean fLessSpecThanG(TaggedFunctionName f, TaggedFunctionName g) { List<Param> msf_parameters = f.getParameters(); List<Param> cand_parameters = g.getParameters(); if (msf_parameters.size() != cand_parameters.size()) { throw new CompilerError("Diff length parameter lists, should not be possible"); } // System.err.println("Is " + g + " more specific than " + f + "? (Comparing " + g.tagF + " and " + f.tagF + ")"); boolean result = oa.lteq(g.tagF, f.tagF); // System.err.println("Answer to: Is " + g + " more specific than " + f + "? " + result); return result; } @Override public int compareTo(OverloadSet o) { return name.stringName().compareTo(o.name.stringName()); } public IdOrOpOrAnonymousName getName() { return name; } public int getParamCount() { return paramCount; } /** * returns the code generation signature for the overloaded function. * (separation of concerns problem here -- taking joins of types, * and returning a target-platform (i.e., Java) signature). * * @return */ public String getSignature() { return getSignature(-1); } protected int selfIndex() { return -1; } public String getSignature(int param_to_skip) { if (principalMember != null) return jvmSignatureFor(principalMember); return overloadedDomainSig(param_to_skip) + NamingCzar.jvmTypeDesc(getRange(), ifNone); } public Type getRange() { if (principalMember != null) { TaggedFunctionName f = principalMember; return STypesUtil.insertStaticParams(normalizeSelfType(f.getReturnType()), f.tagF.staticParameters()); } else { List<Type> typesToJoin = new ArrayList(lessSpecificThanSoFar.size()); for (TaggedFunctionName f : lessSpecificThanSoFar) { List<StaticParam> sparams = f.tagF.staticParameters(); typesToJoin.add(STypesUtil.insertStaticParams(normalizeSelfType(f.getReturnType()), sparams)); } return join(ta, typesToJoin); } } /** * Type-level join that erases to Any if it obtains a Union type. * * TODO: should really erase to concrete least upper bound in type hierarchy, * but that requires extending TypeAnalyzer. */ private static Type join(TypeAnalyzer ta, List<Type> tys) { TypeSchemaAnalyzer tsa = new TypeSchemaAnalyzer(ta); int l = tys.size(); Type r = tys.get(0); if (l > 1) for (Type rr : tys.subList(1, l)) { r = tsa.joinED(r, rr); } // ta.join(JavaConversions.asIterable(tys)); if (r instanceof UnionType) { r = NodeFactory.makeAnyType(r.getInfo().getSpan()); } return r; } /** * Given an intersection of arrow types (what we get for at least some * overloaded functions) and an indication of how many parameters actually * appeared, create the signature for the overloaded function that will be * called. * * Also needs an appropriate TypeAnalyzer * <p/> * There's some fiddliness with varargs that has to be sorted out. * * @param t * @param paramCount * @param ta * @return */ public static String getSignature(IntersectionType t, int paramCount, TypeAnalyzer ta) { String s = overloadedDomainSig(t, paramCount, ta); Type r = getRange(t, paramCount, ta); s += NamingCzar.jvmTypeDesc(r, null); return s; } /** * Checks the parameter count against the intersection type to see if * this is a real overloading, or one that can trivially be disambiguated * by counting parameters. * * @param t * @param paramCount * @return */ public static boolean actuallyOverloaded(IntersectionType t, int paramCount) { return matchingCount(t, paramCount) > 1; } /** * Returns the number of types in the intersection t whose domain have * paramCount args, except if paramCount is zero, in which case, it returns * zero, even if the true answer is one. * * @param t * @param paramCount * @return */ private static int matchingCount(IntersectionType t, int paramCount) { int sum = 0; List<Type> types = t.getElements(); Type r = null; for (Type type : types) { Type r0; if (type instanceof ArrowType) { ArrowType at = (ArrowType) type; r0 = at.getDomain(); if (r0 instanceof TupleType) { TupleType tt = (TupleType) r0; if (paramCount == tt.getElements().size()) sum++; } else if (paramCount == 1) { // not a tuple means singleton sum++; } } else if (type instanceof IntersectionType) { sum += matchingCount((IntersectionType) type, paramCount); } else { throw new CompilerError("Non arrowtype " + type + " in (function) intersection type"); } } return sum; } /** * Returns the range derived from the an intersection type. * * Note the implicit assumption that the paramCount is not zero; * that this is called for principal members of overload subsets, and * f() can never be a principal member of such a subset because it would * be a singleton. * * * @param t * @param paramCount GREATER THAN ZERO. * @param ta * @return */ private static Type getRange(IntersectionType t, int paramCount, TypeAnalyzer ta) { List<Type> types = t.getElements(); List<Type> typesToJoin = new ArrayList(types.size()); for (Type type : types) { Type r0; if (type instanceof ArrowType) { ArrowType at = (ArrowType) type; // Ensure that the domain count matches the paramCount r0 = at.getDomain(); if (r0 instanceof TupleType) { TupleType tt = (TupleType) r0; if (paramCount != tt.getElements().size()) continue; } else if (paramCount != 1) { continue; } r0 = at.getRange(); } else if (type instanceof IntersectionType) { r0 = getRange((IntersectionType) type, paramCount, ta); } else { throw new CompilerError("Non arrowtype " + type + " in (function) intersection type"); } typesToJoin.add(r0); } return join(ta, typesToJoin); } /** * Gets the type of parameter i from the type derived for those members of the intersection * type with paramCount parameters, with help from the TypeAnalyzer ta. * * @param t * @param i * @param paramCount * @param ta * @return */ private static Type getParamType(IntersectionType t, int i, int paramCount, TypeAnalyzer ta) { List<Type> types = t.getElements(); List<Type> typesToJoin = new ArrayList(types.size()); for (Type type : types) { Type r0; r0 = getParamType(type, i, paramCount, ta); if (r0 == null) continue; typesToJoin.add(normalizeSelfType(r0)); } return join(ta, typesToJoin); } /** * Gets the type of parameter i from the type derived for those members of * the type with paramCount parameters, with help from the TypeAnalyzer ta. * * @param type * @param i 0 <= i < paramCount * @param paramCount > 0 * @param ta * @return */ public static Type getParamType(Type type, int i, int paramCount, TypeAnalyzer ta) { Type r0; if (type instanceof ArrowType) { ArrowType at = (ArrowType) type; r0 = at.getDomain(); // Ensure that the domain count matches the paramCount if (r0 instanceof TupleType) { TupleType tt = (TupleType) r0; if (paramCount != tt.getElements().size()) r0 = null; else r0 = tt.getElements().get(i); } else if (paramCount != 1) { r0 = null; } } else if (type instanceof IntersectionType) { r0 = getParamType((IntersectionType) type, i, paramCount, ta); } else { throw new CompilerError("Non arrowtype " + type + " in (function) intersection type"); } return r0; } /** * @param ti * @return */ private static Type normalizeSelfType(Type ti) { if (ti instanceof TraitSelfType) ti = ((TraitSelfType) ti).getNamed(); return ti; } private static boolean tweakedSubtypeTest(TypeAnalyzer ta, Type sub_type, Type super_type) { sub_type = normalizeSelfType(sub_type); super_type = normalizeSelfType(super_type); TypeSchemaAnalyzer tsa = new TypeSchemaAnalyzer(ta); return tsa.subtypeED(sub_type, super_type); } /** * @param paramCount * @return */ private static String overloadedDomainSig(IntersectionType t, int paramCount, TypeAnalyzer ta) { StringBuilder buf = new StringBuilder(); buf.append("("); for (int i = 0; i < paramCount; i++) { buf.append(NamingCzar.jvmTypeDesc(getParamType(t, i, paramCount, ta), null)); } buf.append(")"); return buf.toString(); } private String overloadedDomainSig(int param_to_skip) { StringBuilder buf = new StringBuilder(); buf.append("("); int i = 0; for (Type t : overloadedDomain()) { if (i != param_to_skip) buf.append(NamingCzar.jvmTypeDesc(t, ifNone)); i++; } buf.append(")"); return buf.toString(); } protected List<Type> overloadedDomain() { List<Type> res = new ArrayList<Type>(paramCount); if (principalMember != null) { List<Param> params = principalMember.getParameters(); List<StaticParam> sparams = principalMember.tagF.staticParameters(); for (int i = 0; i < paramCount; i++) { Param p = params.get(i); res.add(sParamTaggedTypeFromParam(p, sparams)); } } else for (int i = 0; i < paramCount; i++) { res.add(overloadedParamType(i)); } return res; } /** * @param p * @param sparams * @return */ public Type sParamTaggedTypeFromParam(Param p, List<StaticParam> sparams) { return STypesUtil.insertStaticParams(normalizeSelfType((Type) p.getIdType().unwrap()), sparams); } private Type overloadedParamType(int param) { List<Type> typesToJoin = new ArrayList<Type>(lessSpecificThanSoFar.size()); for (TaggedFunctionName f : lessSpecificThanSoFar) { List<Param> params = f.getParameters(); List<StaticParam> sparams = f.tagF.staticParameters(); Param p = params.get(param); if (!(p.getIdType().unwrap() instanceof Type)) throw new CompilerError("Type is expected: " + p.getIdType().unwrap()); typesToJoin.add(sParamTaggedTypeFromParam(p, sparams)); } return join(ta, typesToJoin); } /** * If there is a principal member (occurs in exported overloaded functions, * for example) then this could return more exceptions than necessary, for * example { IOException, FileNotFoundException }. * * For local overloads, principal members need not exist, hence we need the * union for the general case. * @return */ public String[] getExceptions() { HashSet<Type> exceptions = new HashSet<Type>(); for (TaggedFunctionName f : lessSpecificThanSoFar) { List<Type> f_exceptions = f.getThrownTypes(); exceptions.addAll(f_exceptions); } String[] string_exceptions = new String[exceptions.size()]; int i = 0; for (Type e : exceptions) { string_exceptions[i++] = NamingCzar.jvmTypeDesc(e, ifNone, false, false); } return string_exceptions; } protected String generateClosureTableName(TaggedFunctionName f) { //throw new CompilerError("need to determine naming scheme for function instantiated closure tables"); return Naming.cacheTableName(f.tagF.unambiguousName().getText()); } protected String generateClosureTableOwner(TaggedFunctionName f) { //throw new CompilerError("need to determine naming scheme for function instantiated closure tables"); return Naming.dotToSep(f.tagA.getText()); } /** * * may want to refactor this and version for methods in ForTraitOrObject subtype * * @param f - not used * @return */ protected String instanceArrowSchema(TaggedFunctionName f) { return NamingCzar.makeArrowDescriptor(f.getParameters(), f.getReturnType(), ifNone); } /** * * just the function name for normal functions * * @param f * @return */ protected String functionName(TaggedFunctionName f) { return NamingCzar.idOrOpToString(f.tagF.name()); } // /** // * // * For a normal function, we just use the number of parameters provided // * // * @param f // * @return // */ // protected int functionParamNum(TaggedFunctionName f) { // return f.getParameters().size(); // } /** * * for normal functions, return the given TFN * * @param f * @return */ protected TaggedFunctionName getFunctionToCall(TaggedFunctionName f) { return f; } /** * Provides information about the static type and structure of the static * type of a parameter to a function in an overload. * * @author dr2chase, kbn */ static class TypeStructure { /** If not generic, this is the Java type (boxed) * that the actual parameter must satisfy, * according to the variance. */ final String fullname; /** * If generic, this is the stem; the stem must match, and then * the parameters must match appropriately. */ final String rttiStem; /** static type parameters. * IF NULL, this is a VarType node, to be stored in local localIndex * when evaluated at runtime during dispatch. */ final TypeStructure[] staticParameters; /** index of local to store corresponding type (VarType or generic)*/ final int localIndex; /** next index after this node and all of its descendants. */ final int successorIndex; /** What variance applies to this node? * 1 = co, 0 = in, -1 = contra */ final int variance; public static final int COVARIANT = 1; public static final int INVARIANT = 0; public static final int CONTRAVARIANT = -1; /** flag indicating whether this type structure * represents a type variable, or there exists * a type variable nested inside this type structure * Used to determine if inference is needed */ final boolean containsTypeVariables; /** * If true, then the type in question is an object, and a faster type * check is possible. This is NYI plumbing for an obvious and probably * helpful optimization. */ final boolean isObject; public TypeStructure(String fullname, String stem, TypeStructure[] parameters, int localIndex, int successorIndex, int variance, boolean containsTypeVariables, boolean isObject) { super(); this.fullname = fullname; this.rttiStem = stem; this.staticParameters = parameters; this.localIndex = localIndex; this.successorIndex = successorIndex; this.variance = variance; this.containsTypeVariables = containsTypeVariables; this.isObject = isObject; } void emitInstanceOf(MethodVisitor mv, Label if_fail, boolean value_cast) { if (!containsTypeVariables) { /* easy case, no type variables = no inference */ emitInstanceOfNG(mv, if_fail, value_cast); // helpful for debugging } else { // has generic type variable if (value_cast) { // convert value to its type. // invokeinterface Any.getRTTI() mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Naming.ANY_TYPE_CLASS, Naming.RTTI_GETTER, Naming.STATIC_PARAMETER_GETTER_SIG); } if (staticParameters == null) { // VarType case -- leaf occurrence of static parameter // TODO need to check bound on type against generic decl. mv.visitVarInsn(Opcodes.ASTORE, localIndex); } else { //RTTI on the top of the stack String RTTIinterface = Naming.stemInterfaceToRTTIinterface(this.rttiStem); //might actually be a class in cases of Arrow and Tuple RTTIs Label ahead = new Label(); //DUP RTTI mv.visitInsn(Opcodes.DUP); //check that the RTTI matches what we're looking for mv.visitTypeInsn(Opcodes.INSTANCEOF, RTTIinterface); //if not, pop and try the next match mv.visitJumpInsn(Opcodes.IFNE, ahead); mv.visitInsn(Opcodes.POP); mv.visitJumpInsn(Opcodes.GOTO, if_fail); //if so, store the RTTI for possible inferences mv.visitLabel(ahead); mv.visitTypeInsn(Opcodes.CHECKCAST, RTTIinterface); mv.visitVarInsn(Opcodes.ASTORE, localIndex); //recursive check on static parameters int i = Naming.STATIC_PARAMETER_ORIGIN; for (TypeStructure p : staticParameters) { mv.visitVarInsn(Opcodes.ALOAD, localIndex); String method_name = Naming.staticParameterGetterName(rttiStem, i); if (RTTIinterface.endsWith(Naming.RTTI_INTERFACE_SUFFIX)) mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, RTTIinterface, method_name, Naming.STATIC_PARAMETER_GETTER_SIG); else // ends in RTTI_CLASS_SUFFIX - invoke virtual mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, RTTIinterface, method_name, Naming.STATIC_PARAMETER_GETTER_SIG); // get parameter p.emitInstanceOf(mv, if_fail, false); //, top_level_invariants); i++; } } } } /** * @param mv * @param if_fail * @param value_cast */ public void emitInstanceOfNG(MethodVisitor mv, Label if_fail, boolean value_cast) { if (value_cast) { InstantiatingClassloader.generalizedInstanceOf(mv, fullname); // Branch ahead if failure } else { // TOS is a Fortress RTTI // must check if the type extends the target type. // tricky cases are Arrow and Tuple. // getstatic fullname.RTTI // swap // invokevirtual RTTI.runtimeSupertypeOf mv.visitFieldInsn(Opcodes.GETSTATIC, fullname, Naming.RTTI_FIELD, Naming.RTTI_CONTAINER_DESC); mv.visitInsn(Opcodes.SWAP); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); } mv.visitJumpInsn(Opcodes.IFEQ, if_fail); } } static class SelfTypeStructure extends TypeStructure { final TypeStructure chainee; public SelfTypeStructure(String fullname, String stem, TypeStructure[] parameters, int localIndex, int successorIndex, int variance, boolean containsTypeVariables, boolean isObject, TypeStructure chained) { super(fullname, stem, parameters, localIndex, successorIndex, variance, containsTypeVariables, isObject); chainee = chained; } void emitInstanceOf(MethodVisitor mv, Label if_fail, boolean value_cast) { Label pop_then_fail = new Label(); Label second_arm = new Label(); mv.visitInsn(Opcodes.DUP); chainee.emitInstanceOf(mv, pop_then_fail, value_cast); mv.visitJumpInsn(Opcodes.GOTO, second_arm); mv.visitLabel(pop_then_fail); mv.visitInsn(Opcodes.POP); mv.visitJumpInsn(Opcodes.GOTO, if_fail); mv.visitLabel(second_arm); super.emitInstanceOf(mv, if_fail, value_cast); } } /** * Returns a type decision structure for the given input type. * * @param t the type to build the structure for. * @param spmap map recording occurrences of static parameter names. * Can be null for return types; these are not looked up at run time * @param variance context -- how must the type match? * @param storeAtIndex after the type test returns, store the result here. * @param staticParams the static parameters for this arm of the dispatch. * @return */ TypeStructure makeTypeStructure(Type t, MultiMap<String, TypeStructure> spmap, int variance, int storeAtIndex, List<StaticParam> staticParams, Functional eff) { String fullname = NamingCzar.jvmBoxedTypeName(t, ifNone); String rttiStem = null; TypeStructure[] parameters = null; int[] variances = null; List<Type> type_elements = null; boolean hasTypeVariables = false; boolean isVarType = false; boolean isObject = false; if (t instanceof TupleType) { type_elements = ((TupleType) t).getElements(); int numStaticArgs = type_elements.size(); variances = arrayOfInt(numStaticArgs, variance); rttiStem = Naming.TUPLE_RTTI_TAG + numStaticArgs; } else if (t instanceof ArrowType) { ArrowType at = ((ArrowType) t); Type range = at.getRange(); Type domain = at.getDomain(); if (false && domain instanceof TupleType) { type_elements = Useful.<Type, Type>list(((TupleType) domain).getElements(), range); } else { type_elements = Useful.<Type>list(domain, range); } int l = type_elements.size(); variances = arrayOfInt(l, ProjectProperties.DISABLE_CONTRAVARIANCE ? 0 : -variance); variances[l - 1] = variance; rttiStem = Naming.ARROW_RTTI_TAG + l; } else if (t instanceof TraitSelfType) { return makeTypeStructure(((TraitSelfType) t).getNamed(), spmap, variance, storeAtIndex, staticParams, eff); } else if (t instanceof TraitType) { // Would love to inquire if this is an object type TraitType tt = (TraitType) t; List<StaticArg> tt_sa = tt.getArgs(); Id tt_id = tt.getName(); List<String> tt_sa_oprs = new ArrayList<String>(); List<StaticArg> tt_sa_no_oprs = new ArrayList<StaticArg>(); for (StaticArg sta : tt_sa) { if (sta instanceof OpArg) { tt_sa_oprs.add(((OpArg) sta).getId().getText()); } else { tt_sa_no_oprs.add(sta); } } rttiStem = CodeGen.stemFromId(tt_id, packageAndClassName); rttiStem = Naming.oprArgAnnotatedRTTI(rttiStem, tt_sa_oprs); if (tt_sa.size() > 0) { // process args into types. Non-type args will be somewhat problematic at first. type_elements = new ArrayList<Type>(); // need to figure out how to normalize array length if non-type args. variances = arrayOfInt(tt_sa_no_oprs.size(), 0); int variance_index = 0; for (StaticArg sta : tt_sa_no_oprs) { if (sta instanceof TypeArg) { TypeArg sta_ta = (TypeArg) sta; type_elements.add(sta_ta.getTypeArg()); // if interesting variance, change it here. } else { // unhandled case. throw new CompilerError("Only handling some static args of generic types"); } variance_index++; } } } else if (t instanceof VarType) { VarType tt = (VarType) t; Id tt_id = tt.getName(); rttiStem = tt_id.getText(); // DO NOT API-ANNOTATE! // If the overload arm has no static parameters, or if they // come from an enclosing scope (from a trait type, if this is // a method), then we do not care about them, they are effectively // type constants. if (staticParams != null) { for (StaticParam sp : staticParams) { IdOrOp spn = sp.getName(); if (spn.getText().equals(rttiStem)) { hasTypeVariables = true; isVarType = true; break; } } } } else if (t instanceof BottomType) { // Do nothing, see how that works.... // throw new CompilerError("Not handling Bottom type yet in generic overload dispatch"); } else if (t instanceof AnyType) { // throw new CompilerError("Not handling Any type yet in generic overload dispatch"); // might need to init "stem" } else if (t instanceof AbbreviatedType) { throw new CompilerError("Not handling Abbreviated type yet in generic overload dispatch"); } else { throw new CompilerError("Not handling " + t.getClass() + " type yet in generic overload dispatch"); } if (type_elements != null) { parameters = new TypeStructure[type_elements.size()]; int i = 0; int next_index = storeAtIndex + 1; for (Type tt : type_elements) { TypeStructure ts = makeTypeStructure(tt, spmap, variances[i], next_index, staticParams, null); //has type variable if it or any nested structures have type variables hasTypeVariables = hasTypeVariables || ts.containsTypeVariables; parameters[i++] = ts; next_index = ts.successorIndex; } return new TypeStructure(fullname, rttiStem, parameters, storeAtIndex, next_index, variance, hasTypeVariables, isObject); } else if (isVarType) { TypeStructure x = null; /* If fullname is UP_INDEX, then this is "self", and we need to examine the * declared method to see if it has any static parameters. The inference * will trivially succeed, but we need to extract the parameters, so for now, * just do the inference. */ if (fullname.equals(Naming.UP_INDEX)) { // has to be a declared method DeclaredMethod dm = (DeclaredMethod) eff; if (dm.traitStaticParameters().size() > 0) { TraitType tt = NodeFactory.makeTraitTypeFromParams(dm.declaringTrait(), dm.traitStaticParameters()); x = makeTypeStructure(tt, spmap, variance, storeAtIndex, staticParams, eff); } } if (x == null) x = new TypeStructure(fullname, rttiStem, parameters, storeAtIndex, storeAtIndex + 1, variance, hasTypeVariables, isObject); else x = new SelfTypeStructure(fullname, rttiStem, parameters, storeAtIndex, x.successorIndex, variance, hasTypeVariables, isObject, x); if (spmap != null) spmap.putItem(rttiStem, x); return x; } else { return new TypeStructure(fullname, rttiStem, parameters, storeAtIndex, storeAtIndex + 1, variance, hasTypeVariables, isObject); } } public static TypeStructure makeParamTypeStructure(StaticParam sp, int index, int variance) { String name = sp.getName().getText(); //type structures representing static type parameters always have type variables in them return new TypeStructure(name, name, null, index, index + 1, variance, true, false); } /** * @param size * @param initial */ private int[] arrayOfInt(int size, int initial) { int[] variances = new int[size]; Arrays.fill(variances, initial); return variances; } public void generateCall(MethodVisitor mv, int firstArgIndex, int one_if_method_closure) { if (!splitDone) { throw new CompilerError("Must split overload set before generating call(s)"); } int l = specificDispatchOrder.length; TaggedFunctionName[] functionsToCall = new TaggedFunctionName[l]; for (int i = 0; i < l; i++) { functionsToCall[i] = getFunctionToCall(specificDispatchOrder[i]); } // create type structures for parameter types. TypeStructure[][] type_structures = new TypeStructure[l][]; MultiMap[] spmaps = new MultiMap[l]; TypeStructure[] return_type_structures = new TypeStructure[l]; for (int i = 0; i < l; i++) { TaggedFunctionName f = functionsToCall[i]; Functional eff = f.getF(); List<Param> parameters = f.getParameters(); MultiMap<String, TypeStructure> spmap = new MultiMap<String, TypeStructure>(); spmaps[i] = spmap; List<StaticParam> staticParams = staticParametersOf(f.getF()); Type rt = oa.getRangeType(eff); return_type_structures[i] = makeTypeStructure(rt, null, 1, 0, staticParams, eff); // skip parameters -- no 'this' for ordinary functions if (parameters.size() == 1 && oa.getDomainType(eff) instanceof TupleType) { TupleType tt = (TupleType) oa.getDomainType(eff); List<Type> tl = tt.getElements(); int storeAtIndex = tl.size(); // DRC back this out + firstArgIndex; // little dubious here, not sure we are getting the // right type structures for generic methods. what about 'self' TypeStructure[] f_type_structures = new TypeStructure[tl.size()]; type_structures[i] = f_type_structures; for (int j = 0; j < tl.size(); j++) { Type t = STypesUtil.insertStaticParams(tl.get(j), tt.getInfo().getStaticParams()); TypeStructure type_structure = makeTypeStructure(t, spmap, 1, storeAtIndex, staticParams, eff); f_type_structures[j] = type_structure; storeAtIndex = type_structure.successorIndex; } } else { int storeAtIndex = parameters.size(); // DRC back this out + firstArgIndex; TypeStructure[] f_type_structures = new TypeStructure[parameters.size()]; type_structures[i] = f_type_structures; for (int j = 0; j < parameters.size(); j++) { if (j != selfIndex()) { Type t = oa.getParamType(eff, j); TypeStructure type_structure = makeTypeStructure(t, spmap, 1, storeAtIndex, staticParams, eff); f_type_structures[j] = type_structure; storeAtIndex = type_structure.successorIndex; } } } } for (int i = 0; i < l; i++) { TaggedFunctionName f = functionsToCall[i]; TypeStructure[] f_type_structures = type_structures[i]; Label lookahead = null; boolean infer = false; List<StaticParam> staticParams = staticParametersOf(f.getF()); boolean last_case = i == l - 1; /* Trust the static checker; no need to verify * applicability of the last one. * Also, static parameters will be provided by static checker for the last one */ // Will need lookahead for the next one. lookahead = new Label(); // if this was a generic method that needs inference, we need to include the receiver argument // in the inference even if the firstArgIndex is 1 so that we can include it in inference // and dispatch //KBN-WIP is there a cleaner way to do this? int offset = (f_type_structures.length == specificDispatchOrder[i].getParameters().size()) ? firstArgIndex : 0; for (int j = 0; j < f_type_structures.length; j++) { if (j != selfIndex()) { //inference needed if the type structure contains generics TODO: do generics not appearing in the parameters make sense? probably not, but might need to deal with them. if (f_type_structures[j].containsTypeVariables) infer = true; } } if (infer || !last_case) for (int j = 0; j < f_type_structures.length; j++) { // Load actual parameter if (j != selfIndex()) { mv.visitVarInsn(Opcodes.ALOAD, j + offset); f_type_structures[j].emitInstanceOf(mv, lookahead, true); } } //Runtime inference for some cases if (infer) { @SuppressWarnings("unchecked") MultiMap<String, TypeStructure> staticTss = spmaps[i]; int localCount = f_type_structures[f_type_structures.length - 1].successorIndex; //counter for use storing stuff such as lower bounds //create type structures for lower bounds Map<StaticParam, TypeStructure> lowerBounds = new HashMap<StaticParam, TypeStructure>(); for (StaticParam sp : staticParams) lowerBounds.put(sp, makeParamTypeStructure(sp, localCount++, TypeStructure.COVARIANT)); //gather different types of bounds into Multimaps for use later MultiMap<StaticParam, StaticParam> relativeLowerBounds = new MultiMap<StaticParam, StaticParam>(); //form X :> Y MultiMap<StaticParam, Type> genericUpperBounds = new MultiMap<StaticParam, Type>(); //form X <: GenericStem[\ ... \] where Y appears in ... MultiMap<StaticParam, Type> concreteUpperBounds = new MultiMap<StaticParam, Type>(); //form X <: T where T contains no type variables for (int outer = 0; outer < staticParams.size(); outer++) { StaticParam outerSP = staticParams.get(outer); for (BaseType bt : outerSP.getExtendsClause()) { if (bt instanceof VarType) { // outerSP <: bt so outerSP will provide a lower bound on BT String varName = ((VarType) bt).getName().getText(); boolean found = false; for (int inner = 0; inner < outer && !found; inner++) { StaticParam innerSP = staticParams.get(inner); if (varName.equals(innerSP.getName().getText())) { relativeLowerBounds.putItem(innerSP, outerSP); // outerSP provides a lower bound on innerSP found = true; } } if (!found) throw new CompilerError( "Bad Scoping of static parameters found during runtime inference codegen:" + varName + " not declared before used in a bound"); } else if (bt instanceof AnyType) { //figure out if concrete or generic //do nothing - no need to add meaningless upper bound } else if (bt instanceof NamedType) { if (isGeneric(bt)) genericUpperBounds.putItem(outerSP, bt); else concreteUpperBounds.putItem(outerSP, bt); } } } //infer and load RTTIs for (int j = 0; j < staticParams.size(); j++) { StaticParam sp = staticParams.get(staticParams.size() - 1 - j); //reverse order due to left to right scoping Set<TypeStructure> instances = staticTss.get(sp.getName().getText()); //sort static parameters by their variance and put into //arrays using their local variable number List<Integer> invariantInstances = new ArrayList<Integer>(); List<Integer> covariantInstances = new ArrayList<Integer>(); List<Integer> contravariantInstances = new ArrayList<Integer>(); if (instances != null) for (TypeStructure ts : instances) { switch (ts.variance) { case TypeStructure.INVARIANT: invariantInstances.add(ts.localIndex); break; case TypeStructure.CONTRAVARIANT: contravariantInstances.add(ts.localIndex); break; case TypeStructure.COVARIANT: covariantInstances.add(ts.localIndex); break; default: throw new CompilerError("Unexpected Variance on TypeStructure during " + "generic instantiation analysis for overload dispatch"); } } // if any invariant instances, we must use that RTTI and check that //1) any other invariant instances are the same type (each subtypes the other) //2) any covariant instances are subtypes of the invariant instance //3) any contravariant instances are supertypes of the invariant instance if (invariantInstances.size() > 0) { //a valid instantiation must use the runtime type //of all invariant instances (which must all be the same) //thus, wlog, we can use the first invariant instance int RTTItoUse = invariantInstances.get(0); //1) for each other invariant instance, they must be the same //which we test by checking that each subtypes the other for (int k = 1; k < invariantInstances.size(); k++) { int RTTIcompare = invariantInstances.get(k); //RTTItoUse.runtimeSupertypeOf(RTTIcompare) mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); mv.visitVarInsn(Opcodes.ALOAD, RTTIcompare); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, lookahead); //if false fail //RTTIcompare.runtimeSupertypeOf(RTTItoUse) mv.visitVarInsn(Opcodes.ALOAD, RTTIcompare); mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, lookahead); //if false fail } //2) for each covariant instance, the runtime type (RTTIcompare) must be a // subtype of the instantiated type (RTTItoUse) for (int RTTIcompare : covariantInstances) { //RTTItoUse.runtimeSupertypeOf(RTTIcompare) mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); mv.visitVarInsn(Opcodes.ALOAD, RTTIcompare); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, lookahead); //if false fail } //3) for each contravariant instance, the instantiated type (RTTItoUse) must be a // subtype of the runtime type (RTTIcompare) for (int RTTIcompare : contravariantInstances) { //RTTIcompare.runtimeSupertypeOf(RTTItoUse) mv.visitVarInsn(Opcodes.ALOAD, RTTIcompare); mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, lookahead); //if false fail } //check lower bounds given by other variables Set<StaticParam> relativeLB = relativeLowerBounds.get(sp); if (relativeLB != null) for (StaticParam lb : relativeLB) { //RTTItoUse.runtimeSupertypeOf(otherLB) int otherOffset = lowerBounds.get(lb).localIndex; mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); mv.visitVarInsn(Opcodes.ALOAD, otherOffset); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, lookahead); //if false fail } //verify meets upper bounds Set<Type> concreteUB = concreteUpperBounds.get(sp); if (concreteUB != null) for (Type cub : concreteUB) { //transform into RTTI generateRTTIfromStaticType(mv, cub); mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, lookahead); //if false fail } //generate more bounds for generic upper bounds Set<Type> genericUB = genericUpperBounds.get(sp); if (genericUB != null) for (Type gub : genericUB) { TypeStructure newTS = makeTypeStructure(gub, staticTss, TypeStructure.COVARIANT, localCount, staticParams, null); localCount = newTS.successorIndex; mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); newTS.emitInstanceOf(mv, lookahead, false); //fail if RTTItoUse doesn't have this structure } //checks out, so store the RTTI we will use into the lower bound for this parameter mv.visitVarInsn(Opcodes.ALOAD, RTTItoUse); int index = lowerBounds.get(sp).localIndex; mv.visitVarInsn(Opcodes.ASTORE, index); } else if (contravariantInstances.size() == 0) { //we can do inference for covariant-only occurrences boolean started = false; if (covariantInstances.size() > 0) { started = true; mv.visitVarInsn(Opcodes.ALOAD, covariantInstances.get(0)); for (int k = 1; k < covariantInstances.size(); k++) { mv.visitVarInsn(Opcodes.ALOAD, covariantInstances.get(k)); //TODO: allow unions joinStackNoUnion(mv, lookahead); //fails if cannot join w/o union } } //incorporate lower bounds Set<StaticParam> relativeLB = relativeLowerBounds.get(sp); if (relativeLB != null) for (StaticParam lb : relativeLB) { mv.visitVarInsn(Opcodes.ALOAD, lowerBounds.get(lb).localIndex); if (started) { //join it in //TODO: allow unions joinStackNoUnion(mv, lookahead); } else { //start with this lower bound started = true; } } if (started) { //verify meets upper bounds Set<Type> concreteUB = concreteUpperBounds.get(sp); if (concreteUB != null) for (Type cub : concreteUB) { Label cleanup = new Label(); Label next = new Label(); mv.visitInsn(Opcodes.DUP); generateRTTIfromStaticType(mv, cub); //transform concrete bound into RTTI mv.visitInsn(Opcodes.SWAP); // LB <: CUB mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, cleanup); mv.visitJumpInsn(Opcodes.GOTO, next); mv.visitLabel(cleanup); mv.visitInsn(Opcodes.POP); mv.visitJumpInsn(Opcodes.GOTO, lookahead); mv.visitLabel(next); } //checks out, so store to lower bound of sp int index = lowerBounds.get(sp).localIndex; mv.visitVarInsn(Opcodes.ASTORE, index); //generate more bounds for generic upper bounds Set<Type> genericUB = genericUpperBounds.get(sp); if (genericUB != null) for (Type gub : genericUB) { TypeStructure newTS = makeTypeStructure(gub, staticTss, TypeStructure.COVARIANT, localCount, staticParams, null); localCount = newTS.successorIndex; mv.visitVarInsn(Opcodes.ALOAD, index); newTS.emitInstanceOf(mv, lookahead, false); //fail if candidate doesn't have this structure } } else { //Bottom is ok - no need to check upper bounds //or generate lower bounds mv.visitFieldInsn(Opcodes.GETSTATIC, Naming.RT_VALUES_PKG + "VoidRTTI", Naming.RTTI_SINGLETON, Naming.RTTI_CONTAINER_DESC); int index = lowerBounds.get(sp).localIndex; mv.visitVarInsn(Opcodes.ASTORE, index); } } else { //otherwise, we might need to do inference which is not implemented yet throw new CompilerError("non-invariant inference with contravariance not implemented"); } } //load instance cache table to avoid classloader when possible String tableName = this.generateClosureTableName(specificDispatchOrder[i]); //use original function for table name String tableOwner = this.generateClosureTableOwner(f); mv.visitFieldInsn(Opcodes.GETSTATIC, tableOwner, tableName, Naming.CACHE_TABLE_DESC); //load template class name String arrow = this.instanceArrowSchema(f); //NamingCzar.makeArrowDescriptor(f.getParameters(), f.getReturnType(),f.tagA); String functionName = this.functionName(f); String templateClass = Naming.genericFunctionPkgClass(Naming.dotToSep(f.tagA.getText()), functionName, Naming.LEFT_OXFORD + Naming.RIGHT_OXFORD, arrow); if (otherOverloadKeys.contains(templateClass)) { templateClass = Naming.genericFunctionPkgClass(Naming.dotToSep(f.tagA.getText()), NamingCzar.mangleAwayFromOverload(functionName), Naming.LEFT_OXFORD + Naming.RIGHT_OXFORD, arrow); //templateClass = NamingCzar.mangleAwayFromOverload(templateClass); } mv.visitLdcInsn(templateClass); String ic_sig; if (staticParams.size() > 6) { //use an array //load the function: RThelpers.loadClosureClass:(BAlongTree,String,RTTI[]) String paramList = Naming.CACHE_TABLE_DESC + NamingCzar.descString + Naming.RTTI_CONTAINER_ARRAY_DESC; ic_sig = Naming.makeMethodDesc(paramList, Naming.internalToDesc(NamingCzar.internalObject)); mv.visitLdcInsn(staticParams.size()); mv.visitTypeInsn(Opcodes.ANEWARRAY, Naming.RTTI_CONTAINER_TYPE); //dup array enough times to store RTTIs into it //know need at least 6 more mv.visitInsn(Opcodes.DUP); //first one to get arrays as top two elts on stack for (int numDups = staticParams.size() - 1; numDups > 0; numDups = numDups / 2) mv.visitInsn(Opcodes.DUP2); if (staticParams.size() % 2 == 0) mv.visitInsn(Opcodes.DUP); //if even, started halving with an odd number, so needs one last //store parameters into array for (int k = 0; k < staticParams.size(); k++) { int index = lowerBounds.get(staticParams.get(k)).localIndex; mv.visitLdcInsn(k); //index is the static param number mv.visitVarInsn(Opcodes.ALOAD, index); mv.visitInsn(Opcodes.AASTORE); } //array left on stack } else { //load the function: RTHelpers.loadClosureClass:(BAlongTree,(String,RTTI)^n)Object ic_sig = InstantiatingClassloader.jvmSignatureForOnePlusNTypes( Naming.CACHE_TABLE_TYPE + ";L" + NamingCzar.internalString, staticParams.size(), Naming.RTTI_CONTAINER_TYPE, Naming.internalToDesc(NamingCzar.internalObject)); //load parameter RTTIs for (int k = 0; k < staticParams.size(); k++) { int index = lowerBounds.get(staticParams.get(k)).localIndex; mv.visitVarInsn(Opcodes.ALOAD, index); } } mv.visitMethodInsn(Opcodes.INVOKESTATIC, Naming.RT_HELPERS, "loadClosureClass", ic_sig); //cast to object arrow int numParams = f.getParameters().size(); String objectAbstractArrow = NamingCzar.objectAbstractArrowTypeForNParams(numParams); InstantiatingClassloader.generalizedCastTo(mv, objectAbstractArrow); //if a method parameters converted //loadThisForMethods(mv); //load parameters for (int j = 0; j < f_type_structures.length; j++) { // Load actual parameter if (j != selfIndex()) { mv.visitVarInsn(Opcodes.ALOAD, j); // DRC back this out+ one_if_method_closure); // + firstArgIndex); KBN if a method, parameters already converted //no cast needed here - done by apply method } } //call apply method String objectArrow = NamingCzar.objectArrowTypeForNParams(numParams); String applySig = InstantiatingClassloader.jvmSignatureForNTypes(numParams, NamingCzar.internalObject, Naming.internalToDesc(NamingCzar.internalObject)); mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, objectArrow, Naming.APPLY_METHOD, applySig); //cast to correct return type Type f_return = f.getReturnType(); if (f_return instanceof BottomType) { CodeGen.castToBottom(mv); } else { String returnType = NamingCzar.makeBoxedTypeName(f_return, f.tagA); InstantiatingClassloader.generalizedCastTo(mv, returnType); } } else { //no inferences needed loadThisForMethods(mv); for (int j = 0; j < f_type_structures.length; j++) { // Load actual parameter if (j != selfIndex()) { mv.visitVarInsn(Opcodes.ALOAD, j + firstArgIndex); InstantiatingClassloader.generalizedCastTo(mv, f_type_structures[j].fullname); } } String sig = jvmSignatureFor(f); invokeParticularMethod(mv, f, sig); Type f_return = f.getReturnType(); if (f_return instanceof BottomType) { CodeGen.castToBottom(mv); } } mv.visitInsn(Opcodes.ARETURN); if (lookahead != null) mv.visitLabel(lookahead); } } //Generates an RTTI from a Type by recursing through the structure //grabbing singleton RTTIs or invoking factory methods. private void generateRTTIfromStaticType(MethodVisitor mv, Type t) { if (t instanceof TraitType) { List<StaticArg> args = ((TraitType) t).getArgs(); if (args != null && args.size() > 0) { //recurse for (StaticArg sa : args) { if (sa instanceof TypeArg) { generateRTTIfromStaticType(mv, ((TypeArg) sa).getTypeArg()); } else { throw new CompilerError("Expected TypeArg for RTTI generation"); } } //find component for type and call factory String typeName = NamingCzar.jvmTypeDesc(t, this.ifNone, false); mv.visitMethodInsn(Opcodes.INVOKESTATIC, typeName + Naming.RTTI_CLASS_SUFFIX, Naming.RTTI_FACTORY, Naming.rttiFactorySig(args.size())); } else { //find component for type and grab singleton RTTI String typeName = NamingCzar.jvmTypeDesc(t, this.ifNone, false); mv.visitFieldInsn(Opcodes.GETSTATIC, typeName + Naming.RTTI_CLASS_SUFFIX, Naming.RTTI_SINGLETON, Naming.RTTI_CONTAINER_DESC); } } else { throw new CompilerError("Expected Trait Type for RTTI generation"); } } //checks if a type is generic by looking for VarTypes in the type private boolean isGeneric(Type t) { if (t instanceof VarType) { return true; } else if (t instanceof TraitType) { List<StaticArg> args = ((TraitType) t).getArgs(); if (args != null && args.size() > 0) { for (StaticArg arg : args) { if (arg instanceof TypeArg) { if (isGeneric(((TypeArg) arg).getTypeArg())) return true; } else { throw new CompilerError("Expecting a TypeArg when checking genericity"); } } return false; //no parts were generic } else return false; } else return false; } //checks that the two RTTI on the top of the stack are in a subtyping //relationship. If they are, it leave the more general one on //the stack. If they aren't it removes both and continues execution //at the lookahead label. private void joinStackNoUnion(MethodVisitor mv, Label lookahead) { Label try2 = new Label(); Label next = new Label(); Label cleanup = new Label(); mv.visitInsn(Opcodes.DUP2); //#2 <: #1 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, try2); //if no, try opposite mv.visitInsn(Opcodes.POP); // want #1 (lower on the stack) mv.visitJumpInsn(Opcodes.GOTO, next); //done mv.visitLabel(try2); mv.visitInsn(Opcodes.SWAP); mv.visitInsn(Opcodes.DUP2); // #1 <: #2 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Naming.RTTI_CONTAINER_TYPE, Naming.RTTI_SUBTYPE_METHOD_NAME, Naming.RTTI_SUBTYPE_METHOD_SIG); mv.visitJumpInsn(Opcodes.IFEQ, cleanup); //if no, fail dispatch mv.visitInsn(Opcodes.POP); // want #2 (now lower on the stack) mv.visitJumpInsn(Opcodes.GOTO, next); //done mv.visitLabel(cleanup); mv.visitInsn(Opcodes.POP2); mv.visitJumpInsn(Opcodes.GOTO, lookahead); mv.visitLabel(next); } protected void loadThisForMethods(MethodVisitor mv) { // default does nothing } /** * Invoke f (it's a forwarding call) with casts inserted as necessary. * * @param mv * @param firstArgIndex * @param f */ private void generateLeafCall(MethodVisitor mv, int firstArgIndex, TaggedFunctionName f) { String sig = jvmSignatureFor(f); int i = firstArgIndex; List<Param> params = f.getParameters(); for (Param p : params) { mv.visitVarInsn(Opcodes.ALOAD, i); TypeOrPattern ty = p.getIdType().unwrap(); if (!(ty instanceof Type)) throw new CompilerError("Type is expected: " + ty); InstantiatingClassloader.generalizedCastTo(mv, NamingCzar.jvmBoxedTypeName((Type) ty, ifNone)); // mv.visitTypeInsn(Opcodes.CHECKCAST, NamingCzar.jvmBoxedTypeDesc((Type)ty, ifNone)); i++; } if (CodeGenerationPhase.debugOverloading >= 3) System.err.println("Emitting call " + f.tagF + sig); invokeParticularMethod(mv, f, sig); mv.visitInsn(Opcodes.ARETURN); } /** * @param f * @return */ String jvmSignatureFor(TaggedFunctionName f) { return NamingCzar.jvmSignatureFor(f.tagF, f.tagA); } /** * @param fu * @return */ protected List<StaticParam> staticParametersOf(Functional fu) { List<StaticParam> params = null; if (fu instanceof FunctionalMethod) { List<StaticParam> ltsp = ((FunctionalMethod) fu).traitStaticParameters(); List<StaticParam> lfsp = ((FunctionalMethod) fu).staticParameters(); List<StaticParam> lsp = Useful.concat(ltsp, lfsp); if (lsp.size() > 0) params = lsp; } else if (fu instanceof DeclaredFunction) { DeclaredFunction df = (DeclaredFunction) fu; List<StaticParam> lsp = df.staticParameters(); if (lsp.size() > 0) params = lsp; } else if (fu instanceof DeclaredMethod) { List<StaticParam> lsp = fu.staticParameters(); if (lsp.size() > 0) params = lsp; } else if (fu instanceof FieldGetterOrSetterMethod) { List<StaticParam> lsp = fu.staticParameters(); if (lsp.size() > 0) params = lsp; } else if (fu instanceof Constructor) { throw new CompilerError("Unimplemented arm of jvm signature " + fu); } else { throw new CompilerError("Unexpected subtype of FunctionalMethod " + fu); } return params; } public String toString() { if (lessSpecificThanSoFar.size() == 1) { return lessSpecificThanSoFar.iterator().next().toString(); } if (splitDone) { return toStringR(""); } else { return lessSpecificThanSoFar.toString(); } } private String toStringR(String indent) { { String s = indent; int l = specificDispatchOrder.length; for (int i = 0; i < l; i++) { TaggedFunctionName f = specificDispatchOrder[i]; s += f.toString() + "\n"; } return s; } } /** * Overloaded functions get a slightly mangled name. * * @param name * @return */ public static String oMangle(String name) { return name; // no mangling after all. } public void generateAnOverloadDefinition(String _name, CodeGenClassWriter cv) { // System.err.println("Generating "+ name + "\n" + // " principalMember "+ principalMember + "\n" + this.toStringR(" ")); String filtered_name = chooseName(_name, NodeUtil.nameSuffixString(name)); generateAnOverloadDefinitionInner(filtered_name, cv); for (Map.Entry<String, OverloadSet> o_entry : getOverloadSubsets().entrySet()) { String ss = o_entry.getKey(); OverloadSet sos = o_entry.getValue(); if (sos != this) { sos.generateAnOverloadDefinitionInner(filtered_name, cv); } } } protected String chooseName(String callers_name, String nameSuffixString) { return callers_name; } protected void generateAnOverloadDefinitionInner(String _name, CodeGenClassWriter cv) { if (principalMember == null) return; // "(" anOverloadedArg^N ")" returnType // Not sure what to do with return type. String signature = getSignature(); String[] exceptions = getExceptions(); // Right now we extract any necessary static arguments from // the principalMember. This may turn out to be wrong, but it // ought to be the case that principalMember is always set to // the statically most applicable method for the given set of // overloadings under consideration. [At present this does // not always appear to be the case in practice.] List<StaticParam> sargs = null; /* Some overloads have static args, but even if they are supplied, * runtime inference is still necessary (so I think) -- drc 2010-02-24 * * Not quite so -- if each static parameter is mentioned in any * invariant occurrence, then the supplied type is exact. * Runtime inference is only necessary when the occurrences are * ALL in variant context. Static analysis can also supply * no-more-precise-than information that it learns from context. * If no-less and no-more precise information are equal, then * again, dynamic inference is not needed. */ if (principalMember != null) { sargs = staticParametersOf(principalMember.tagF); } if (CodeGenerationPhase.debugOverloading >= 2) System.err.println("Emitting overload " + _name + signature); String PCNOuter = null; Naming.XlationData xldata = null; String overloaded_name = oMangle(_name); ArrayList<InitializedStaticField> isf_list = new ArrayList<InitializedStaticField>(); if (sargs != null) { // Map<String, String> xlation = new HashMap<String, String>(); xldata = CodeGen.xlationData(Naming.FUNCTION_GENERIC_TAG); String sparamsType = NamingCzar.genericDecoration(sargs, xldata, ifNone); // TODO: which signature is which? One needs to not have generics info in it. genericSchema = NamingCzar.makeArrowDescriptor(ifNone, overloadedDomain(), getRange()); /* Save this for later, to forestall collisions with * single functions that hit the generic type. * * Question: is this a problem with references to single * types within the overload itself? I think it might be, if we * do not use exactly the same handshake. */ // temporary fix to problems with type analysis. Check to make sure // that we don't generate a generic function class that was already // generated by our parent // A more complete fix would delve into the type checker // see bug PROJECTFORTRESS-19 (not_working_library_tests/MaybeTest2.fss) if (parent != null && parent.genericSchema.equals(genericSchema)) return; //prevent duplication String packageAndClassName = NamingCzar.javaPackageClassForApi(ifNone); // If we have static arguments, then our caller must be // invoking us by instantiating a closure class and then // calling its apply method. Thus we need to make sure // that we generate the expected closure class rather than // a top-level method. String PCN = Naming.genericFunctionPkgClass(packageAndClassName, _name, sparamsType, genericSchema); PCNOuter = Naming.genericFunctionPkgClass(packageAndClassName, _name, Naming.LEFT_OXFORD + Naming.RIGHT_OXFORD, genericSchema); // System.err.println("Looks generic.\n signature " + signature + // "\n gArrType " + genericArrowType + // "\n sparamsType " + sparamsType + // "\n PCN " + PCN + // "\n PCNOuter " + PCNOuter); cv = new CodeGenClassWriter(ClassWriter.COMPUTE_FRAMES, cv); overloaded_name = InstantiatingClassloader.closureClassPrefix(PCN, cv, PCN, signature, null, isf_list); } MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, // access, overloaded_name, // name, signature, // sp.getFortressifiedSignature(), null, // signature, // depends on generics, I think exceptions); // exceptions); generateBody(mv, 0); if (PCNOuter != null) { InstantiatingClassloader.optionalStaticsAndClassInitForTO(isf_list, cv); cv.dumpClass(PCNOuter, xldata); } } protected void generateBody(MethodVisitor mv, int one_if_method_closure) { mv.visitCode(); // Label fail = new Label(); generateCall(mv, firstArg(), one_if_method_closure); // Guts of overloaded method // Emit failure case // mv.visitLabel(fail); // // Boilerplate for throwing an error. // // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitMethodInsn(Opcodes.INVOKESTATIC, NamingCzar.miscCodegen, NamingCzar.matchFailure, NamingCzar.errorReturn); mv.visitInsn(Opcodes.ATHROW); mv.visitMaxs(getParamCount(), getParamCount()); // autocomputed mv.visitEnd(); } public BATree<String, OverloadSet> getOverloadSubsets() { return overloadSubsets; } public Set<String> getOtherKeys() { return otherOverloadKeys; } protected int firstArg() { return 0; } static public class Local extends AmongApis { public Local(final APIName apiname, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<Functional> defs, int n) { super(apiname, name, ta, Useful.applyToAll(defs, new F<Functional, TaggedFunctionName>() { @Override public TaggedFunctionName apply(Functional f) { return new TaggedFunctionName(apiname, f); } }), n); } } static public abstract class Factory { abstract public OverloadSet make(final APIName apiname, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<Functional> defs, int n); } static public final Factory localFactory = new Factory() { @Override public OverloadSet make(APIName apiname, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<Functional> defs, int n) { return new Local(apiname, name, ta, defs, n); } }; static public class TraitOrObjectFactory extends Factory { final int invokeOpcode; final ClassNameBundle cnb; final CodeGen cg; public TraitOrObjectFactory(int invoke_opcode, ClassNameBundle cnb, CodeGen cg) { this.invokeOpcode = invoke_opcode; this.cnb = cnb; this.cg = cg; } @Override public OverloadSet make(APIName apiname, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<Functional> defs, int n) { Functional one_func = defs.iterator().next(); int self_index = Naming.NO_SELF; if (one_func instanceof FunctionalMethod) { self_index = ((FunctionalMethod) one_func).selfPosition(); String new_name = Naming.fmDottedName(NodeUtil.nameSuffixString(name), self_index); if (name instanceof Id) name = NodeFactory.makeId((Id) name, new_name); else if (name instanceof Op) name = NodeFactory.makeOp((Op) name, new_name); else throw new CompilerError("Was not expecting an overloaded anonymous name."); } return new ForTraitOrObject(apiname, name, ta, defs, null, n, self_index, cnb, invokeOpcode, cg); } }; // TODO if it is a functional method, need to whack the name, too. static public class ForTraitOrObject extends AmongApis { /** * Indicates, in the Functional representation of the methods, * which parameter will not be present. */ final int selfIndex; final int invokeOpcode; final ClassNameBundle cnb; final CodeGen cg; ForTraitOrObject(final APIName apiname, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<Functional> defs, OverloadSet parent, int n, int self_index, ClassNameBundle cnb, int invoke_opcode, CodeGen cg) { super(apiname, name, ta, Useful.applyToAll(defs, new F<Functional, TaggedFunctionName>() { @Override public TaggedFunctionName apply(Functional f) { return new TaggedFunctionName(apiname, f); } }), n); selfIndex = self_index; this.cnb = cnb; this.invokeOpcode = invoke_opcode; this.cg = cg; } ForTraitOrObject(APIName ifNone, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<TaggedFunctionName> childLSTSF, OverloadSet parent, int paramCount, boolean this_disambiguates_the_erasure, int self_index, ClassNameBundle cnb, int invoke_opcode, CodeGen cg) { super(ifNone, name, ta, childLSTSF, paramCount); selfIndex = self_index; this.cnb = cnb; this.invokeOpcode = invoke_opcode; this.cg = cg; } protected OverloadSet makeSubset(Set<TaggedFunctionName> childLSTSF, TaggedFunctionName _principalMember, OverloadSet parent) { OverloadSet subset = new ForTraitOrObject(ifNone, name, ta, childLSTSF, parent, paramCount, true, selfIndex, cnb, invokeOpcode, cg); subset.principalMember = _principalMember; subset.otherOverloadKeys = parent.otherOverloadKeys; return subset; } /** * Only load if this is not a functional method * (selfIndex is not NO_SELF) */ protected void loadThisForMethods(MethodVisitor mv) { //if (selfIndex == Naming.NO_SELF) mv.visitVarInsn(Opcodes.ALOAD, 0); } protected String chooseName(String callers_name, String nameSuffixString) { return nameSuffixString; } protected int firstArg() { return selfIndex() == Naming.NO_SELF ? 1 : 0; } @Override protected int selfIndex() { return this.selfIndex; } /** * Emit the invocation for a particular type of overloaded functions. * * @param mv * @param f * @param sig */ protected void invokeParticularMethod(MethodVisitor mv, TaggedFunctionName f, String sig) { List<StaticParam> sargs = staticParametersOf(f.tagF); String sparamsType = ""; String genericArrowType = ""; //String ownerName = NamingCzar.apiAndMethodToMethodOwner(f.tagA, f.tagF); String mname = NodeUtil.nameSuffixString(name); // NamingCzar.apiAndMethodToMethod(f.tagA, f.tagF); // this ought to work better here. // Why is this not "mname" instead of name.stringName? if (getOverloadSubsets().containsKey(mname + sig)) { mname = NamingCzar.mangleAwayFromOverload(mname); } if (sargs != null) { genericArrowType = NamingCzar.makeArrowDescriptor(ifNone, oa.getDomainType(f.tagF), oa.getRangeType(f.tagF)); sparamsType = NamingCzar.genericDecoration(sargs, null, ifNone); // ownerName = // Naming.genericFunctionPkgClass(ownerName, mname, // sparamsType, genericArrowType); mname = Naming.APPLIED_METHOD; } mv.visitMethodInsn(invokeOpcode, cnb.className, mname, sig); } String jvmSignatureFor(TaggedFunctionName f) { // TODO need to skip selfIndex in f. return NamingCzar.jvmSignatureFor(f.tagF, selfIndex, f.tagA); } /* * See super implementation for comparison and comments. * * @param _name * @param cv */ protected void generateAnOverloadDefinitionInner(String _name, CodeGenClassWriter cv) { // "(" anOverloadedArg^N ")" returnType // Not sure what to do with return type. String signature = getSignature(selfIndex); String[] exceptions = getExceptions(); List<StaticParam> sargs = null; String PCNOuter = null; Naming.XlationData xldata = null; String overloaded_name = oMangle(_name); if (principalMember != null) { sargs = staticParametersOf(principalMember.tagF); } if (CodeGenerationPhase.debugOverloading >= 2) System.err.println("Emitting overloaded method " + _name + signature); if (sargs != null) { // Not yet prepared for overloaded GENERIC methods! Span span = NodeUtil.getSpan(name); List<Type> t1 = overloadedDomain(); Type domain = t1.size() == 1 ? t1.get(0) : NodeFactory.makeTupleType(t1); // DRC why not fnni = new FnNameInfo(principalMember.tagF, cg.thisApi())? // could be a name problem Functional eff = principalMember.tagF; Functional eff_original = principalMember.tagF; if (eff instanceof Method) eff_original = ((Method) eff).originalMethod(); FnNameInfo fnni = new FnNameInfo(eff_original, cg.thisApi()); FnNameInfo fnni_closure = new FnNameInfo(eff, cg.thisApi()); // FnNameInfo fnni = new FnNameInfo(sargs, // ((HasTraitStaticParameters)(principalMember.tagF)).traitStaticParameters(), // getRange(), domain, cg.thisApi(), (IdOrOp) name, span ); fnni_closure = fnni_closure.convertGenericMethodToClosureDecl(selfIndex, cg.currentTraitObjectDecl.getHeader().getStaticParams()); CodeGen.GenericMethodBodyMaker gmbm = new CodeGen.GenericMethodBodyMaker() { @Override public void generateGenericMethodBody(CodeGen cg, int selfIndex, String applied_method, String modified_sig) { // TODO Auto-generated method stub MethodVisitor mv = cg.getCw().visitCGMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, applied_method, modified_sig, null, null); generateBody(mv, 1); } }; String TO_method_name = cnb.stemClassName + Naming.UP_INDEX + overloaded_name; Id gfid = NodeFactory.makeId(span, TO_method_name); boolean in_a_trait = invokeOpcode == Opcodes.INVOKEINTERFACE; String selfType = in_a_trait ? cg.traitOrObjectName + NamingCzar.springBoard : cg.traitOrObjectName; String method_name = NamingCzar.genericMethodName(fnni, selfIndex, cg.thisApi()); String table_name = Naming.cacheTableName(method_name); //closure table uses unmangled generic method name otherOverloadKeys.add(method_name); String template_class_name = cg.generateGenericFunctionClass(fnni_closure, gmbm, gfid, selfIndex, cg.traitOrObjectName); cg.generateGenericMethodClosureFinder(method_name, table_name, template_class_name, selfType, in_a_trait); // throw new CompilerError("Not yet ready for overloaded generic methods:\n>> " + principalMember + "\n" + this); } else { ArrayList<InitializedStaticField> isf_list = new ArrayList<InitializedStaticField>(); MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, // access, overloaded_name, // name, signature, // sp.getFortressifiedSignature(), null, // signature, // depends on generics, I think exceptions); // exceptions); generateBody(mv, 0); if (PCNOuter != null) { InstantiatingClassloader.optionalStaticsAndClassInitForTO(isf_list, cv); cv.dumpClass(PCNOuter, xldata); } } } /** * @param fu * @return */ protected List<StaticParam> staticParametersOf(Functional fu) { // unlike top-level functional methods only "have" their own // static parameters (i.e., trait static parameters are ignored). List<StaticParam> params = fu.staticParameters(); if (params.size() == 0) params = null; // List<StaticParam> params = null; // if (fu instanceof FunctionalMethod) { // params = ((FunctionalMethod) fu).staticParameters(); // if (params.size() == 0) // params = null; // } else { // params = super.staticParametersOf(fu); // } return params; } /** * table name for a generic method of a trait is the unmangled generic method name * with a table suffix */ @Override protected String generateClosureTableName(TaggedFunctionName f) { String tableName = NamingCzar.genericMethodName(new FnNameInfo(f.tagF, f.tagA), ((Method) f.tagF).selfPosition(), f.tagA); return Naming.cacheTableName(tableName); } /** * Table owner is the springboard (DefaultTraitMethods) for the declaring trait or object * of the functional in provided TaggedFunctionName */ @Override protected String generateClosureTableOwner(TaggedFunctionName f) { FnNameInfo info = new FnNameInfo(f.tagF, f.tagA); Method m = (Method) f.tagF; Id declaringTO = m.declaringTrait(); ClassNameBundle cnb = this.cg.new_ClassNameBundle(declaringTO, info.getTrait_static_params(), null); return m.declarerIsObject() ? cnb.className : (cnb.className + NamingCzar.springBoard); } /** * version for methods - for right now it is a copy of the original with extra logic * to add the self type parameter to the disambiguating arrow schema for generic methods. */ // @Override // protected String compute_overload_name(TaggedFunctionName f) { // // String filtered_name = chooseName(name.stringName(), NodeUtil.nameSuffixString(name)); // static_parameters = staticParametersOf(f.tagF); // if (static_parameters != null) { // xldata = CodeGen.xlationData(Naming.FUNCTION_GENERIC_TAG); // String sparamsType = NamingCzar.genericDecoration(static_parameters, xldata, ifNone); // // genericSchema = instanceArrowSchema(f); // String packageAndClassName = NamingCzar.javaPackageClassForApi(ifNone); // String PCNOuter = // PCNOuter??? // Naming.genericFunctionPkgClass(packageAndClassName, filtered_name, // // Naming.makeTemplateSParams(sparamsType), // sparamsType, // genericSchema); // return PCNOuter; // } // return name.stringName()+jvmSignatureFor(f); // } /** * generates the arrow schema for the tagged function * in this case, it will be a method and so the self-type * will be added as a generic parameter */ @Override protected String instanceArrowSchema(TaggedFunctionName f) { //DONE previously now //NOTE selfIndex should be NO_SELF since this is a (non-functional) method overload set //FnDecl new_fndecl = cg.convertGenericMethodToClosureDecl(((FnDecl) ((Method)f.tagF).ast()), selfIndex, f.tagF.getSpan()); FnNameInfo methodName = new FnNameInfo(f.tagF, f.tagA); //KBN - this will not work correctly for functional methods //consider refactoring the function in a better way // the two possibilities seem to not be very similar ArrowType methodArrow = methodName.functionArrowType(); String arrow = NamingCzar.jvmTypeDesc(methodArrow, f.tagA, false); return arrow; } /** * * since this is a method, the name includes the trait and functional * name separated by the up finger character * * @param f * @return */ @Override protected String functionName(TaggedFunctionName f) { Method m = (Method) f.tagF; String methodName = NamingCzar.idOrOpToString(m.name()); String traitName = NamingCzar.idOrOpToString(m.declaringTrait()); return traitName + Naming.UP_INDEX + methodName; } // /** // * // * f will be a (nonfunctional) method so the this parameter // * is appended to the start of the parameter list // * // * @param f // * @return // */ // @Override // protected int functionParamNum(TaggedFunctionName f) { // return f.getParameters().size() + 1; // } /** * if the function is generic, then * converts into a top-level function with self as the first * parameter * * @param f * @return */ @Override protected TaggedFunctionName getFunctionToCall(TaggedFunctionName f) { if (f.tagF.staticParameters().size() > 0) { //NOTE selfIndex should be NO_SELF since this is a (non-functional) method overload set // Also know that f.tagF will be a DeclaredMethod FnDecl new_fndecl = cg.convertGenericMethodToClosureDecl(((FnDecl) ((Method) f.tagF).ast()), selfIndex, f.tagF.getSpan()); Functional new_functional = new DeclaredMethod(new_fndecl, (DeclaredMethod) f.tagF); return new TaggedFunctionName(f.tagA, new_functional); } else return f; } } static public class AmongApis extends OverloadSet { /** * Emit the invocation for a particular type of overloaded functions. * * @param mv * @param f * @param sig */ protected void invokeParticularMethod(MethodVisitor mv, TaggedFunctionName f, String sig) { List<StaticParam> sargs = staticParametersOf(f.tagF); String ownerName = NamingCzar.apiAndMethodToMethodOwner(f.tagA, f.tagF); String mname = NamingCzar.apiAndMethodToMethod(f.tagA, f.tagF); // this ought to work better here. if (sargs != null) { String trial_owner_name = closureClassNameForGenericOverloadArm(f, sargs, ownerName, mname); if (getOverloadSubsets().containsKey(trial_owner_name)) { trial_owner_name = closureClassNameForGenericOverloadArm(f, sargs, ownerName, NamingCzar.mangleAwayFromOverload(mname)); } mname = Naming.APPLIED_METHOD; mv.visitMethodInsn(Opcodes.INVOKESTATIC, trial_owner_name, mname, sig); } else { if (getOverloadSubsets().containsKey(name.stringName() + sig)) { mname = NamingCzar.mangleAwayFromOverload(mname); } mv.visitMethodInsn(Opcodes.INVOKESTATIC, ownerName, mname, sig); } } /** * @param f * @param sargs * @param ownerName * @param mname * @return */ public String closureClassNameForGenericOverloadArm(TaggedFunctionName f, List<StaticParam> sargs, String ownerName, String mname) { String genericArrowType = NamingCzar.makeArrowDescriptor(ifNone, oa.getDomainType(f.tagF), oa.getRangeType(f.tagF)); String sparamsType = NamingCzar.genericDecoration(sargs, null, ifNone); ownerName = Naming.genericFunctionPkgClass(ownerName, mname, sparamsType, genericArrowType); return ownerName; } /* Boilerplate follows, because this is a subtype. */ protected AmongApis(APIName ifNone, IdOrOpOrAnonymousName name, TypeAnalyzer ta, OverloadingOracle oa, Set<TaggedFunctionName> lessSpecificThanSoFar, OverloadSet parent, int paramCount) { super(ifNone, name, ta, oa, lessSpecificThanSoFar, parent, paramCount); } public AmongApis(APIName ifNone, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<TaggedFunctionName> defs, int n) { super(name, ta, new OverloadingOracle(ta), defs, n, ifNone); } protected AmongApis(APIName ifNone, IdOrOpOrAnonymousName name, TypeAnalyzer ta, Set<TaggedFunctionName> defs, OverloadSet parent, int n) { super(ifNone, name, ta, new OverloadingOracle(ta), defs, parent, n); } protected OverloadSet makeSubset(Set<TaggedFunctionName> childLSTSF, TaggedFunctionName _principalMember, OverloadSet parent) { OverloadSet subset = new AmongApis(ifNone, name, ta, new OverloadingOracle(ta), childLSTSF, parent, paramCount); subset.principalMember = _principalMember; subset.otherOverloadKeys = parent.otherOverloadKeys; return subset; } public int hashCode() { return super.hashCode(); } public boolean equals(Object o) { if (o == null) return false; if ((o.getClass() != this.getClass()) || (o.hashCode() != this.hashCode())) { return false; } else { return super.equals(o); } } } public boolean notGeneric() { for (TaggedFunctionName tfn : lessSpecificThanSoFar) { Functional f = tfn.getF(); List<StaticParam> lsp = staticParametersOf(f); if (lsp.size() > 0) return false; } return true; } }