From 268056542b7e3ac368cf9057ceecd679c3a0d62d Mon Sep 17 00:00:00 2001 From: Fayez Abu Alia Date: Wed, 10 Jan 2018 11:36:29 +0100 Subject: [PATCH] =?UTF-8?q?erzeugt=20bytecode=20f=C3=BCr=20generics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dhbwstuttgart/bytecode/BytecodeGen.java | 122 ++++++++++++-- .../bytecode/BytecodeGenMethod.java | 47 ++++-- .../bytecode/DescriptorToString.java | 73 ++++++++- .../bytecode/MethodFromMethodCall.java | 29 +++- .../bytecode/NormalConstructor.java | 21 ++- .../dhbwstuttgart/bytecode/NormalMethod.java | 28 +++- src/de/dhbwstuttgart/bytecode/Signature.java | 155 ++++++++++++++++++ .../dhbwstuttgart/bytecode/TypeToString.java | 38 +++++ test/bytecode/Generics.jav | 2 + test/bytecode/Generics2.jav | 6 + test/bytecode/Generics2Test.java | 7 + test/bytecode/GenericsTest.java | 7 + test/bytecode/InterfaceTest.java | 7 + test/bytecode/JavaTXCompilerTest.java | 36 ++-- test/bytecode/LamAssign.jav | 4 + test/bytecode/LamAssignTest.java | 7 + 16 files changed, 526 insertions(+), 63 deletions(-) create mode 100644 src/de/dhbwstuttgart/bytecode/Signature.java create mode 100644 src/de/dhbwstuttgart/bytecode/TypeToString.java create mode 100644 test/bytecode/Generics2.jav create mode 100644 test/bytecode/Generics2Test.java create mode 100644 test/bytecode/GenericsTest.java create mode 100644 test/bytecode/InterfaceTest.java create mode 100644 test/bytecode/LamAssignTest.java diff --git a/src/de/dhbwstuttgart/bytecode/BytecodeGen.java b/src/de/dhbwstuttgart/bytecode/BytecodeGen.java index 1b6ef47b..f1994c20 100644 --- a/src/de/dhbwstuttgart/bytecode/BytecodeGen.java +++ b/src/de/dhbwstuttgart/bytecode/BytecodeGen.java @@ -1,5 +1,6 @@ package de.dhbwstuttgart.bytecode; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -8,6 +9,9 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.signature.SignatureVisitor; +import org.objectweb.asm.signature.SignatureWriter; import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal; import de.dhbwstuttgart.syntaxtree.*; @@ -16,6 +20,7 @@ import de.dhbwstuttgart.syntaxtree.statement.literal.Null; import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType; import de.dhbwstuttgart.syntaxtree.type.GenericRefType; import de.dhbwstuttgart.syntaxtree.type.RefType; +import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.typeinference.result.ResultSet; @@ -23,7 +28,6 @@ import de.dhbwstuttgart.typeinference.result.ResultSet; public class BytecodeGen implements ASTVisitor { ClassWriter cw =new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); -// String methDesc; String type; @@ -34,6 +38,12 @@ public class BytecodeGen implements ASTVisitor { // stores parameter, local vars and the next index on the local variable table, which use for aload_i, astore_i,... HashMap paramsAndLocals = new HashMap<>(); + // stores generics and their bounds of class + HashMap genericsAndBounds = new HashMap<>(); + // stores generics and their bounds of method + HashMap genericsAndBoundsMethod = new HashMap<>(); + + HashMap methodParamsAndTypes = new HashMap<>(); byte[] bytecode; HashMap classFiles; @@ -45,14 +55,19 @@ public class BytecodeGen implements ASTVisitor { @Override public void visit(SourceFile sourceFile) { for(ClassOrInterface cl : sourceFile.getClasses()) { - isInterface = (cl.getModifiers()&512)==512; - System.out.println("IS Interface = "+"modifiers= "+cl.getModifiers()+" ->"+(cl.getModifiers()&512) + isInterface); BytecodeGen classGen = new BytecodeGen(classFiles, resultSet); cl.accept(classGen); + System.out.println("In CLASS: "+(cl.getClassName().toString())); classGen.writeClass(cl.getClassName().toString()); } } - + + /** + * Associates the bytecode of the class that was build with the classWriter {@link #cw} + * with the class name in the map {@link #classFiles} + * + * @param name name of the class with which the the bytecode is to be associated + */ private void writeClass(String name) { bytecode = cw.toByteArray(); classFiles.put(name, bytecode); @@ -62,12 +77,30 @@ public class BytecodeGen implements ASTVisitor { public HashMap getClassFiles() { return classFiles; } + + @Override public void visit(ClassOrInterface classOrInterface) { className = classOrInterface.getClassName().toString(); - // access flages?? - cw.visit(Opcodes.V1_8, classOrInterface.getModifiers()+Opcodes.ACC_SUPER, classOrInterface.getClassName().toString() - , null, classOrInterface.getSuperClass().toString().replace(".", "/"), null); + + isInterface = (classOrInterface.getModifiers()&512)==512; + System.out.println("IS Interface = "+"modifiers= "+classOrInterface.getModifiers()+" ->"+(classOrInterface.getModifiers()&512) + isInterface); + + int acc = isInterface?classOrInterface.getModifiers()+Opcodes.ACC_ABSTRACT:classOrInterface.getModifiers()+Opcodes.ACC_SUPER; + String sig = null; + /* if class has generics then creates signature + * Signature looks like: + * Superclass + */ + if(classOrInterface.getGenerics().iterator().hasNext()) { + Signature signature = new Signature(classOrInterface, genericsAndBounds); + + System.out.println(signature.toString()); + sig = signature.toString(); + } + // needs implemented Interfaces? + cw.visit(Opcodes.V1_8, acc, classOrInterface.getClassName().toString() + , sig, classOrInterface.getSuperClass().acceptTV(new TypeToDescriptor()), null); // for each field in the class for(Field f : classOrInterface.getFieldDecl()) { @@ -85,17 +118,34 @@ public class BytecodeGen implements ASTVisitor { } cw.visitSource(classOrInterface.getClassName().toString()+".jav", null); } - + @Override public void visit(Constructor field) { - NormalConstructor constructor = new NormalConstructor(field); - String desc = constructor.accept(new DescriptorToString(resultSet)); - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", desc, null, null); + field.getParameterList().accept(this); + + String desc = null; + boolean hasGen = false; + for(String paramName : methodParamsAndTypes.keySet()) { + genericsAndBounds.containsKey(paramName); + hasGen = true; + } + String sig = null; + if(hasGen) { + System.out.println("IM IN CONST HAS Gens"); + Signature signature = new Signature(field, genericsAndBounds,methodParamsAndTypes); + sig = signature.toString(); + System.out.println(sig); + } + NormalConstructor constructor = new NormalConstructor(field,genericsAndBounds,hasGen); + desc = constructor.accept(new DescriptorToString(resultSet)); + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", desc, sig, null); mv.visitCode(); System.out.println("-----Constructor-----"); - BytecodeGenMethod gen = new BytecodeGenMethod(className,resultSet,field, mv,paramsAndLocals,desc,cw,isInterface); - - mv.visitInsn(Opcodes.RETURN); + BytecodeGenMethod gen = new BytecodeGenMethod(className,resultSet,field, mv,paramsAndLocals,cw, + genericsAndBoundsMethod,genericsAndBounds,isInterface); + if(!field.getParameterList().iterator().hasNext()) { + mv.visitInsn(Opcodes.RETURN); + } mv.visitMaxs(0, 0); mv.visitEnd(); } @@ -106,14 +156,48 @@ public class BytecodeGen implements ASTVisitor { // else it will be stored in pos 1 and this will be stored in pos 0 method.getParameterList().accept(this); - NormalMethod meth = new NormalMethod(method); - String methDesc = meth.accept(new DescriptorToString(resultSet)); + String methDesc = null; + + // Method getModifiers() ? + int acc = isInterface?Opcodes.ACC_ABSTRACT:0; System.out.println("-----Method-----"); - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), methDesc, null, null); + + boolean hasGenInParameterList = genericsAndBounds.containsKey(method.getReturnType().acceptTV(new TypeToDescriptor())); + if(!hasGenInParameterList) { + for(String paramName : methodParamsAndTypes.keySet()) { + if(genericsAndBounds.containsKey(paramName)) { + hasGenInParameterList = true; + break; + } + } + } + String sig = null; + boolean hasGen = method.getGenerics().iterator().hasNext() || hasGenInParameterList; + // Wenn ReturnType has Generics?? Fun1<...> wie testet man das generic hat?? + System.out.println(method.getReturnType().acceptTV(new TypeToString())); +// if(method.getReturnType().acceptTV(new TypeToString()).equals("TPH")) { +// Signature signature = new Signature(method, genericsAndBoundsMethod, methodParamsAndTypes,resultSet); +// sig = signature.toString(); +// System.out.println(sig); +// } + /* if method has generics, create signature */ + if(hasGen) { + // resultset hier zum testen + Signature signature = new Signature(method, genericsAndBoundsMethod, methodParamsAndTypes,resultSet); + sig = signature.toString(); + System.out.println(sig); + + } + + NormalMethod meth = new NormalMethod(method,genericsAndBounds,genericsAndBoundsMethod,hasGen); + methDesc = meth.accept(new DescriptorToString(resultSet)); + System.out.println("methDesc" + methDesc); + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC+acc, method.getName(), methDesc, sig, null); + mv.visitCode(); - BytecodeGenMethod gen = new BytecodeGenMethod(className,resultSet,method, mv,paramsAndLocals,methDesc,cw,isInterface); + BytecodeGenMethod gen = new BytecodeGenMethod(className,resultSet,method, mv,paramsAndLocals,cw,genericsAndBounds,genericsAndBounds,isInterface); mv.visitMaxs(0, 0); mv.visitEnd(); } @@ -121,11 +205,13 @@ public class BytecodeGen implements ASTVisitor { @Override public void visit(ParameterList formalParameters) { paramsAndLocals = new HashMap<>(); + methodParamsAndTypes = new HashMap<>(); Iterator itr = formalParameters.iterator(); int i = 1; while(itr.hasNext()) { FormalParameter fp = itr.next(); paramsAndLocals.put(fp.getName(), i); + methodParamsAndTypes.put(fp.getName(), fp.getType()); fp.accept(this); i++; } diff --git a/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java b/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java index ffdf1450..5368cf93 100644 --- a/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java +++ b/src/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java @@ -21,6 +21,7 @@ import de.dhbwstuttgart.syntaxtree.Method; import de.dhbwstuttgart.syntaxtree.StatementVisitor; import de.dhbwstuttgart.syntaxtree.statement.literal.Literal; import de.dhbwstuttgart.syntaxtree.statement.literal.Null; +import de.dhbwstuttgart.syntaxtree.type.FunN; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; import de.dhbwstuttgart.typeinference.result.ResultSet; @@ -29,12 +30,13 @@ public class BytecodeGenMethod implements StatementVisitor{ private Method m; private MethodVisitor mv; private HashMap paramsAndLocals = new HashMap<>(); - private String desc; private String className; private int lamCounter; private ClassWriter cw; private ResultSet resultSet; private boolean isInterface; + HashMap genericsAndBoundsMethod; + private HashMap genericsAndBounds; //for tests ** private String fieldName; @@ -46,8 +48,9 @@ public class BytecodeGenMethod implements StatementVisitor{ private ArrayList varsFunInterface; - public BytecodeGenMethod(String className,ResultSet resultSet, Method m, MethodVisitor mv, HashMap paramsAndLocals, - String desc, ClassWriter cw, boolean isInterface) { + public BytecodeGenMethod(String className,ResultSet resultSet, Method m, MethodVisitor mv, + HashMap paramsAndLocals, ClassWriter cw, + HashMap genericsAndBoundsMethod, HashMap genericsAndBounds, boolean isInterface) { this.where = "<<<<<< NORMAL METHOD >>>>>>"; @@ -56,29 +59,32 @@ public class BytecodeGenMethod implements StatementVisitor{ this.m = m; this.mv = mv; this.paramsAndLocals = paramsAndLocals; - this.desc = desc; this.cw = cw; + this.genericsAndBoundsMethod = genericsAndBoundsMethod; + this.genericsAndBounds = genericsAndBounds; this.isInterface = isInterface; this.lamCounter = -1; this.varsFunInterface = new ArrayList<>(); System.out.println("PARAMS = "+this.paramsAndLocals.size()); - this.m.block.accept(this); - System.out.println("PARAMS = "+this.paramsAndLocals.size()); - for(int i = 0; i(); @@ -114,7 +120,7 @@ public class BytecodeGenMethod implements StatementVisitor{ superCall.receiver.accept(this); superCall.arglist.accept(this); // mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", superCall.name, desc,false); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), superCall.name, desc,isInterface); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), superCall.name, "()V",isInterface); } // ?? @@ -151,9 +157,6 @@ public class BytecodeGenMethod implements StatementVisitor{ assign.rightSide.accept(this); assign.lefSide.accept(this); } - - - } @Override @@ -165,7 +168,9 @@ public class BytecodeGenMethod implements StatementVisitor{ public void visit(LambdaExpression lambdaExpression) { System.out.println("\n++ In Lambda: "); this.lamCounter++; - + + System.out.println("Lam Hs Gens: " + lambdaExpression.getGenerics().iterator().hasNext()); + System.out.println("Lam Hs Gens: " + lambdaExpression.getReturnType().acceptTV(new TypeToString())); Lambda lam = new Lambda(lambdaExpression); String lamDesc = lam.accept(new DescriptorToString(resultSet)); //Call site, which, when invoked, returns an instance of the functional interface to which @@ -177,7 +182,9 @@ public class BytecodeGenMethod implements StatementVisitor{ Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", mt.toMethodDescriptorString(), false); String methodName = "lambda$new$" + this.lamCounter; +// String typeErasure = "(Ljava/lang/Object;)Ljava/lang/Object;"; // Type erasure +// Type arg1 = Type.getMethodType(typeErasure); Type arg1 = Type.getMethodType(lamDesc); // real Type Type arg3 = Type.getMethodType(lamDesc); @@ -209,7 +216,7 @@ public class BytecodeGenMethod implements StatementVisitor{ MethodVisitor mvLambdaBody = cw.visitMethod(Opcodes.ACC_PRIVATE+ staticOrInstance + Opcodes.ACC_SYNTHETIC, methodName, arg3.toString(), null, null); - new BytecodeGenMethod(lambdaExpression,this.resultSet,mvLambdaBody,arg3.toString(),indexOfFirstParamLam,isInterface); + new BytecodeGenMethod(lambdaExpression,this.resultSet,mvLambdaBody,indexOfFirstParamLam,isInterface); mvLambdaBody.visitMaxs(0, 0); mvLambdaBody.visitEnd(); @@ -277,7 +284,8 @@ public class BytecodeGenMethod implements StatementVisitor{ methodCall.receiver.accept(this); methodCall.arglist.accept(this); - MethodFromMethodCall method = new MethodFromMethodCall(methodCall.arglist, methodCall.getType()); + MethodFromMethodCall method = new MethodFromMethodCall(methodCall.arglist, methodCall.getType(), + genericsAndBoundsMethod,genericsAndBounds); String mDesc = method.accept(new DescriptorToString(resultSet)); System.out.println("is Vars empty: "+varsFunInterface.isEmpty()); @@ -287,6 +295,7 @@ public class BytecodeGenMethod implements StatementVisitor{ mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, getResolvedType(methodCall.receiver.getType()), methodCall.name, mDesc, false); }else { + System.out.println("mDesc = " + mDesc); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getResolvedType(methodCall.receiver.getType()), methodCall.name, mDesc, isInterface); } @@ -397,6 +406,8 @@ public class BytecodeGenMethod implements StatementVisitor{ @Override public void visit(AssignToField assignLeftSide) { +// temporäre Lösung für testen, bis ich weiss wie man funktionale +// interfaces erkennt if(isRightSideALambda) varsFunInterface.add(assignLeftSide.field.getType()); // Loads the an object reference from the local variable diff --git a/src/de/dhbwstuttgart/bytecode/DescriptorToString.java b/src/de/dhbwstuttgart/bytecode/DescriptorToString.java index 044218e4..481dbc5d 100644 --- a/src/de/dhbwstuttgart/bytecode/DescriptorToString.java +++ b/src/de/dhbwstuttgart/bytecode/DescriptorToString.java @@ -30,9 +30,37 @@ public class DescriptorToString implements DescriptorVisitor{ Iterator itr = method.getParameterList().iterator(); while(itr.hasNext()) { FormalParameter fp = itr.next(); - desc = desc + "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + if(method.hasGen()) { + String fpDesc = fp.getType().acceptTV(new TypeToDescriptor()); + if(method.getGenericsAndBoundsMethod().containsKey(fpDesc)) { + desc += "L"+method.getGenericsAndBoundsMethod().get(fpDesc)+ ";"; + }else if(method.getGenericsAndBounds().containsKey(fpDesc)){ + desc += "L"+method.getGenericsAndBounds().get(fpDesc)+ ";"; + }else { + desc += "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } + }else { + desc += "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } } - desc = addReturnType(desc,method.getReturnType(), resultSet); + + if(resultSet.resolveType(method.getReturnType()).resolvedType.toString().equals("void")) { + desc += ")V"; + }else { + if(method.hasGen()) { + String ret = method.getReturnType().acceptTV(new TypeToDescriptor()); + if(method.getGenericsAndBoundsMethod().containsKey(ret)) { + desc += ")L"+method.getGenericsAndBoundsMethod().get(ret)+ ";"; + }else if(method.getGenericsAndBounds().containsKey(ret)){ + desc += ")L"+method.getGenericsAndBounds().get(ret)+ ";"; + }else { + desc += ")" + "L"+resultSet.resolveType(method.getReturnType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } + }else { + desc += ")" + "L"+resultSet.resolveType(method.getReturnType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } + } +// desc = addReturnType(desc,method.getReturnType(), resultSet); return desc; } @@ -42,7 +70,19 @@ public class DescriptorToString implements DescriptorVisitor{ Iterator itr = constructor.getParameterList().iterator(); while(itr.hasNext()) { FormalParameter fp = itr.next(); - desc = desc + "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + if(constructor.hasGen()) { + System.out.println("Cons has Gens"); + String fpDesc = fp.getType().acceptTV(new TypeToDescriptor()); + System.out.println(fpDesc); + if(constructor.getGenericsAndBounds().containsKey(fpDesc)){ + desc += "L"+constructor.getGenericsAndBounds().get(fpDesc)+ ";"; + }else { + desc += "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } + }else { + System.out.println("Cons has NOT Gens"); + desc += "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } } desc = desc + ")V"; return desc; @@ -56,6 +96,7 @@ public class DescriptorToString implements DescriptorVisitor{ FormalParameter fp = itr.next(); desc = desc + "L"+resultSet.resolveType(fp.getType()).resolvedType.acceptTV(new TypeToDescriptor()) + ";"; } + System.out.println("LamReturnType: "+lambdaExpression.getReturnType().acceptTV(new TypeToString())); desc = addReturnType(desc, lambdaExpression.getReturnType(), resultSet); return desc; } @@ -75,10 +116,30 @@ public class DescriptorToString implements DescriptorVisitor{ @Override public String visit(MethodFromMethodCall methodFromMethodCall) { String desc = "("; - for(Expression e : methodFromMethodCall.argList.getArguments()) { - desc = desc + "L"+resultSet.resolveType(e.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + for(Expression e : methodFromMethodCall.getArgList().getArguments()) { + String d = e.getType().acceptTV(new TypeToDescriptor()); + if(methodFromMethodCall.getGenericsAndBoundsMethod().containsKey(d)) { + desc += "L"+methodFromMethodCall.getGenericsAndBoundsMethod().get(d)+ ";"; + }else if(methodFromMethodCall.getGenericsAndBounds().containsKey(d)) { + desc += "L"+methodFromMethodCall.getGenericsAndBounds().get(d)+ ";"; + }else { + desc += "L"+resultSet.resolveType(e.getType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } } - desc = addReturnType(desc, methodFromMethodCall.returnType, resultSet); + + if(resultSet.resolveType(methodFromMethodCall.getReturnType()).resolvedType.toString().equals("void")) { + desc += ")V"; + }else { + String ret = resultSet.resolveType(methodFromMethodCall.getReturnType()).resolvedType.acceptTV(new TypeToDescriptor()); + if(methodFromMethodCall.getGenericsAndBoundsMethod().containsKey(ret)) { + desc += ")L"+methodFromMethodCall.getGenericsAndBoundsMethod().get(ret)+ ";"; + }else if(methodFromMethodCall.getGenericsAndBounds().containsKey(ret)){ + desc += ")L"+methodFromMethodCall.getGenericsAndBounds().get(ret)+ ";"; + }else { + desc += ")" + "L"+resultSet.resolveType(methodFromMethodCall.getReturnType()).resolvedType.acceptTV(new TypeToDescriptor())+ ";"; + } + } +// desc = addReturnType(desc, methodFromMethodCall.getReturnType(), resultSet); return desc; } diff --git a/src/de/dhbwstuttgart/bytecode/MethodFromMethodCall.java b/src/de/dhbwstuttgart/bytecode/MethodFromMethodCall.java index 034e2abf..330b0666 100644 --- a/src/de/dhbwstuttgart/bytecode/MethodFromMethodCall.java +++ b/src/de/dhbwstuttgart/bytecode/MethodFromMethodCall.java @@ -1,15 +1,38 @@ package de.dhbwstuttgart.bytecode; +import java.util.HashMap; + import de.dhbwstuttgart.syntaxtree.statement.ArgumentList; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; public class MethodFromMethodCall { - ArgumentList argList; - RefTypeOrTPHOrWildcardOrGeneric returnType; + private ArgumentList argList; + private RefTypeOrTPHOrWildcardOrGeneric returnType; + private HashMap genericsAndBoundsMethod; + private HashMap genericsAndBounds; - public MethodFromMethodCall(ArgumentList argList,RefTypeOrTPHOrWildcardOrGeneric returnType) { + public MethodFromMethodCall(ArgumentList argList,RefTypeOrTPHOrWildcardOrGeneric returnType, + HashMap genericsAndBoundsMethod,HashMap genericsAndBounds) { this.argList = argList; this.returnType = returnType; + this.genericsAndBoundsMethod = genericsAndBoundsMethod; + this.genericsAndBounds = genericsAndBounds; + } + + public ArgumentList getArgList() { + return argList; + } + + public RefTypeOrTPHOrWildcardOrGeneric getReturnType() { + return returnType; + } + + public HashMap getGenericsAndBoundsMethod(){ + return genericsAndBoundsMethod; + } + + public HashMap getGenericsAndBounds(){ + return genericsAndBounds; } public String accept(DescriptorVisitor descVisitor) { diff --git a/src/de/dhbwstuttgart/bytecode/NormalConstructor.java b/src/de/dhbwstuttgart/bytecode/NormalConstructor.java index d029d21e..d2174fd4 100644 --- a/src/de/dhbwstuttgart/bytecode/NormalConstructor.java +++ b/src/de/dhbwstuttgart/bytecode/NormalConstructor.java @@ -1,15 +1,34 @@ package de.dhbwstuttgart.bytecode; +import java.util.HashMap; + import de.dhbwstuttgart.syntaxtree.Constructor; import de.dhbwstuttgart.syntaxtree.ParameterList; public class NormalConstructor { private Constructor constructor; + private HashMap genericsAndBounds; + private boolean hasGenerics; - public NormalConstructor(Constructor constructor) { + public NormalConstructor(Constructor constructor, boolean hasGenerics) { this.constructor = constructor; + this.hasGenerics = hasGenerics; } + public NormalConstructor(Constructor constructor, HashMap genericsAndBounds, boolean hasGenerics) { + this.constructor = constructor; + this.genericsAndBounds = genericsAndBounds; + this.hasGenerics = hasGenerics; + } + + public HashMap getGenericsAndBounds() { + return genericsAndBounds; + } + + public boolean hasGen() { + return hasGenerics; + } + public ParameterList getParameterList() { return constructor.getParameterList(); } diff --git a/src/de/dhbwstuttgart/bytecode/NormalMethod.java b/src/de/dhbwstuttgart/bytecode/NormalMethod.java index 2dd81d78..3f6bf2b4 100644 --- a/src/de/dhbwstuttgart/bytecode/NormalMethod.java +++ b/src/de/dhbwstuttgart/bytecode/NormalMethod.java @@ -1,14 +1,28 @@ package de.dhbwstuttgart.bytecode; +import java.util.HashMap; + import de.dhbwstuttgart.syntaxtree.Method; import de.dhbwstuttgart.syntaxtree.ParameterList; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; public class NormalMethod { private Method method; + private HashMap genericsAndBounds; + private HashMap genericsAndBoundsMethod; + private boolean hasGenerics; - public NormalMethod(Method method) { + public NormalMethod(Method method, boolean hasGenerics) { this.method = method; + this.hasGenerics = hasGenerics; + } + + public NormalMethod(Method method, HashMap genericsAndBounds, + HashMap genericsAndBoundsMethod,boolean hasGenerics) { + this.method = method; + this.genericsAndBounds = genericsAndBounds; + this.genericsAndBoundsMethod = genericsAndBoundsMethod; + this.hasGenerics = hasGenerics; } public Method getMethod() { @@ -19,10 +33,22 @@ public class NormalMethod { return method.getParameterList(); } + public HashMap getGenericsAndBounds(){ + return genericsAndBounds; + } + + public HashMap getGenericsAndBoundsMethod(){ + return genericsAndBoundsMethod; + } + public RefTypeOrTPHOrWildcardOrGeneric getReturnType() { return method.getType(); } + public boolean hasGen() { + return this.hasGenerics; + } + public String accept(DescriptorVisitor descVisitor) { return descVisitor.visit(this); } diff --git a/src/de/dhbwstuttgart/bytecode/Signature.java b/src/de/dhbwstuttgart/bytecode/Signature.java new file mode 100644 index 00000000..362429a3 --- /dev/null +++ b/src/de/dhbwstuttgart/bytecode/Signature.java @@ -0,0 +1,155 @@ +package de.dhbwstuttgart.bytecode; + +import java.util.HashMap; +import java.util.Iterator; + +import org.objectweb.asm.signature.SignatureVisitor; +import org.objectweb.asm.signature.SignatureWriter; + +import de.dhbwstuttgart.syntaxtree.ClassOrInterface; +import de.dhbwstuttgart.syntaxtree.Constructor; +import de.dhbwstuttgart.syntaxtree.GenericTypeVar; +import de.dhbwstuttgart.syntaxtree.Method; +import de.dhbwstuttgart.syntaxtree.type.GenericRefType; +import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; +import de.dhbwstuttgart.typeinference.result.ResultSet; + +public class Signature { + private ClassOrInterface classOrInterface; + private HashMap genericsAndBounds; + private HashMap genericsAndBoundsMethod; + private SignatureWriter sw; + private Constructor constructor; + private Method method; + private HashMap methodParamsAndTypes; + private ResultSet resultSet; + + public Signature(ClassOrInterface classOrInterface, HashMap genericsAndBounds) { + this.classOrInterface = classOrInterface; + this.genericsAndBounds = genericsAndBounds; + sw = new SignatureWriter(); + createSignatureForClassOrInterface(); + } + + public Signature(Constructor constructor, HashMap genericsAndBounds, HashMap methodParamsAndTypes) { + this.constructor = constructor; + this.genericsAndBounds = genericsAndBounds; + this.methodParamsAndTypes = methodParamsAndTypes; + sw = new SignatureWriter(); + createSignatureForConsOrMethod(this.constructor,true); + } + + public Signature(Method method, HashMap genericsAndBoundsMethod, + HashMap methodParamsAndTypes, ResultSet resultSet) { + this.method = method; + this.genericsAndBoundsMethod = genericsAndBoundsMethod; + this.methodParamsAndTypes = methodParamsAndTypes; + this.resultSet = resultSet; + sw = new SignatureWriter(); + createSignatureForConsOrMethod(this.method,false); + } + + /** + * Creates signature for a method or constructor with @see {@link SignatureWriter} + * Signature looks like: + * (params L.. OR T.. Or basistape)ReturnType + * + * @param method method or constructor + * @param isConstructor true if constructor + */ + private void createSignatureForConsOrMethod(Method method, boolean isConstructor) { + Iterator itr = method.getGenerics().iterator(); + // visits all formal type parameter and visits their bounds + while(itr.hasNext()) { + GenericTypeVar g = itr.next(); + getBoundsOfTypeVar(g,genericsAndBoundsMethod); + } + // visits each method-parameter to create the signature + for(String paramName : methodParamsAndTypes.keySet()) { + RefTypeOrTPHOrWildcardOrGeneric t = methodParamsAndTypes.get(paramName); + // parameter type deswegen ist true + doVisitParamsOrReturn(t,true); + } + if(isConstructor) { + sw.visitReturnType().visitBaseType('V'); + }else { + RefTypeOrTPHOrWildcardOrGeneric returnType = method.getReturnType(); + // return type deswegen ist false + doVisitParamsOrReturn(returnType, false); + } +// sw.visitEnd(); + } + /** + * Visits parameter type or return type with {@link SignatureVisitor} to create + * the method signature + * @param t type of parameter or return type + * @param isParameterType true if t is type of parameter + */ + private void doVisitParamsOrReturn(RefTypeOrTPHOrWildcardOrGeneric t, boolean isParameterType) { + String type = t.acceptTV(new TypeToString()); + SignatureVisitor sv; + if(isParameterType) { + sv = sw.visitParameterType(); + } + else { + sv = sw.visitReturnType(); + } + switch (type) { + case "RT": + sv.visitClassType(t.acceptTV(new TypeToDescriptor())); + break; + case "GRT": + GenericRefType g = (GenericRefType) t; + sv.visitTypeVariable(g.getParsedName()); + break; + case "TPH": + System.out.println(resultSet.resolveType(t).resolvedType.acceptTV(new TypeToDescriptor())); +// sv.visitInterface().visitClassType(resultSet.resolveType(t).resolvedType.acceptTV(new TypeToDescriptor())+";"); + + break; + default: + if(!isParameterType) + sv.visitBaseType('V'); + break; + } + } + /** + * Creates signature for class or interface with {@link SignatureWriter} + * Signature looks like: + * superclass + */ + private void createSignatureForClassOrInterface() { + Iterator itr = classOrInterface.getGenerics().iterator(); + + while(itr.hasNext()) { + GenericTypeVar g = itr.next(); + getBoundsOfTypeVar(g,genericsAndBounds); + } + + sw.visitSuperclass().visitClassType(classOrInterface.getSuperClass().acceptTV(new TypeToDescriptor()));; + sw.visitEnd(); + } + /** + * Get bounds of type variable + * @param g type variable + * @param genAndBounds + */ + private void getBoundsOfTypeVar(GenericTypeVar g, HashMap genAndBounds) { + sw.visitFormalTypeParameter(g.getParsedName()); + + Iterator bItr = g.getBounds().iterator(); + while(bItr.hasNext()) { + RefTypeOrTPHOrWildcardOrGeneric b =bItr.next(); + String boundDesc = b.acceptTV(new TypeToDescriptor()); + // Ensure that <...> extends java.lang.Object OR ... + sw.visitClassBound().visitClassType(boundDesc); + genAndBounds.put(g.getParsedName(), boundDesc); + } + sw.visitClassBound().visitEnd(); + } + + public String toString() { + return sw.toString(); + } + +} diff --git a/src/de/dhbwstuttgart/bytecode/TypeToString.java b/src/de/dhbwstuttgart/bytecode/TypeToString.java new file mode 100644 index 00000000..86d4124a --- /dev/null +++ b/src/de/dhbwstuttgart/bytecode/TypeToString.java @@ -0,0 +1,38 @@ +package de.dhbwstuttgart.bytecode; + +import de.dhbwstuttgart.exceptions.NotImplementedException; +import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType; +import de.dhbwstuttgart.syntaxtree.type.GenericRefType; +import de.dhbwstuttgart.syntaxtree.type.RefType; +import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType; +import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; +import de.dhbwstuttgart.syntaxtree.type.TypeVisitor; + +public class TypeToString implements TypeVisitor{ + + @Override + public String visit(RefType refType) { + return "RT"; + } + + @Override + public String visit(SuperWildcardType superWildcardType) { + throw new NotImplementedException(); + } + + @Override + public String visit(TypePlaceholder typePlaceholder) { + return "TPH"; + } + + @Override + public String visit(ExtendsWildcardType extendsWildcardType) { + throw new NotImplementedException(); + } + + @Override + public String visit(GenericRefType genericRefType) { + return "GRT"; + } + +} diff --git a/test/bytecode/Generics.jav b/test/bytecode/Generics.jav index 2c7a67b6..bb7b2af5 100644 --- a/test/bytecode/Generics.jav +++ b/test/bytecode/Generics.jav @@ -1,5 +1,7 @@ class Generics { + Generics(B b){ + } B mt1(B b){ return mt1(b); } diff --git a/test/bytecode/Generics2.jav b/test/bytecode/Generics2.jav new file mode 100644 index 00000000..52d5caa2 --- /dev/null +++ b/test/bytecode/Generics2.jav @@ -0,0 +1,6 @@ +class Generics2{ + B m1(B b){ + return b; + } + +} \ No newline at end of file diff --git a/test/bytecode/Generics2Test.java b/test/bytecode/Generics2Test.java new file mode 100644 index 00000000..26e52665 --- /dev/null +++ b/test/bytecode/Generics2Test.java @@ -0,0 +1,7 @@ +package bytecode; + +public class Generics2Test extends JavaTXCompilerTest{ + public Generics2Test() { + this.fileName = "Generics2"; + } +} diff --git a/test/bytecode/GenericsTest.java b/test/bytecode/GenericsTest.java new file mode 100644 index 00000000..cca16129 --- /dev/null +++ b/test/bytecode/GenericsTest.java @@ -0,0 +1,7 @@ +package bytecode; + +public class GenericsTest extends JavaTXCompilerTest { + public GenericsTest() { + this.fileName = "Generics"; + } +} diff --git a/test/bytecode/InterfaceTest.java b/test/bytecode/InterfaceTest.java new file mode 100644 index 00000000..ed378127 --- /dev/null +++ b/test/bytecode/InterfaceTest.java @@ -0,0 +1,7 @@ +package bytecode; + +public class InterfaceTest extends JavaTXCompilerTest{ + public InterfaceTest() { + this.fileName = "Interface1"; + } +} diff --git a/test/bytecode/JavaTXCompilerTest.java b/test/bytecode/JavaTXCompilerTest.java index 39ff5084..f017e23e 100644 --- a/test/bytecode/JavaTXCompilerTest.java +++ b/test/bytecode/JavaTXCompilerTest.java @@ -25,11 +25,12 @@ public class JavaTXCompilerTest { private static final String rootDirectory = System.getProperty("user.dir")+"/test/bytecode/"; private static final List filesToTest = new ArrayList<>(); - + + protected String fileName = ""; + @Test public void test() throws IOException, java.lang.ClassNotFoundException { System.out.println(rootDirectory); - String fileName = "Faculty"; filesToTest.add(new File(rootDirectory+fileName+".jav")); System.out.println(rootDirectory+fileName+".jav"); JavaTXCompiler compiler = new JavaTXCompiler(filesToTest); @@ -48,7 +49,7 @@ public class JavaTXCompilerTest { if(pos != -1) { name = f.getName().substring(0, pos); } - this.writeClassFile(bytecode, name); + this.writeClassFile(bytecode); } } @@ -61,20 +62,23 @@ public class JavaTXCompilerTest { return bytecodeGen.getClassFiles(); } - public void writeClassFile(HashMap classFiles, String name) { + public void writeClassFile(HashMap classFiles) { FileOutputStream output; - byte[] bytecode = classFiles.get(name); - try { - System.out.println("generating .class file"); - output = new FileOutputStream(new File(System.getProperty("user.dir") + "/testBytecode/generatedBC/" +name+".class")); - output.write(bytecode); - output.close(); - System.out.println(".class file generated"); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } + for(String name : classFiles.keySet()) { + byte[] bytecode = classFiles.get(name); + try { + System.out.println("generating"+name+ ".class file"); + output = new FileOutputStream(new File(System.getProperty("user.dir") + "/testBytecode/generatedBC/" +name+".class")); + output.write(bytecode); + output.close(); + System.out.println(name+".class file generated"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } static String readFile(String path, Charset encoding) diff --git a/test/bytecode/LamAssign.jav b/test/bytecode/LamAssign.jav index e522bd3b..3df81780 100644 --- a/test/bytecode/LamAssign.jav +++ b/test/bytecode/LamAssign.jav @@ -7,3 +7,7 @@ class LamAssign { return lam1; } } + +interface Fun1{ + A apply(B b); +} diff --git a/test/bytecode/LamAssignTest.java b/test/bytecode/LamAssignTest.java new file mode 100644 index 00000000..3442c1d1 --- /dev/null +++ b/test/bytecode/LamAssignTest.java @@ -0,0 +1,7 @@ +package bytecode; + +public class LamAssignTest extends JavaTXCompilerTest{ + public LamAssignTest() { + this.fileName = "LamAssign"; + } +}