package de.dhbwstuttgart.syntaxtree; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.function.Function; import de.dhbwstuttgart.typeinference.Menge; import java.util.stream.Stream; import de.dhbwstuttgart.logger.Logger; import de.dhbwstuttgart.logger.Section; import de.dhbwstuttgart.core.MyCompiler; import de.dhbwstuttgart.parser.JavaClassName; import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory; import de.dhbwstuttgart.syntaxtree.modifier.Modifiers; import de.dhbwstuttgart.syntaxtree.modifier.Public; import de.dhbwstuttgart.syntaxtree.type.FunN; import de.dhbwstuttgart.syntaxtree.type.GenericTypeVar; import de.dhbwstuttgart.syntaxtree.type.RefType; import de.dhbwstuttgart.syntaxtree.type.Type; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.Void; import de.dhbwstuttgart.typeinference.ByteCodeResult; import de.dhbwstuttgart.typeinference.ConstraintsSet; import de.dhbwstuttgart.typeinference.FunNInterface; import de.dhbwstuttgart.typeinference.Pair; import de.dhbwstuttgart.typeinference.ResultSet; import de.dhbwstuttgart.typeinference.TypeinferenceResultSet; import de.dhbwstuttgart.typeinference.TypeinferenceResults; import de.dhbwstuttgart.typeinference.UnifyConstraintsSet; import de.dhbwstuttgart.typeinference.assumptions.ClassAssumption; import de.dhbwstuttgart.typeinference.assumptions.TypeAssumptions; import de.dhbwstuttgart.typeinference.exceptions.DebugException; import de.dhbwstuttgart.typeinference.exceptions.TypeinferenceException; import de.dhbwstuttgart.typeinference.unify.TypeUnify; import de.dhbwstuttgart.typeinference.unify.Unifikationsalgorithmus; import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure; import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType; import de.dhbwstuttgart.typeinference.unify.model.UnifyPair; public class SourceFile extends SyntaxTreeNode { protected static Logger codegenlog = Logger.getLogger("codegen"); protected static Logger inferencelog = Logger.getLogger("inference"); private String pkgName; public List<Class> KlassenVektor = new Menge<Class>(); private List<JavaClassName> imports; /** * Die SourceFile repräsntiert eine zu einem Syntaxbaum eingelesene Java-Datei. * SourceFile stellt dabei den Wurzelknoten des Syntaxbaumes dar. */ public SourceFile(List<Class> classDefinitions) { this.KlassenVektor = classDefinitions; } /** * PL 2014-10-25 * schnitt1 checkt ob die Typeplaceholders aus in den Elemeneten aus vars enthalten sind * Rückgabe ist die Menge der Indizies von vars der Schnittmengen mit var nicht leer sind. * @param var * @param vars * @param indexe * @return */ static Menge<Integer> schnitt1 (Menge<PlaceholderType> var, Menge<Menge<PlaceholderType>> vars, Menge<Integer> indexe) { int j = -1; for (Menge<PlaceholderType> varelems : vars) { j++; if (varelems != null) { if (var.stream().map(x -> varelems.contains(x)).reduce(false, (a,b) -> (a || b)) && (!indexe.contains(j))) { Menge<PlaceholderType> rekvarelements = vars.elementAt(j); vars.setElementAt(null, j);//Element erledigt muss nicht nochmals bearbeitet werden. indexe.addElement(j); indexe = schnitt1(rekvarelements, vars, indexe); } } } return indexe; } /** * Bildet Schnittmengen der Mengen von Typeplaceholders * Rueckgabe ist die Menge der Menge von Indizies die Schnittmengen sind. * @param vars * @return */ public static Menge<Menge<Integer>> schnitt (Menge<Menge<PlaceholderType>> vars) { Menge<Menge<Integer>> ret = new Menge<>(); int i = -1; for (Menge<PlaceholderType> var : vars) { i++; if (var != null) {//Element wurde noch bearbeitet Menge<Integer> indexe = new Menge<>(); indexe.add(i); ret.add(schnitt1(var, vars, indexe)); } } return ret; } public static Set<Set<UnifyPair>> cartesianProduct(List<UnifyPair> constraints, FiniteClosure finiteClosure){ //IDEE: Man bildet Zusammenhangskomponenten von Paaren, die gemeinsame Variablen haben // und unifizert nur die Zusammenhangskomponenten in Schritten 1 - 5 //Schritt 1: Alle Variablen in den Paaren von Elementen einsammeln Menge<Menge<PlaceholderType>> constraintsclonevars = constraints.stream().map(p -> {Menge<PlaceholderType> TPHs = new Menge<>(); TPHs.addAll(p.getInvolvedPlaceholderTypes()); TPHs.addAll(p.getInvolvedPlaceholderTypes()); return TPHs;} ).collect(Menge::new, Menge::add, Menge::addAll); //Schritt 2: Schnittmengen jedes Elements mit jedem Elememt von vars bilden und dann index zusammenfassen //in indexset sind dann die Mengen von Indizes enthalten, die gemeisam unifiziert wreden müssen Menge<Menge<Integer>> indexeset = new Menge<>(); if (constraintsclonevars != null && constraintsclonevars.size()>0) { indexeset = SourceFile.schnitt(constraintsclonevars); } //Schritt 3: Umwandlung der Indizes in die zugehoerigen Elemente // In streamconstraintsclone sind die Mengen von Paar enthalten die unifiziert werden muessen Stream<Menge<UnifyPair>> streamconstraintsclone = indexeset.stream().<Menge<UnifyPair>>map(x -> x.stream() .<UnifyPair>map(i -> constraints.get(i)) .<Menge<UnifyPair>>collect(Menge::new, Menge::add, Menge::addAll)); //Menge<Menge<Pair>> vecconstraintsclone = streamconstraintsclone.collect(Menge::new, Menge::add, Menge::addAll); //System.out.println(); //Schritt 4: Unifikation Menge<Set<Set<UnifyPair>>> vecunifyResult = //streamconstraintsclone.map(x -> Unify.unify(x, finiteClosure)).collect(Menge::new, Menge::add, Menge::addAll); //DEBUG-Variante streamconstraintsclone.map(x -> { Set<Set<UnifyPair>> z = new TypeUnify().unify(x, finiteClosure); return z; } ).collect(Menge::new, Menge::add, Menge::addAll); //card gibt die Cardinalitaet der unifizierten Mengen an Menge<Integer> card = vecunifyResult.stream().map(x -> x.size()).collect(Menge::new, Menge::add, Menge::addAll); ;//.reduce(1,(a,b) -> { if ((a > 0) && (b > 0)) return (a * b); else return 1; }); //Schritt 5: Bildung des cartesischen Produkts //sollte wieder entfernt werden: Weiterarbeit mit: //[[x_1 -> t_1, x_2 -> t2], [x_1 -> t'_1, x_2 -> t'_2]] x ... x [[x_n -> t_1n], [x_n -> t2n], [x_n -> t3n]] Set<Set<UnifyPair>> cardprodret_start = new Menge<>(); cardprodret_start.add(new Menge<UnifyPair>()); //cart. Produkt mit Linkverschiebung Set<Set<UnifyPair>> unifyResult = vecunifyResult.stream().reduce(cardprodret_start, (x, y) -> { Set<Set<UnifyPair>> cardprodret= new Menge<>(); if (y.size() > 0) { //System.out.println(y); //Menge<Menge<Pair>> cardprodretold = x; //cardprodret = new Menge<>(); for(Set<UnifyPair> xElement : x) { for (Set<UnifyPair> yElement : y){ Set<UnifyPair> help = new Menge<>(); help.addAll(yElement); help.addAll(xElement); cardprodret.add(help); } } } else return new Menge<>(); //kein unifiziertes Ergebnis, damit wird das Geseamtergebnis [] return cardprodret; }); return unifyResult; } ///////////////////////////////////////////////////////////////////////// // TypeReconstructionAlgorithmus ///////////////////////////////////////////////////////////////////////// /** * Tyrekonstruktionsalgorithmus: ruft f�r jede Klasse den Algorithmus TRProg auf. * Dessen Ergebnismenge A, die Menge aller Typannahmen, f�r eine Klasse dient als * Eingabe f�r TRProg der n�chsten Klasse. Am Ende enth�lt A alle m�glichen * Typkombinationen f�r alle Klassen zusammen. * <br>Author: J�rg B�uerle * @return Liste aller m�glichen Typkombinationen * @throws CTypeReconstructionException Wenn was schief l�uft */ public Menge<TypeinferenceResultSet> typeReconstruction(TypeAssumptions globalAssumptions) { Menge<TypeinferenceResultSet> ret = new Menge<TypeinferenceResultSet>(); //Logger initialisieren: Logger typinferenzLog = Logger.getLogger("Typeinference"); //Alle Assumptions für diese SourceFile sammeln: for(Class klasse : this.KlassenVektor){ globalAssumptions.add(klasse.getPublicFieldAssumptions()); } //Assumptions der importierten Klassen sammeln: TypeAssumptions importAssumptions = this.makeBasicAssumptionsFromJRE(imports, true); globalAssumptions.add(importAssumptions); typinferenzLog.debug("Von JRE erstellte Assumptions: "+importAssumptions, Section.TYPEINFERENCE); ConstraintsSet oderConstraints = new ConstraintsSet(); //Alle Constraints der in dieser SourceFile enthaltenen Klassen sammeln: for(Class klasse : KlassenVektor){ oderConstraints.add(klasse.typeReconstruction(globalAssumptions)); } /*//////////////// * Paare in MPairs umwandeln * (Wird zunächst mal weggelassen. Constraints werden erst beim Unifizieren umgewandelt *///////////////// //UnifyTypeFactory.convert(oderConstraints); //FiniteClosure generieren: FiniteClosure finiteClosure = UnifyTypeFactory.generateFC(globalAssumptions); typinferenzLog.debug("FiniteClosure: \n"+finiteClosure, Section.TYPEINFERENCE); //////////////// //Typen in UnifyTypen umwandeln: //////////////// UnifyConstraintsSet unifyConstraints = UnifyTypeFactory.convert(oderConstraints); //Unmögliche ConstraintsSets aussortieren durch Unifizierung Unifikationsalgorithmus unifier = (pairs)->new TypeUnify().unify(pairs, finiteClosure); unifyConstraints.filterWrongConstraints(unifier); //unifyConstraints.unifyUndConstraints(unifier); //rausgeworfen für Tests (08.12.2015) typinferenzLog.debug("Ãbriggebliebene Konstraints:\n"+oderConstraints+"\n", Section.TYPEINFERENCE); typinferenzLog.debug("Ãbriggebliebene Konvertierte Konstraints:\n"+unifyConstraints+"\n", Section.TYPEINFERENCE); //////////////// //Karthesisches Produkt bilden: //////////////// Set<Set<UnifyPair>> xConstraints = unifyConstraints.cartesianProduct(); //Sets zu Listen umwandeln: //Set<List<UnifyPair>> allUnifiedConstraints = xConstraints.stream().map((set)-> new ArrayList<>(set)).collect(Menge::new, Menge::add, Menge::addAll);; typinferenzLog.debug("Finite Closure: "+finiteClosure, Section.TYPEINFERENCE); typinferenzLog.debug("Karthesisches Produkt der Constraints: "+xConstraints, Section.TYPEINFERENCE); //finiteClosure.generateFullyNamedTypes(globalAssumptions); ////////////////////////////// // Unifizierung der Constraints: ////////////////////////////// boolean unifyFail = true; for(Set<UnifyPair> constraints : xConstraints){ //Alle durch das Karthesische Produkt entstandenen Möglichkeiten durchgehen: typinferenzLog.debug("\nUnifiziere Constraints:\n"+constraints, Section.TYPEINFERENCE); typinferenzLog.debug("\nFC:\n"+finiteClosure, Section.TYPEINFERENCE); long start = System.currentTimeMillis(); Set<Set<UnifyPair>> unifyResult = new TypeUnify().unify(constraints, finiteClosure); long time = System.currentTimeMillis()-start; typinferenzLog.debug("\nErgebnis der Unifizierung:\n"+unifyResult, Section.TYPEINFERENCE); typinferenzLog.debug("\nAnzahl Lösungen:\n"+unifyResult.size(), Section.TYPEINFERENCE); //typinferenzLog.debug("\nZeit für Unifizierung: "+time + "ms", Section.TYPEINFERENCE); Menge<Menge<Pair>> convertedResult = unifyResult.parallelStream().<Menge<Pair>>map((Set<UnifyPair> resultSet)->{ Menge<Pair> innerConvert = resultSet.stream().map((UnifyPair mp)->UnifyTypeFactory.convert(mp)) .collect(Menge<Pair>::new, Menge::add, Menge::addAll); return innerConvert; }).collect(Menge::new, Menge::add, Menge::addAll); Menge<Pair> convertedConstraints = constraints.stream().map( (UnifyPair mp)->{return UnifyTypeFactory.convert(mp);} ).collect(Menge<Pair>::new, Menge::add, Menge::addAll); //Dann den Ergebnissen anfügen typinferenzLog.debug("\nErgebnis der Unifizierung (Konvertiert):\n"+convertedResult, Section.TYPEINFERENCE); //result.addAll(convertedResult); typinferenzLog.debug("\nJavaFiles:\n", Section.TYPEINFERENCE); //typinferenzLog.debug(this.printJavaCode(new ResultSet(new Menge<Pair>()))); //Für jede Klasse in diesem SourceFile gilt das selbe ResultSet: for(Class klasse : this.KlassenVektor){ //Der Unifikationsalgorithmus kann wiederum auch mehrere Lösungen errechnen, diese werden im folgenden durchlaufen: for(Menge<Pair> resultSet : convertedResult){ unifyFail = false; //Ein Unifiziertes Ergebnis ist entstanden (es kann auch leer sein, das bedeutet nur, dass die Constraints mindestens in einem Fall Sinn ergaben) //Add Result set as a new ReconstructionResult to ret: TypeinferenceResultSet reconstructionResult = new TypeinferenceResultSet(klasse, convertedConstraints, new ResultSet(resultSet)); ret.add(reconstructionResult); //ResultSet res = new ResultSet(resultSet); typinferenzLog.debug("JavaFile für ResultSet "+reconstructionResult+"\n", Section.TYPEINFERENCE); typinferenzLog.debug(klasse.printJavaCode(reconstructionResult), Section.TYPEINFERENCE); } } } if(unifyFail){ if(!this.KlassenVektor.isEmpty())throw new TypeinferenceException("Fehler in Typinferierung", this.KlassenVektor.firstElement()); } return ret; } /** * Erstellt die Assumptions der standardmäÃig importierten Packages (java.lang.) sowie der von imports übergebenen Klassen zusammen. * @param imports * @param withSuptypes - Gibt an, ob auch die subklassen der Packages den Assumptions angefügt werden sollen. * @return * TODO: Diese Methode neu erstellen */ public TypeAssumptions makeBasicAssumptionsFromJRE(List<JavaClassName> imports, boolean withSubtypes) // ino.end // ino.method.makeBasicAssumptionsFromJRE.21409.body { return null; } // ino.end private Class getSuperClassOfJREClass(java.lang.Class<?> x, TypeAssumptions ass) { Class ret; java.lang.Class s = x.getSuperclass(); if(s == null){ return new Class("java.lang.Object",new Modifiers(), 0); } Menge<String> supertypeGenPara = new Menge<>();//Die Generischen Parameter für die Superklasse berechnen: java.lang.reflect.TypeVariable[] superclassTVS=s.getTypeParameters(); for(int tvi=0;tvi<superclassTVS.length;tvi++){ supertypeGenPara.addElement(superclassTVS[tvi].getName()); } Class ss = this.getSuperClassOfJREClass(s, ass); ret = new Class(s.getName(),ss.getType(),new Modifiers(),supertypeGenPara); ass.addClassAssumption(new ClassAssumption(ss)); //Die beiden SuperKlassen den Assumptions anfügen... ass.addClassAssumption(new ClassAssumption(ret)); return ret; } // ino.method.isBaseType.21412.definition private boolean isBaseType(String type) // ino.end // ino.method.isBaseType.21412.body { return baseTypeTranslationTable.containsValue(type); } // ino.end /*Die contains Methode des Menges vergleicht bei Strings nicht korrekt, * da zwei Strings mit dem gleichen Inhalt unterschiedliche Instanzen sind. * Deshalb diese Methode 07-01-20 luar*/ private boolean containsString(Menge<UsedId> searchMenge, String searchString) { boolean found = false; for(UsedId id : searchMenge) { String s = id.getQualifiedName().toString(); found |= s.equals(searchString); } return found; } // ino.method.createTypeFromJavaGenericType.21415.definition private Type createTypeFromJavaGenericType(java.lang.reflect.Type type, java.lang.Class<?> cl, Hashtable<String,GenericTypeVar>jreSpiderRegistry, Class parentClass) // ino.end // ino.method.createTypeFromJavaGenericType.21415.body { /* auskommentiert, da die Klassen von Sun in der Open JDK 1.8 nicht unterstützt werden. if(type instanceof TypeVariableImpl){ TypeVariableImpl tvi=((TypeVariableImpl)type); return(new GenericTypeVar(jreSpiderRegistry.get(tvi.getName()).getName().toString(),parentClass,-1)); }else{ */ GenericTypeVar gtv = jreSpiderRegistry.get(type.getTypeName()); if(gtv != null)return gtv; //new GenericTypeVar(jreSpiderRegistry.get(type.getTypeName()).getName().toString(),parentClass,-1)); //String jccNameForClass=baseTypeTranslationTable.get(cl.getSimpleName()); String jccNameForClass=baseTypeTranslationTable.get(cl.getName()); if(cl.getSimpleName().equalsIgnoreCase("void")){ return(new Void(parentClass,-1)); }else if(jccNameForClass!=null){ RefType rt=new RefType(jccNameForClass,parentClass,-1); rt.setPrimitiveFlag(true); return(rt); }else{ //return(new RefType(cl.getSimpleName())); return(new RefType(cl.getName(),parentClass,-1)); } //} } // ino.method.getPackageName.21427.defdescription type=javadoc /** * Erzeugt f�r jede Klasse einen Menge, in den Referenzen auf die GenericTypeVars * dieser Klasse gespeichert werden. Diese Mengeen werden unter den Klassennamen * in der * Ergebnisdatenstruktur abgelegt. Au�erdem werden alle Klassennamen gespeichert. * <br/>Author: J�rg B�uerle * @param res * / * /*private void addClassNamesAndGenericsToRR(CTypeReconstructionResult res){ * Iterator<Class> it = this.getClassIterator(); * while(it.hasNext()){ * Class cl = it.next(); * res.addClassName(cl.get_classname()); * Menge<GenericTypeVar> genericsList = new Menge<GenericTypeVar>(); * * for(int i =0; i<cl.get_ParaList().size(); i++){ * Type para = (Type)cl.get_ParaList().elementAt(i); * if(para instanceof GenericTypeVar){ * genericsList.addElement((GenericTypeVar)para); * } * } * res.addGenericTypeVars(cl.get_classname(), genericsList); * } * } */ // ino.end // ino.method.getPackageName.21427.definition public UsedId getPackageName() // ino.end // ino.method.getPackageName.21427.body { return pkgName; } // ino.end // ino.method.setPackageName.21430.definition public void setPackageName(UsedId pkgName) // ino.end // ino.method.setPackageName.21430.body { this.pkgName = pkgName; // Die Package-Namen fuer alle Klassen und Interfaces // im Source-File nachziehen for (int i=0; i<KlassenVektor.size(); i++) { KlassenVektor.elementAt(i).setPackageName(pkgName); } } // ino.end public ImportDeclarations getImports() { return(imports); } // ino.method.getClassIterator.21439.definition public Iterator<Class> getClassIterator() // ino.end // ino.method.getClassIterator.21439.body { return KlassenVektor.iterator(); } // ino.end // ino.method.getInterfaceIterator.21442.definition public Iterator<Interface> getInterfaceIterator() // ino.end // ino.method.getInterfaceIterator.21442.body { return InterfaceVektor.iterator(); } // ino.end @Override public void parserPostProcessing(SyntaxTreeNode parent) { if(parent!=null)throw new DebugException("Eine SourceFile hat kein Elternelement im Syntaxbaum"); super.parserPostProcessing(this); //for(SyntaxTreeNode node : this.getChildren())node.parserPostProcessing(this); } @Override public SyntaxTreeNode getParent() { return null; } @Override public Menge<SyntaxTreeNode> getChildren() { Menge<SyntaxTreeNode> ret = new Menge<SyntaxTreeNode>(); for(Class cl : this.KlassenVektor){ ret.add(cl); } return ret; } /** * SourceFile stellt eine geparste Java-Datei dar. Mit dieser Methode wird der Name der eingelesenen Datei gesetzt. * @param filename - Der Name der eingelesenen JavaDatei */ @Deprecated public void setFileName(String filename) { //this.filename = filename; } @Override public int getOffset() { // TODO Auto-generated method stub return 0; } @Override public int getVariableLength() { // TODO Auto-generated method stub return 0; } /** * Bytecode generieren für das resultSet * @return */ public Menge<ByteCodeResult> generateBytecode(TypeinferenceResults results) { Menge<ByteCodeResult> ret = new Menge<>(); for(Class cl : this.KlassenVektor){ ret.add(cl.genByteCode(results)); } //Alle FunN Klassen erzeugen: for(ClassAssumption funNAss : MyCompiler.makeFunNAssumptions().getClassAssumptions()){ ret.add(funNAss.getAssumedClass().genByteCode(results)); } /* //Add all FunN Interfaces for(Pair ucons : results.getUnifiedConstraints()){ for(Type t : ucons.getTypes()){ List<Class> xClasses = t.isClassFromJavaX(); for(Class xClass : xClasses){ ByteCodeResult bC = xClass.genByteCode(results); if(! ret.contains(bC))ret.add(bC); } } } */ return ret; } } // ino.end