/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * (C) Copyright IBM Corporation 2006-2010. */ package x10cpp.visit; import static x10cpp.visit.SharedVarsMethods.chevrons; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import polyglot.ast.Expr; import polyglot.types.Context; import polyglot.types.LocalInstance; import polyglot.types.Type; import polyglot.types.Types; import polyglot.util.CodeWriter; import polyglot.util.Position; import x10.types.ParameterType; import x10.types.X10ClassDef; import x10.types.X10ClassType; import x10.types.X10MethodDef; import x10.types.MethodInstance; import polyglot.types.TypeSystem; import x10cpp.types.X10CPPContext_c; /** * A class to encapsulate details of how the methods * of an interface are mapped to itable entries and * various code-generation details of how itables * are implemented. */ public final class ITable { private final X10ClassType interfaceType; private final MethodInstance[] methods; private final boolean hasOverloadedMethods; private final boolean[] overloaded; /** * Construct the ITable instance for the given X10 interface type * @param interfaceType The interface for which to build the ITable */ public ITable(X10ClassType interfaceType) { assert interfaceType.flags().isInterface() : "Cannot create an ITable for a non-interface type"; this.interfaceType = interfaceType; methods = collectMethods(interfaceType); Arrays.sort(methods, new MethodComparator()); boolean foundOverload = false; overloaded = new boolean[methods.length]; for (int i=1; i<methods.length; i++) { boolean ol = methods[i-1].name().toString().equals(methods[i].name().toString()); if (ol) { overloaded[i-1] = true; overloaded[i] = true; foundOverload = true; } } hasOverloadedMethods = foundOverload; } private static MethodInstance[] collectMethods(X10ClassType interfaceType) { TypeSystem xts = (TypeSystem)interfaceType.typeSystem(); ArrayList<MethodInstance> uniqueMethods = new ArrayList<MethodInstance>(); uniqueMethods.addAll(interfaceType.methods()); for (X10ClassType superInterface : ((TypeSystem)interfaceType.typeSystem()).allImplementedInterfaces(interfaceType)) { for (MethodInstance newMethod : superInterface.methods()) { boolean duplicate = false; for (MethodInstance oldMethod : uniqueMethods) { if (MethodComparator.compareTo(oldMethod, newMethod) == 0) { duplicate = true; break; } } if (!duplicate) { uniqueMethods.add(newMethod); } } } return uniqueMethods.toArray(new MethodInstance[uniqueMethods.size()]); } /** * @return the canonically ordered array of MethodDecls that * are included in the itable for this interface. This is * a set union of the methods directly declared by the interface and * methods inherited from its superinterfaces. */ public MethodInstance[] getMethods() { return methods; } /** * @return the interfaceType that this itable is being used to implement. */ public X10ClassType getInterface() { return interfaceType; } /** * @return true if the ITable contains two methods with the same name * that are statically overloaded. If true, then when declaring the * iTable we will need to do additional name mangling to disambiguate * the C++ names used for the elements of the ITable. */ public boolean hasOverloadedMethods() { return hasOverloadedMethods; } /** * @return true if the ITable contains no methods at all, false otherwise. */ public boolean isEmpty() { return methods.length == 0; } public String mangledName(MethodInstance meth) { if (hasOverloadedMethods) { for (int i=0; i<methods.length; i++) { if (MethodComparator.compareTo(methods[i], meth) == 0) { if (overloaded[i]) { return "_m"+i+"__"+Emitter.mangled_method_name(meth.name().toString()); } else { return Emitter.mangled_method_name(meth.name().toString()); } } } assert false : "Method "+meth+" is not a member of interface "+interfaceType; return Emitter.mangled_method_name(meth.name().toString()); } else { return Emitter.mangled_method_name(meth.name().toString()); } } public void emitFunctionPointerDecl(CodeWriter cw, Emitter emitter, MethodInstance meth, String memberPtr, boolean includeName) { MethodInstance mi = (MethodInstance) meth; X10MethodDef md = mi.x10Def(); Type rootReturnType = emitter.findRootMethodReturnType(md, null, mi); String returnType = Emitter.translateType(rootReturnType, true); String name = mangledName(meth); cw.write(returnType+" ("+memberPtr+"::*"+(includeName ? name : "")+") ("); boolean first = true; for (Type f : meth.formalTypes()) { if (!first) cw.write(", "); cw.write(Emitter.translateType(f, true)); first = false; } cw.write(")"); } public void emitITableDecl(X10ClassType cls, int itableNum, Emitter emitter, CodeWriter h) { String interfaceCType = Emitter.translateType(interfaceType, false); X10ClassDef cd = cls.x10Def(); boolean doubleTemplate = cd.typeParameters().size() > 0 && interfaceType.x10Def().typeParameters().size() > 0; h.write("static "+(doubleTemplate ? "typename ":"")+interfaceCType+ (doubleTemplate ? "::template itable<":"::itable<")+Emitter.translateType(cls, false)+" > _itable_"+itableNum+";"); h.newline(); } public void emitITableInitialization(X10ClassType cls, int itableNum, MessagePassingCodeGenerator cg, CodeWriter h, CodeWriter sw) { X10ClassDef cd = cls.x10Def(); if (cls.isX10Struct()) { // For an interface implemented by a struct, we need to generate // an additional thunk class and itable for use by the IBox of the // struct. String interfaceCType = Emitter.translateType(interfaceType, false); String clsCType = Emitter.translateType(cls, false); String thunkBaseType = Emitter.mangled_non_method_name(cd.name().toString()); String thunkParams = ""; if (cd.typeParameters().size() != 0) { String args = ""; int s = cd.typeParameters().size(); for (Type t: cd.typeParameters()) { args += Emitter.translateType(t, true); // type arguments are always translated as refs if (--s > 0) args +=", "; } thunkParams = chevrons(args); } boolean doubleTemplate = cd.typeParameters().size() > 0 && interfaceType.x10Def().typeParameters().size() > 0; if (cd.package_() != null) { Emitter.openNamespaces(sw, cd.package_().get().fullName()); sw.newline(); } String thunkType = thunkBaseType + "_ibox"+itableNum; String parentCType = "x10::lang::IBox"+chevrons(clsCType); String recvArg = "this->value"; cg.emitter.printTemplateSignature(cd.typeParameters(), sw); sw.write("class "+thunkType+" : public "+parentCType+" {"); sw.newline(); sw.write("public:"); sw.newline(4); sw.begin(0); sw.write("static "+(doubleTemplate ? "typename ":"")+interfaceCType+ (doubleTemplate ? "::template itable<":"::itable<")+thunkType+thunkParams+" > itable;"); sw.newline(); for (MethodInstance meth : methods) { sw.write(Emitter.translateType(meth.returnType(), true)); sw.write(" "); sw.write(Emitter.mangled_method_name(meth.name().toString())); sw.write("("); boolean first = true; int argNum=0; for (Type f : meth.formalTypes()) { if (!first) sw.write(", "); sw.write(Emitter.translateType(f, true)+" arg"+(argNum++)); first = false; } sw.write(") {"); sw.newline(4); sw.begin(0); if (!meth.returnType().isVoid()) sw.write("return "); sw.write(recvArg+"->"+Emitter.mangled_method_name(meth.name().toString())+"("); boolean firstArg = true; for (int j=0; j<meth.formalTypes().size(); j++) { sw.write((firstArg ? "arg": ", arg")+j); firstArg = false; } sw.write(")"); sw.write(";"); sw.end(); sw.newline(); sw.write("}"); sw.newline(); } sw.end(); sw.newline(); sw.write("};"); sw.newline(); cg.emitter.printTemplateSignature(cd.typeParameters(), sw); sw.write((doubleTemplate ? "typename " : "")+interfaceCType+(doubleTemplate ? "::template itable<" : "::itable<")+ thunkType+thunkParams+" > "+" "+thunkType+thunkParams+"::itable"); if (!isEmpty()) { int methodNum = 0; sw.write("("); for (MethodInstance meth : methods) { if (methodNum > 0) sw.write(", "); sw.write("&"+thunkType+thunkParams+"::"+Emitter.mangled_method_name(meth.name().toString())); methodNum++; } sw.write(")"); } sw.write(";"); sw.newline(); if (cd.package_() != null) { Emitter.closeNamespaces(sw, cd.package_().get().fullName()); sw.newline(); } } String interfaceCType = Emitter.translateType(interfaceType, false); String clsCType = Emitter.translateType(cls, false); boolean doubleTemplate = cd.typeParameters().size() > 0 && interfaceType.x10Def().typeParameters().size() > 0; cg.emitter.printTemplateSignature(cd.typeParameters(), sw); sw.write((doubleTemplate ? "typename " : "")+interfaceCType+(doubleTemplate ? "::template itable<" : "::itable<")+ Emitter.translateType(cls, false)+" > "+" "+clsCType+"::_itable_"+itableNum+""); if (!isEmpty()) { int methodNum = 0; sw.write("("); for (MethodInstance meth : methods) { if (methodNum > 0) sw.write(", "); sw.write("&"+clsCType+"::"+Emitter.mangled_method_name(meth.name().toString())); methodNum++; } sw.write(")"); } sw.write(";"); sw.newline(); } /** * Helper class to impose a canonical ordering on the methods of an interface. */ private static final class MethodComparator implements Comparator<MethodInstance> { public int compare(MethodInstance m1, MethodInstance m2) { return compareTo(m1, m2); } public static int compareTo(MethodInstance m1, MethodInstance m2) { int nameCompare = m1.name().toString().compareTo(m2.name().toString()); if (nameCompare != 0) return nameCompare; // Statically overloaded method. Order by comparing function signatures. List<Type> m1Formals = m1.formalTypes(); List<Type> m2Formals = m2.formalTypes(); if (m1Formals.size() < m2Formals.size()) return -1; if (m1Formals.size() > m2Formals.size()) return 1; // Have same number of formal parameters; impose arbitrary order via toString of formals // NOTE: Exploiting X10 2.0 semantics that methods can't be overloaded based on constraints. // If that is changed in the future, we will have to fix this code. Iterator<Type> i1 = m1Formals.iterator(); Iterator<Type> i2 = m2Formals.iterator(); while (i1.hasNext()) { Type f1 = Types.baseType(i1.next()); Type f2 = Types.baseType(i2.next()); int fcompare = f1.toString().compareTo(f2.toString()); if (fcompare != 0) return fcompare; } // X10 allows covariant return types, but not overloading based on return type. // Therefore we ignore return type in comparing methods and if we get to this point the // methods are considered to be equal. return 0; } } }