diff --git a/pom.xml b/pom.xml index 8fe73131..78e5bf45 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,14 @@ http://maven.apache.org/maven-v4_0_0.xsd"> + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + --enable-preview + + org.antlr antlr4-maven-plugin @@ -69,8 +77,8 @@ http://maven.apache.org/maven-v4_0_0.xsd"> - 17 - 17 + 19 + 19 de.dhbwstuttgart.core.ConsoleInterface diff --git a/resources/bytecode/javFiles/Cycle.jav b/resources/bytecode/javFiles/Cycle.jav new file mode 100644 index 00000000..85f2ba9a --- /dev/null +++ b/resources/bytecode/javFiles/Cycle.jav @@ -0,0 +1,6 @@ +class Cycle { + m(x, y) { + y = x; + x = y; + } +} \ No newline at end of file diff --git a/resources/bytecode/javFiles/Inf.jav b/resources/bytecode/javFiles/Inf.jav index 3c785a6e..788fca8d 100644 --- a/resources/bytecode/javFiles/Inf.jav +++ b/resources/bytecode/javFiles/Inf.jav @@ -12,8 +12,44 @@ public class Inf { b=a; } } -// v w -// \ / -// z y b -// \ / \ / -// x a \ No newline at end of file + +/* +TPH M m(TPH N x, TPH O y, TPH P a)({ + TPH Q z; + TPH R v; + TPH S w; + TPH T b; + (y)::TPH O = (x)::TPH N; + (z)::TPH Q = (x)::TPH N; + (v)::TPH R = (y)::TPH O; + (w)::TPH S = (y)::TPH O; + (y)::TPH O = (a)::TPH P; + (b)::TPH T = (a)::TPH P; + return; + })::TPH U + + Inf()({ + super(()); + })::TPH X + +} +// v::R w::S +// \ / +// z::Q y::O b::T +// \ / \ / +// x::N a::P + +RESULT Final: [[(TPH O < TPH S), (TPH P < TPH O), (TPH O < TPH R), (TPH P < TPH T), (TPH M = void), (TPH N < TPH O), (TPH N < TPH Q)]] +Simplified constraints: [(TPH O < TPH S), (TPH P < TPH O), (TPH O < TPH R), (TPH P < TPH T), (TPH N < TPH O), (TPH N < TPH Q)] +m: [(TPH DDV = java.lang.Object), (TPH DDX = java.lang.Object), (TPH DDX < TPH DDV), (TPH N < TPH DDX), (TPH P < TPH DDX)] +Class Inf: [] +Inf: [] + +Unify nach Oder-Constraints-Anpassung: +UND:[(void =. M, , -1 WC: false, IT: false), (N <. O, 1 WC: false, IT: false, 1 WC: false, IT: false), (P <. O, 1 WC: false, IT: false, 1 WC: false, IT: false), (N <. Q, 1 WC: false, IT: false, 0 WC: true, IT: false), (O <. S, 1 WC: false, IT: false, 0 WC: true, IT: false), (O <. R, 1 WC: false, IT: false, 0 WC: true, IT: false), (P <. T, 1 WC: false, IT: false, 0 WC: true, IT: false)] +isInherited = false +isStatement = false + +ODER: +*/ + diff --git a/resources/bytecode/javFiles/Infimum.jav b/resources/bytecode/javFiles/Infimum.jav new file mode 100644 index 00000000..7c475394 --- /dev/null +++ b/resources/bytecode/javFiles/Infimum.jav @@ -0,0 +1,6 @@ +class Infimum { + m(x, y, z) { + y = x; + z = x; + } +} \ No newline at end of file diff --git a/resources/bytecode/javFiles/OLFun.jav b/resources/bytecode/javFiles/OLFun.jav index e780a06f..adf8c6a8 100644 --- a/resources/bytecode/javFiles/OLFun.jav +++ b/resources/bytecode/javFiles/OLFun.jav @@ -4,14 +4,10 @@ import java.lang.Double; import java.util.Vector; import java.lang.Boolean; - - - public class OLFun { //f = x -> {return x + x;}; - m(f, x) { - x = f.apply(x+x); + x = f.apply(x+x); } } \ No newline at end of file diff --git a/resources/bytecode/javFiles/OLFun2.jav b/resources/bytecode/javFiles/OLFun2.jav new file mode 100644 index 00000000..406c0d98 --- /dev/null +++ b/resources/bytecode/javFiles/OLFun2.jav @@ -0,0 +1,13 @@ +import java.lang.String; +import java.lang.Integer; +import java.lang.Double; +import java.util.Vector; +import java.lang.Boolean; + +public class OLFun2 { + + x; + m(f){ + x = f.apply(x + x) + } +} \ No newline at end of file diff --git a/resources/bytecode/javFiles/SimpleCycle.jav b/resources/bytecode/javFiles/SimpleCycle.jav index 8e19ea41..2f45aa18 100644 --- a/resources/bytecode/javFiles/SimpleCycle.jav +++ b/resources/bytecode/javFiles/SimpleCycle.jav @@ -1,11 +1,11 @@ public class SimpleCycle { m(a,b,d){ - /* var g; + var g; var h; g = h; h = g; - + /* var y; var z; y=z; @@ -18,8 +18,8 @@ public class SimpleCycle { var f = d; b = x; var l = c; - a = l; */ - + a = l; + */ } } \ No newline at end of file diff --git a/resources/bytecode/javFiles/Tph.jav b/resources/bytecode/javFiles/Tph.jav index 9faa570c..3f9d0aab 100644 --- a/resources/bytecode/javFiles/Tph.jav +++ b/resources/bytecode/javFiles/Tph.jav @@ -3,7 +3,6 @@ public class Tph { m(a,b){ var c = m2(b); return a; -// return m2(b); } m2(b){ diff --git a/resources/bytecode/javFiles/Tph2.jav b/resources/bytecode/javFiles/Tph2.jav index 760e4fa7..2f9c53e6 100644 --- a/resources/bytecode/javFiles/Tph2.jav +++ b/resources/bytecode/javFiles/Tph2.jav @@ -4,7 +4,7 @@ public class Tph2 { return id.apply(x); } - +/* m(a,b){ var c = m2(a,b); //m2(a,b); @@ -14,4 +14,5 @@ public class Tph2 { m2(a,b){ return b; } +*/ } \ No newline at end of file diff --git a/resources/bytecode/javFiles/Tph7.jav b/resources/bytecode/javFiles/Tph7.jav new file mode 100644 index 00000000..cc55afb5 --- /dev/null +++ b/resources/bytecode/javFiles/Tph7.jav @@ -0,0 +1,11 @@ +public class Tph7 { + + m(a,b){ + var c = m2(b); + return m2(b); + } + + m2(b){ + return b; + } +} \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java b/src/main/java/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java index 0565d933..5cbcfca9 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/BytecodeGenMethod.java @@ -13,7 +13,10 @@ import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; +import de.dhbwstuttgart.bytecode.funN.FunNGenerator; +import de.dhbwstuttgart.bytecode.funN.FunNUtilities; import de.dhbwstuttgart.bytecode.utilities.*; import de.dhbwstuttgart.environment.DirectoryClassLoader; import de.dhbwstuttgart.exceptions.NotImplementedException; @@ -589,10 +592,21 @@ public class BytecodeGenMethod implements StatementVisitor { this.lamCounter++; String typeErasure = createDescriptorWithTypeErasure(lambdaExpression); + //ToDo Etienne: Double Check + RefTypeOrTPHOrWildcardOrGeneric returnType = resolver.resolve(lambdaExpression.getReturnType()); + List argumentTypes = lambdaExpression + .params + .getFormalparalist() + .stream() + .map(FormalParameter::getType) + .map(resolver::resolve) + .collect(Collectors.toList()); + FunNUtilities funNUtilities = FunNGenerator.getInstance(); + FunNUtilities.writeClassFile(funNUtilities.getSuperClassName(argumentTypes.size()), + funNUtilities.generateSuperBytecode(argumentTypes.size()), path); + FunNUtilities.writeClassFile(funNUtilities.getSpecializedClassName(argumentTypes, returnType), + funNUtilities.generateSpecializedBytecode(argumentTypes, returnType), path); - ByteCodeForFunNGenerator.generateBCForFunN(lambdaExpression, typeErasure,path); - - Lambda lam = new Lambda(lambdaExpression); String lamDesc = lam.accept(new DescriptorToString(resultSet)); // Call site, which, when invoked, returns an instance of the functional @@ -814,8 +828,20 @@ public class BytecodeGenMethod implements StatementVisitor { } else if(!helper.isInCurrPkg(clazz)){ if(clazz.contains(CONSTANTS.$$)) { mDesc = helper.getDescriptorOfApplyMethod(methCallType); - helper.generateBCForFunN(mDesc); -// mDesc = helper.generateBCForFunN(methCallType,typesOfParams); + //ToDo Etienne: Double Check + RefTypeOrTPHOrWildcardOrGeneric returnType = resolver.resolve(methodCall.getType()); + List argumentTypes = methodCall + .arglist + .getArguments() + .stream() + .map(TypableStatement::getType) + .map(resolver::resolve) + .collect(Collectors.toList()); + FunNUtilities funNUtilities = FunNGenerator.getInstance(); + FunNUtilities.writeClassFile(funNUtilities.getSuperClassName(argumentTypes.size()), + funNUtilities.generateSuperBytecode(argumentTypes.size()), path); + FunNUtilities.writeClassFile(funNUtilities.getSpecializedClassName(argumentTypes, returnType), + funNUtilities.generateSpecializedBytecode(argumentTypes, returnType), path); }else { try { cLoader2 = new DirectoryClassLoader(path, classLoader); diff --git a/src/main/java/de/dhbwstuttgart/bytecode/descriptor/DescriptorToString.java b/src/main/java/de/dhbwstuttgart/bytecode/descriptor/DescriptorToString.java index 90064863..c272c12f 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/descriptor/DescriptorToString.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/descriptor/DescriptorToString.java @@ -153,6 +153,7 @@ public class DescriptorToString implements DescriptorVisitor, CONSTANTS { return desc; } + //ToDo Etienne: ändern @Override public String visit(Lambda lambdaExpression) { String desc = "("; diff --git a/src/main/java/de/dhbwstuttgart/bytecode/descriptor/TypeToDescriptor.java b/src/main/java/de/dhbwstuttgart/bytecode/descriptor/TypeToDescriptor.java index aa3db5b1..f10f8070 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/descriptor/TypeToDescriptor.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/descriptor/TypeToDescriptor.java @@ -1,17 +1,24 @@ package de.dhbwstuttgart.bytecode.descriptor; -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; +import de.dhbwstuttgart.bytecode.funN.FunNGenerator; +import de.dhbwstuttgart.bytecode.funN.FunNUtilities; +import de.dhbwstuttgart.syntaxtree.type.*; public class TypeToDescriptor implements TypeVisitor{ + private final boolean specializedFunN; + + public TypeToDescriptor(){ this(true); } + + public TypeToDescriptor(boolean specializedFunN) { this.specializedFunN = specializedFunN; } + @Override public String visit(RefType refType) { + if (refType.getName().toString().matches("Fun\\d+\\$\\$") && specializedFunN) { + FunNUtilities funNUtilities = FunNGenerator.getInstance(); + return funNUtilities.getSpecializedDescriptor(funNUtilities.getArguments(refType.getParaList()), funNUtilities.getReturnType(refType.getParaList())); + } + return refType.getName().toString().replace(".", "/"); // String t = refType.getName().toString().replace(".", "/"); // return t.equals("Fun1")?(t+"$$"):t; diff --git a/src/main/java/de/dhbwstuttgart/bytecode/funN/FunNGenerator.java b/src/main/java/de/dhbwstuttgart/bytecode/funN/FunNGenerator.java new file mode 100644 index 00000000..3a651c93 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/bytecode/funN/FunNGenerator.java @@ -0,0 +1,149 @@ +package de.dhbwstuttgart.bytecode.funN; + +import de.dhbwstuttgart.bytecode.descriptor.TypeToDescriptor; +import de.dhbwstuttgart.bytecode.signature.TypeToSignature; +import de.dhbwstuttgart.bytecode.utilities.CONSTANTS; +import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.syntaxtree.type.GenericRefType; +import de.dhbwstuttgart.syntaxtree.type.RefType; +import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; +import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.objectweb.asm.Opcodes.*; + +/** + * //ToDo beschreiben + * + * @since Studienarbeit Type Erasure + * @author etiennezink + */ +public class FunNGenerator implements FunNUtilities{ + + private static FunNGenerator funNGenerator = new FunNGenerator(); + + public static FunNGenerator getInstance(){ + return funNGenerator; + } + + private final String argumentGenericBase = "T"; + private final String returnGeneric = "R"; + private final String methodName = "apply"; + private final int bytecodeVersion = V1_8; + + private final String objectSuperType = Type.getInternalName(Object.class).replace('.','/'); + private final RefType objectRefType = new RefType(new JavaClassName(objectSuperType), null); + private final String objectSignature = applySignature(objectRefType); + + private String applyDescriptor(RefTypeOrTPHOrWildcardOrGeneric a) { return a.acceptTV(new TypeToDescriptor(true)); } + private String applySignature(RefTypeOrTPHOrWildcardOrGeneric a) { return a.acceptTV(new TypeToSignature(true)); } + private String applyNameDescriptor(RefTypeOrTPHOrWildcardOrGeneric a){ return a instanceof TypePlaceholder ? "LTPH;" : String.format("L%s;", applyDescriptor(a)); } + + @Override + public byte[] generateSuperBytecode(int numberArguments) { + StringBuilder superFunNClassSignature = new StringBuilder("<"); + StringBuilder superFunNMethodSignature = new StringBuilder("("); + StringBuilder superFunNMethodDescriptor = new StringBuilder("("); + + for (int currentParameter = 1; currentParameter <= numberArguments; currentParameter++){ + superFunNClassSignature.append(String.format("%s%d:%s",argumentGenericBase, currentParameter, objectSignature)); + superFunNMethodSignature.append(String.format("T%s;", applySignature( new GenericRefType(argumentGenericBase + currentParameter, null)))); + superFunNMethodDescriptor.append(objectSignature); + } + superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature)); + superFunNMethodSignature.append(String.format(")T%s;", applySignature(new GenericRefType(returnGeneric, null)))); + superFunNMethodDescriptor.append(String.format(")%s", objectSignature)); + + ClassWriter classWriter = new ClassWriter(0); + MethodVisitor methodVisitor; + classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSuperClassName(numberArguments), superFunNClassSignature.toString(), objectSuperType, null); + methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, methodName, superFunNMethodDescriptor.toString(), superFunNMethodSignature.toString(), null); + methodVisitor.visitEnd(); + classWriter.visitEnd(); + return classWriter.toByteArray(); + } + + @Override + public String getSuperClassName(int numberArguments) { + return String.format("Fun%d$$", numberArguments); + } + + @Override + public byte[] generateSpecializedBytecode(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType) { + List parameters = Stream + .concat(argumentTypes.stream(), Stream.of(returnType)) + .collect(Collectors.toList()); + RefType superFunN = new RefType(new JavaClassName(getSuperClassName(argumentTypes.size())), parameters , null); + StringBuilder funNClassSignature = new StringBuilder(objectSignature + (superFunN.acceptTV(new TypeToSignature(false)))); + boolean containsGeneric = false; + + String genericSignature = "<"; + for (RefTypeOrTPHOrWildcardOrGeneric typeArgument : parameters) { + //ToDo Etienne: Refactor + if (typeArgument instanceof GenericRefType){ + GenericRefType generic = (GenericRefType) typeArgument; + if(genericSignature.contains(generic.getParsedName())) continue; + genericSignature += String.format("%s:%s", generic.getParsedName(), applyDescriptor(generic)); + containsGeneric = true; + } else if(typeArgument instanceof TypePlaceholder){ + TypePlaceholder placeholder = (TypePlaceholder) typeArgument; + if(genericSignature.contains(applySignature(placeholder).substring(1))) continue; + genericSignature += String.format("%s:%s", applySignature(placeholder).substring(1), objectSignature); + containsGeneric = true; + } + } + genericSignature += ">"; + if (containsGeneric) funNClassSignature.insert(0, genericSignature); + + ClassWriter classWriter = new ClassWriter(0); + classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, new String[]{getSuperClassName(argumentTypes.size())}); + classWriter.visitEnd(); + return classWriter.toByteArray(); + } + + @Override + public String getSpecializedClassName(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType) { + return String.format("Fun%d$$%s%s", + argumentTypes.size(), + argumentTypes + .stream() + .map(this::applyNameDescriptor) + .collect(Collectors.joining()), + applyNameDescriptor(returnType)) + .replace('/', '$') + .replace(";", "$_$"); + } + + @Override + public String getSpecializedDescriptor(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType) { + return applyDescriptor(new RefType(new JavaClassName(getSpecializedClassName(argumentTypes, returnType)), null)); + } + + @Override + public String getSpecializedSignature(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType) { + return applySignature(new RefType(new JavaClassName(getSpecializedClassName(argumentTypes, returnType)), null)); + } + + @Override + public List getArguments(List list) { + return list + .stream() + .limit(Math.max(0, list.size() - 1)) + .collect(Collectors.toList()); + } + + @Override + public RefTypeOrTPHOrWildcardOrGeneric getReturnType(List list) { + if(list.size() == 0) + throw new IndexOutOfBoundsException(); + return list.get(list.size() - 1); + } +} diff --git a/src/main/java/de/dhbwstuttgart/bytecode/funN/FunNUtilities.java b/src/main/java/de/dhbwstuttgart/bytecode/funN/FunNUtilities.java new file mode 100644 index 00000000..7ae4da4d --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/bytecode/funN/FunNUtilities.java @@ -0,0 +1,36 @@ +package de.dhbwstuttgart.bytecode.funN; + +import de.dhbwstuttgart.bytecode.utilities.CONSTANTS; +import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; + +public interface FunNUtilities { + + byte[] generateSuperBytecode(int numberArguments); + String getSuperClassName(int numberArguments); + + byte[] generateSpecializedBytecode(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType); + String getSpecializedClassName(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType); + String getSpecializedDescriptor(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType); + String getSpecializedSignature(List argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType); + + List getArguments(List list); + RefTypeOrTPHOrWildcardOrGeneric getReturnType(List list); + + @Deprecated + public static boolean writeClassFile(String className, byte[] bytecode, File directory) { + try (FileOutputStream output = new FileOutputStream(new File(directory , className + CONSTANTS.EXTENSIONCLASS))){ + output.write(bytecode); + output.flush(); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + +} diff --git a/src/main/java/de/dhbwstuttgart/bytecode/signature/TypeToSignature.java b/src/main/java/de/dhbwstuttgart/bytecode/signature/TypeToSignature.java index c8306f02..0026b85f 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/signature/TypeToSignature.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/signature/TypeToSignature.java @@ -5,6 +5,8 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; +import de.dhbwstuttgart.bytecode.funN.FunNGenerator; +import de.dhbwstuttgart.bytecode.funN.FunNUtilities; import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.GenericsGeneratorResult; import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType; import de.dhbwstuttgart.syntaxtree.type.GenericRefType; @@ -17,18 +19,29 @@ import de.dhbwstuttgart.syntaxtree.type.TypeVisitor; public class TypeToSignature implements TypeVisitor { private List constraints; - public TypeToSignature() { - this.constraints = new ArrayList<>(); - } + private final boolean specializedFunN; + + public TypeToSignature() { this(new ArrayList<>(), true); } + + public TypeToSignature(boolean specializedFunN) { this(new ArrayList<>(), specializedFunN); } public TypeToSignature(List constraints) { + this(constraints, true); + } + + public TypeToSignature(List constraints, boolean specializedFunN){ this.constraints = constraints; + this.specializedFunN = specializedFunN; } @Override public String visit(RefType refType) { if(refType.getName().toString().equals("void")) return "V"; + if (refType.getName().toString().matches("Fun\\d+\\$\\$") && specializedFunN){ + FunNUtilities funNUtilities = FunNGenerator.getInstance(); + return funNUtilities.getSpecializedSignature(funNUtilities.getArguments(refType.getParaList()), funNUtilities.getReturnType(refType.getParaList())); + } // return refType.toString().replace(".", "/"); String params = ""; if(refType.getParaList().size()>0){ diff --git a/src/main/java/de/dhbwstuttgart/bytecode/utilities/Resolver.java b/src/main/java/de/dhbwstuttgart/bytecode/utilities/Resolver.java index f581b374..684fdd4b 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/utilities/Resolver.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/utilities/Resolver.java @@ -25,4 +25,9 @@ public class Resolver { public String getResolvedType(RefTypeOrTPHOrWildcardOrGeneric type) { return resultSet.resolveType(type).resolvedType.acceptTV(new TypeToDescriptor()); } + + //ToDo Etienne: Check ob benötigt + public RefTypeOrTPHOrWildcardOrGeneric resolve(RefTypeOrTPHOrWildcardOrGeneric type) { + return resultSet.resolveType(type).resolvedType; + } } diff --git a/src/main/java/de/dhbwstuttgart/target/ByteArrayClassLoader.java b/src/main/java/de/dhbwstuttgart/target/ByteArrayClassLoader.java new file mode 100644 index 00000000..428e214d --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/ByteArrayClassLoader.java @@ -0,0 +1,16 @@ +package de.dhbwstuttgart.target; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ByteArrayClassLoader extends ClassLoader { + public Class loadClass(byte[] code) { + return this.defineClass(null, code, 0, code.length); + } + + public Class loadClass(Path path) throws IOException { + var code = Files.readAllBytes(path); + return this.defineClass(null, code, 0, code.length); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/bytecode/CodeGenException.java b/src/main/java/de/dhbwstuttgart/target/bytecode/CodeGenException.java new file mode 100644 index 00000000..f517e767 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/bytecode/CodeGenException.java @@ -0,0 +1,7 @@ +package de.dhbwstuttgart.target.bytecode; + +public class CodeGenException extends RuntimeException { + public CodeGenException(String cause) { + super(cause); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java new file mode 100755 index 00000000..04bee492 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java @@ -0,0 +1,1033 @@ +package de.dhbwstuttgart.target.bytecode; + +import de.dhbwstuttgart.syntaxtree.statement.Block; +import de.dhbwstuttgart.target.tree.*; +import de.dhbwstuttgart.target.tree.expression.*; +import de.dhbwstuttgart.target.tree.type.*; +import org.objectweb.asm.*; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.*; + +import static org.objectweb.asm.Opcodes.*; +import static de.dhbwstuttgart.target.tree.expression.TargetBinaryOp.*; +import static de.dhbwstuttgart.target.tree.expression.TargetLiteral.*; + +public class Codegen { + private final TargetClass clazz; + private final ClassWriter cw; + public final String className; + private int lambdaCounter = 0; + private final HashMap lambdas = new HashMap<>(); + + public Codegen(TargetClass clazz) { + this.clazz = clazz; + this.className = clazz.qualifiedName(); + this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + } + + private record LocalVar(int index, String name, TargetType type) {} + + private static class Scope { + Scope parent; + Map locals = new HashMap<>(); + + Scope(Scope parent) { + this.parent = parent; + } + + void add(LocalVar var) { + locals.put(var.name, var); + } + + LocalVar get(String name) { + var local = locals.get(name); + if (local != null) { + return local; + } + if (parent != null) { + return parent.get(name); + } + throw new CodeGenException("Unknown symbol '" + name + "'"); + } + } + + private static class State { + Scope scope = new Scope(null); + int localCounter; + MethodVisitor mv; + + State(MethodVisitor mv, int localCounter) { + this.mv = mv; + this.localCounter = localCounter; + } + + void enterScope() { + this.scope = new Scope(this.scope); + } + + void exitScope() { + this.scope = this.scope.parent; + } + + LocalVar createVariable(String name, TargetType type) { + var local = new LocalVar(localCounter, name, type); + scope.add(local); + localCounter += 1; + return local; + } + } + + private void boxPrimitive(State state, TargetType type) { + var mv = state.mv; + if (type.equals(TargetType.Boolean) || type.equals(TargetType.boolean_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + } else if (type.equals(TargetType.Byte) || type.equals(TargetType.byte_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (type.equals(TargetType.Double) || type.equals(TargetType.double_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + } else if (type.equals(TargetType.Long) || type.equals(TargetType.long_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (type.equals(TargetType.Integer) || type.equals(TargetType.int_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (type.equals(TargetType.Float) || type.equals(TargetType.float_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (type.equals(TargetType.Short) || type.equals(TargetType.short_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (type.equals(TargetType.Char) || type.equals(TargetType.char_)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Char", "valueOf", "(C)Ljava/lang/Char;", false); + } + } + + private void unboxPrimitive(State state, TargetType type) { + var mv = state.mv; + if (type.equals(TargetType.Boolean)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); + } else if (type.equals(TargetType.Byte)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); + } else if (type.equals(TargetType.Double)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); + } else if (type.equals(TargetType.Long)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); + } else if (type.equals(TargetType.Integer)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + } else if (type.equals(TargetType.Float)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); + } else if (type.equals(TargetType.Short)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); + } else if (type.equals(TargetType.Char)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Char", "charValue", "()C", false); + } + } + + private void generateRelationalOperator(State state, TargetRelationalOp op, TargetType type, int code) { + var mv = state.mv; + Label if_true = new Label(); + Label end = new Label(); + generate(state, op.left()); + convertTo(state, op.left().type(), type); + generate(state, op.right()); + convertTo(state, op.right().type(), type); + mv.visitJumpInsn(code, if_true); + mv.visitInsn(ICONST_0); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(if_true); + mv.visitInsn(ICONST_1); + mv.visitLabel(end); + } + + private void generateRelationalOperator(State state, TargetRelationalOp op, TargetType type, int cmp, int code) { + var mv = state.mv; + Label if_true = new Label(); + Label end = new Label(); + generate(state, op.left()); + convertTo(state, op.left().type(), type); + generate(state, op.right()); + convertTo(state, op.right().type(), type); + mv.visitInsn(cmp); + mv.visitJumpInsn(code, if_true); + mv.visitInsn(ICONST_0); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(if_true); + mv.visitInsn(ICONST_1); + mv.visitLabel(end); + } + + private void convertToString(State state, TargetType type) { + var mv = state.mv; + if (type.equals(TargetType.Boolean)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/Boolean;", false); + } else if (type.equals(TargetType.Byte)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (type.equals(TargetType.Double)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/Double;", false); + } else if (type.equals(TargetType.Long)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (type.equals(TargetType.Integer)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (type.equals(TargetType.Float)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (type.equals(TargetType.Short)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (type.equals(TargetType.Char)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/Char;", false); + } + } + + private void convertTo(State state, TargetType source, TargetType dest) { + var mv = state.mv; + if (source.equals(dest)) + return; + if (source.equals(TargetType.Long)) { + if (dest.equals(TargetType.Integer)) { + mv.visitInsn(L2I); + } else if (dest.equals(TargetType.Float)) + mv.visitInsn(L2F); + else if (dest.equals(TargetType.Double)) + mv.visitInsn(L2D); + else if (dest.equals(TargetType.Byte) + || dest.equals(TargetType.Char) + || dest.equals(TargetType.Short)) { + mv.visitInsn(L2I); + convertTo(state, TargetType.Integer, dest); + } + } else if (source.equals(TargetType.Float)) { + if (dest.equals(TargetType.Integer)) + mv.visitInsn(F2I); + else if (dest.equals(TargetType.Double)) + mv.visitInsn(F2D); + else if (dest.equals(TargetType.Long)) + mv.visitInsn(F2L); + else if (dest.equals(TargetType.Byte) + || dest.equals(TargetType.Char) + || dest.equals(TargetType.Short)) { + mv.visitInsn(F2I); + convertTo(state, TargetType.Integer, dest); + } + } else if (source.equals(TargetType.Double)) { + if (dest.equals(TargetType.Integer)) + mv.visitInsn(D2I); + else if (dest.equals(TargetType.Float)) + mv.visitInsn(D2F); + else if (dest.equals(TargetType.Long)) + mv.visitInsn(D2L); + else if (dest.equals(TargetType.Byte) + || dest.equals(TargetType.Char) + || dest.equals(TargetType.Short)) { + mv.visitInsn(D2I); + convertTo(state, TargetType.Integer, dest); + } + } else if (source.equals(TargetType.Byte) + || source.equals(TargetType.Char) + || source.equals(TargetType.Short) + || source.equals(TargetType.Integer)) { + if (dest.equals(TargetType.Byte)) + mv.visitInsn(I2B); + else if (dest.equals(TargetType.Char)) + mv.visitInsn(I2C); + else if (dest.equals(TargetType.Short)) + mv.visitInsn(I2S); + else if (dest.equals(TargetType.Long)) + mv.visitInsn(I2L); + else if (dest.equals(TargetType.Float)) + mv.visitInsn(I2F); + else if (dest.equals(TargetType.Double)) + mv.visitInsn(I2D); + } else { + boxPrimitive(state, source); + mv.visitTypeInsn(CHECKCAST, dest.getInternalName()); + unboxPrimitive(state, dest); + } + } + + private TargetType largerType(TargetType left, TargetType right) { + if (left.equals(TargetType.String) || right.equals(TargetType.String)) { + return TargetType.String; + } else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) { + return TargetType.Double; + } else if (left.equals(TargetType.Float) || right.equals(TargetType.Float)) { + return TargetType.Float; + } else if (left.equals(TargetType.Long) || right.equals(TargetType.Long)) { + return TargetType.Long; + } else { + return TargetType.Integer; + } + } + + private void generateBinaryOp(State state, TargetBinaryOp op) { + var mv = state.mv; + switch (op) { + case Add add: { + if (add.type().equals(TargetType.String)) { + mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); + mv.visitInsn(DUP); + generate(state, add.left()); + convertToString(state, add.left().type()); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V", false); + } else { + generate(state, add.left()); + convertTo(state, add.left().type(), add.type()); + generate(state, add.right()); + convertTo(state, add.right().type(), add.type()); + var type = add.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Char) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IADD); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LADD); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FADD); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DADD); + } else { + throw new CodeGenException("Invalid argument to Add expression"); + } + } + if (add.type().equals(TargetType.String)) { + generate(state, add.right()); + convertToString(state, add.right().type()); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); + } + break; + } + case Sub sub: { + generate(state, sub.left()); + convertTo(state, sub.left().type(), op.type()); + generate(state, sub.right()); + convertTo(state, sub.right().type(), op.type()); + var type = sub.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Char) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(ISUB); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LSUB); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FSUB); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DSUB); + } else { + throw new CodeGenException("Invalid argument to Sub expression"); + } + break; + } + case Div div: { + generate(state, div.left()); + convertTo(state, div.left().type(), op.type()); + generate(state, div.right()); + convertTo(state, div.right().type(), op.type()); + var type = div.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Char) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IDIV); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LDIV); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FDIV); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DDIV); + } else { + throw new CodeGenException("Invalid argument to Div expression"); + } + break; + } + case Mul mul: { + generate(state, mul.left()); + convertTo(state, mul.left().type(), op.type()); + generate(state, mul.right()); + convertTo(state, mul.right().type(), op.type()); + var type = mul.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Char) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IMUL); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LMUL); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FMUL); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DMUL); + } else { + throw new CodeGenException("Invalid argument to Mul expression"); + } + break; + } + case Rem rem: { + generate(state, rem.left()); + convertTo(state, rem.left().type(), op.type()); + generate(state, rem.right()); + convertTo(state, rem.right().type(), op.type()); + var type = rem.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Char) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IREM); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LREM); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FREM); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DREM); + } else { + throw new CodeGenException("Invalid argument to Rem expression"); + } + break; + } + case Or or: { + Label or_false = new Label(); + Label or_true = new Label(); + Label end = new Label(); + generate(state, or.left()); + mv.visitJumpInsn(IFNE, or_true); + generate(state, or.right()); + mv.visitJumpInsn(IFEQ, or_false); + mv.visitLabel(or_true); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(or_false); + mv.visitInsn(ICONST_0); + mv.visitLabel(end); + break; + } + case And and: { + Label and_false = new Label(); + Label end = new Label(); + generate(state, and.left()); + mv.visitJumpInsn(IFEQ, and_false); + generate(state, and.right()); + mv.visitJumpInsn(IFEQ, and_false); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(and_false); + mv.visitInsn(ICONST_0); + mv.visitLabel(end); + break; + } + case BAnd band: { + generate(state, band.left()); + convertTo(state, band.left().type(), op.type()); + generate(state, band.right()); + convertTo(state, band.right().type(), op.type()); + if (band.type().equals(TargetType.Long)) + mv.visitInsn(LAND); + else mv.visitInsn(IAND); + break; + } + case BOr bor: { + generate(state, bor.left()); + convertTo(state, bor.left().type(), op.type()); + generate(state, bor.right()); + convertTo(state, bor.right().type(), op.type()); + if (bor.type().equals(TargetType.Long)) + mv.visitInsn(LOR); + else mv.visitInsn(IOR); + break; + } + case XOr xor: { + generate(state, xor.left()); + convertTo(state, xor.left().type(), op.type()); + generate(state, xor.right()); + convertTo(state, xor.right().type(), op.type()); + if (xor.type().equals(TargetType.Long)) + mv.visitInsn(LXOR); + else mv.visitInsn(IXOR); + break; + } + case Shl shl: { + generate(state, shl.left()); + convertTo(state, shl.left().type(), op.type()); + generate(state, shl.right()); + convertTo(state, shl.right().type(), op.type()); + if (shl.type().equals(TargetType.Long)) + mv.visitInsn(LSHL); + else mv.visitInsn(ISHL); + break; + } + case Shr shr: { + generate(state, shr.left()); + convertTo(state, shr.left().type(), op.type()); + generate(state, shr.right()); + convertTo(state, shr.right().type(), op.type()); + if (shr.type().equals(TargetType.Long)) + mv.visitInsn(LSHR); + else mv.visitInsn(ISHR); + break; + } + case UShr ushr: { + generate(state, ushr.left()); + convertTo(state, ushr.left().type(), op.type()); + generate(state, ushr.right()); + convertTo(state, ushr.right().type(), op.type()); + if (ushr.type().equals(TargetType.Long)) + mv.visitInsn(LUSHR); + else mv.visitInsn(IUSHR); + break; + } + case Greater greater: { + var type = largerType(greater.left().type(), greater.right().type()); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, greater, type, LCMP, IFGT); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, greater, type, FCMPL, IFGT); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, greater, type, DCMPL, IFGT); + } else { + generateRelationalOperator(state, greater, type, IF_ICMPGT); + } + break; + } + case Less less: { + var type = largerType(less.left().type(), less.right().type()); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, less, type, LCMP, IFLT); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, less, type, FCMPL, IFLT); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, less, type, DCMPL, IFLT); + } else { + generateRelationalOperator(state, less, type, IF_ICMPLT); + } + break; + } + case GreaterOrEqual greaterOrEqual: { + var type = largerType(greaterOrEqual.left().type(), greaterOrEqual.right().type()); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, greaterOrEqual, type, LCMP, IFGE); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, greaterOrEqual, type, FCMPL, IFGE); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, greaterOrEqual, type, DCMPL, IFGE); + } else { + generateRelationalOperator(state, greaterOrEqual, type, IF_ICMPGE); + } + break; + } + case LessOrEqual lessOrEqual: { + var type = largerType(lessOrEqual.left().type(), lessOrEqual.right().type()); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, lessOrEqual, type, LCMP, IFLE); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, lessOrEqual, type, FCMPL, IFLE); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, lessOrEqual, type, DCMPL, IFLE); + } else { + generateRelationalOperator(state, lessOrEqual, type, IF_ICMPLE); + } + break; + } + case Equal equal: { + var type = largerType(equal.left().type(), equal.right().type()); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, equal, type, LCMP, IFEQ); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, equal, type, FCMPL, IFEQ); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, equal, type, DCMPL, IFEQ); + } else if (type.equals(TargetType.Char) + || type.equals(TargetType.Short) + || type.equals(TargetType.Byte) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Boolean)) { + generateRelationalOperator(state, equal, type, IF_ICMPEQ); + } else { + generateRelationalOperator(state, equal, type, IF_ACMPEQ); + } + break; + } + case NotEqual notEqual: { + var type = largerType(notEqual.left().type(), notEqual.right().type()); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, notEqual, type, LCMP, IFNE); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, notEqual, type, FCMPL, IFNE); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, notEqual, type, DCMPL, IFNE); + } else if (type.equals(TargetType.Char) + || type.equals(TargetType.Short) + || type.equals(TargetType.Byte) + || type.equals(TargetType.Integer)) { + generateRelationalOperator(state, notEqual, type, IF_ICMPNE); + } else { + generateRelationalOperator(state, notEqual, type, IF_ACMPNE); + } + break; + } + } + } + + private void afterIncDec(State state, TargetUnaryOp op) { + var mv = state.mv; + if (op.expr() instanceof TargetLocalVar localVar) { + mv.visitVarInsn(ASTORE, state.scope.get(localVar.name()).index); + } else if (op.expr() instanceof TargetFieldVar fieldVar) { + generate(state, fieldVar.left()); + mv.visitInsn(SWAP); + mv.visitFieldInsn(PUTFIELD, fieldVar.owner().getInternalName(), fieldVar.right(), fieldVar.type().toSignature()); + } + } + + private void generateUnaryOp(State state, TargetUnaryOp op) { + var mv = state.mv; + switch (op) { + case TargetUnaryOp.Add add: + // This literally does nothing + generate(state, add.expr()); + break; + case TargetUnaryOp.Negate negate: + generate(state, negate.expr()); + if (negate.type().equals(TargetType.Double)) + mv.visitInsn(DNEG); + else if (negate.type().equals(TargetType.Float)) + mv.visitInsn(FNEG); + else if (negate.type().equals(TargetType.Long)) + mv.visitInsn(LNEG); + else mv.visitInsn(INEG); + break; + case TargetUnaryOp.Not not: + generate(state, not.expr()); + if (not.type().equals(TargetType.Long)) { + mv.visitLdcInsn(-1L); + mv.visitInsn(LXOR); + } else { + mv.visitInsn(ICONST_M1); + mv.visitInsn(IXOR); + } + break; + case TargetUnaryOp.PreIncrement preIncrement: + generate(state, preIncrement.expr()); + if (preIncrement.type().equals(TargetType.Float)) { + mv.visitLdcInsn(1F); + mv.visitInsn(FADD); + mv.visitInsn(DUP); + } else if (preIncrement.type().equals(TargetType.Double)) { + mv.visitLdcInsn(1D); + mv.visitInsn(DADD); + mv.visitInsn(DUP2); + } else if (preIncrement.type().equals(TargetType.Long)) { + mv.visitLdcInsn(1L); + mv.visitInsn(LADD); + mv.visitInsn(DUP2); + } else { + mv.visitLdcInsn(1); + mv.visitInsn(IADD); + mv.visitInsn(DUP); + } + boxPrimitive(state, preIncrement.type()); + afterIncDec(state, preIncrement); + break; + case TargetUnaryOp.PreDecrement preDecrement: + generate(state, preDecrement.expr()); + if (preDecrement.type().equals(TargetType.Float)) { + mv.visitLdcInsn(1F); + mv.visitInsn(FSUB); + mv.visitInsn(DUP); + } else if (preDecrement.type().equals(TargetType.Double)) { + mv.visitLdcInsn(1D); + mv.visitInsn(DSUB); + mv.visitInsn(DUP2); + } else if (preDecrement.type().equals(TargetType.Long)) { + mv.visitLdcInsn(1L); + mv.visitInsn(LSUB); + mv.visitInsn(DUP2); + } else { + mv.visitLdcInsn(1); + mv.visitInsn(ISUB); + mv.visitInsn(DUP); + } + boxPrimitive(state, preDecrement.type()); + afterIncDec(state, preDecrement); + break; + case TargetUnaryOp.PostIncrement postIncrement: + generate(state, postIncrement.expr()); + if (postIncrement.type().equals(TargetType.Float)) { + mv.visitInsn(DUP); + mv.visitLdcInsn(1F); + mv.visitInsn(FADD); + } else if (postIncrement.type().equals(TargetType.Double)) { + mv.visitInsn(DUP2); + mv.visitLdcInsn(1D); + mv.visitInsn(DADD); + } else if (postIncrement.type().equals(TargetType.Long)) { + mv.visitInsn(DUP2); + mv.visitLdcInsn(1L); + mv.visitInsn(LADD); + } else { + mv.visitInsn(DUP); + mv.visitLdcInsn(1); + mv.visitInsn(IADD); + } + boxPrimitive(state, postIncrement.type()); + afterIncDec(state, postIncrement); + break; + case TargetUnaryOp.PostDecrement postDecrement: + generate(state, postDecrement.expr()); + if (postDecrement.type().equals(TargetType.Float)) { + mv.visitInsn(DUP); + mv.visitLdcInsn(1F); + mv.visitInsn(FSUB); + } else if (postDecrement.type().equals(TargetType.Double)) { + mv.visitInsn(DUP2); + mv.visitLdcInsn(1D); + mv.visitInsn(DSUB); + } else if (postDecrement.type().equals(TargetType.Long)) { + mv.visitInsn(DUP2); + mv.visitLdcInsn(1L); + mv.visitInsn(LSUB); + } else { + mv.visitInsn(DUP); + mv.visitLdcInsn(1); + mv.visitInsn(ISUB); + } + boxPrimitive(state, postDecrement.type()); + afterIncDec(state, postDecrement); + break; + } + } + + private void generateLambdaExpression(State state, TargetLambdaExpression lambda) { + var mv = state.mv; + + TargetMethod impl; + if (lambdas.containsKey(lambda)) { + impl = lambdas.get(lambda); + } else { + var name = "lambda$" + lambdaCounter++; + var parameters = new ArrayList<>(lambda.captures()); + parameters.addAll(lambda.params()); + + impl = new TargetMethod( + 0, name, Set.of(), + parameters, lambda.returnType(), lambda.block() + ); + generateMethod(impl); + lambdas.put(lambda, impl); + } + + var mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, + MethodType.class, MethodType.class, MethodHandle.class, MethodType.class); + + var bootstrap = new Handle(H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", + mt.toMethodDescriptorString(), false); + var handle = new Handle( + H_INVOKEVIRTUAL, clazz.getName(), impl.name(), + impl.getDescriptor(), false + ); + + // TODO maybe make this a function? + var desugared = "("; + for (var param : lambda.params()) + desugared += "Ljava/lang/Object;"; + desugared += ")"; + if (lambda.returnType() != null) + desugared += "Ljava/lang/Object;"; + else desugared += "V"; + + var params = new ArrayList(); + params.add(new TargetRefType(clazz.qualifiedName())); + params.addAll(lambda.captures().stream().map(MethodParameter::type).toList()); + + var descriptor = TargetMethod.getDescriptor(lambda.type(), params.toArray(TargetType[]::new)); + mv.visitVarInsn(ALOAD, 0); + for (var capture : lambda.captures()) + mv.visitVarInsn(ALOAD, state.scope.get(capture.name()).index); + + mv.visitInvokeDynamicInsn("apply", descriptor, + bootstrap, Type.getType(desugared), handle, + Type.getType(TargetMethod.getDescriptor(impl.returnType(), lambda.params().stream().map(MethodParameter::type).toArray(TargetType[]::new))) + ); + } + + private void generate(State state, TargetExpression expr) { + var mv = state.mv; + switch (expr) { + case TargetClassName className: + break; // NOP + case TargetBlock block: { + var localCounter = state.localCounter; + state.enterScope(); + for (var e : block.statements()) { + generate(state, e); + if (e instanceof TargetMethodCall) { + if (e.type() != null) mv.visitInsn(POP); + } else if (e instanceof TargetStatementExpression) { + mv.visitInsn(POP); + } + } + state.exitScope(); + state.localCounter = localCounter; + break; + } + case TargetCast cast: + generate(state, cast.expr()); + convertTo(state, cast.expr().type(), cast.type()); + break; + case TargetInstanceOf instanceOf: + mv.visitTypeInsn(INSTANCEOF, instanceOf.right().getInternalName()); + break; + case TargetLiteral literal: + switch (literal) { + case IntLiteral intLiteral: + mv.visitLdcInsn(intLiteral.value()); + break; + case FloatLiteral floatLiteral: + mv.visitLdcInsn(floatLiteral.value()); + break; + case LongLiteral longLiteral: + mv.visitLdcInsn(longLiteral.value()); + break; + case StringLiteral stringLiteral: + mv.visitLdcInsn(stringLiteral.value()); + break; + case CharLiteral charLiteral: + mv.visitIntInsn(BIPUSH, charLiteral.value()); + break; + case DoubleLiteral doubleLiteral: + mv.visitLdcInsn(doubleLiteral.value()); + break; + case BooleanLiteral booleanLiteral: + if (booleanLiteral.value()) { + mv.visitInsn(ICONST_1); + } else { + mv.visitInsn(ICONST_0); + } + break; + } + break; + case TargetVarDecl varDecl: { + var local = state.createVariable(varDecl.name(), varDecl.varType()); + if (varDecl.value() != null) { + generate(state, varDecl.value()); + boxPrimitive(state, varDecl.varType()); + mv.visitVarInsn(ASTORE, local.index()); + } + break; + } + case TargetBinaryOp op: + generateBinaryOp(state, op); + break; + case TargetUnaryOp op: + generateUnaryOp(state, op); + break; + case TargetAssign assign: { + switch (assign.left()) { + case TargetLocalVar localVar: { + generate(state, assign.right()); + boxPrimitive(state, assign.right().type()); + var local = state.scope.get(localVar.name()); + mv.visitInsn(DUP); + mv.visitVarInsn(ASTORE, local.index()); + break; + } + case TargetFieldVar dot: { + generate(state, dot.left()); + generate(state, assign.right()); + boxPrimitive(state, assign.right().type()); + if (dot.isStatic()) + mv.visitInsn(DUP); + else mv.visitInsn(DUP_X1); + mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.owner().getInternalName(), dot.right(), dot.type().toSignature()); + break; + } + default: + throw new CodeGenException("Invalid assignment"); + } + break; + } + case TargetLocalVar localVar: { + LocalVar local = state.scope.get(localVar.name()); + mv.visitVarInsn(ALOAD, local.index()); + unboxPrimitive(state, local.type()); + break; + } + case TargetFieldVar dot: { + if (!dot.isStatic()) + generate(state, dot.left()); + mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, dot.left().type().getInternalName(), dot.right(), dot.type().toSignature()); + unboxPrimitive(state, dot.type()); + break; + } + case TargetFor _for: { + state.enterScope(); + var localCounter = state.localCounter; + if (_for.init() != null) + generate(state, _for.init()); + Label start = new Label(); + Label end = new Label(); + mv.visitLabel(start); + if (_for.termination() != null) + generate(state, _for.termination()); + mv.visitJumpInsn(IFEQ, end); + generate(state, _for.body()); + if (_for.increment() != null) { + generate(state, _for.increment()); + if (_for.increment().type() != null) { + mv.visitInsn(POP); + } + } + mv.visitJumpInsn(GOTO, start); + mv.visitLabel(end); + state.exitScope(); + state.localCounter = localCounter; + break; + } + case TargetWhile _while: { + Label start = new Label(); + Label end = new Label(); + mv.visitLabel(start); + generate(state, _while.cond()); + mv.visitJumpInsn(IFEQ, end); + generate(state, _while.body()); + mv.visitJumpInsn(GOTO, start); + mv.visitLabel(end); + break; + } + case TargetIf _if: { + generate(state, _if.cond()); + Label _else = new Label(); + Label end = new Label(); + mv.visitJumpInsn(IFEQ, _else); + generate(state, _if.if_body()); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(_else); + if (_if.else_body() != null) { + generate(state, _if.else_body()); + } + mv.visitLabel(end); + break; + } + case TargetReturn ret: { + if (ret.expression() != null) { + generate(state, ret.expression()); + boxPrimitive(state, ret.expression().type()); + mv.visitInsn(ARETURN); + } else mv.visitInsn(RETURN); + break; + } + case TargetThis _this: { + mv.visitVarInsn(ALOAD, 0); + break; + } + case TargetSuper _super: { + mv.visitVarInsn(ALOAD, 0); + break; + } + case TargetMethodCall call: { + generate(state, call.expr()); + for (var i = 0; i < call.args().size(); i++) { + var e = call.args().get(i); + var arg = call.parameterTypes().get(i); + generate(state, e); + if (!(arg instanceof TargetPrimitiveType)) + boxPrimitive(state, e.type()); + } + var descriptor = call.getDescriptor(); + if (call.owner() instanceof TargetFunNType) // Decay FunN + descriptor = TargetMethod.getDescriptor( + call.returnType() == null ? null : TargetType.Object, + call.parameterTypes().stream().map(x -> TargetType.Object).toArray(TargetType[]::new) + ); + + mv.visitMethodInsn(call.isInterface() ? INVOKEINTERFACE : call.isStatic() ? INVOKESTATIC: call.name().equals("") ? INVOKESPECIAL : INVOKEVIRTUAL, + call.owner().getInternalName(), call.name(), descriptor, call.isInterface()); + + if (call.returnType() != null && !(call.returnType() instanceof TargetPrimitiveType)) { + if (!call.returnType().equals(call.type()) && !(call.type() instanceof TargetGenericType)) + mv.visitTypeInsn(CHECKCAST, call.type().getInternalName()); + unboxPrimitive(state, call.type()); + } + break; + } + case TargetLambdaExpression lambda: + generateLambdaExpression(state, lambda); + break; + case TargetNew _new: { + mv.visitTypeInsn(NEW, _new.type().getInternalName()); + mv.visitInsn(DUP); + for (TargetExpression e : _new.params()) { + generate(state, e); + boxPrimitive(state, e.type()); + } + mv.visitMethodInsn(INVOKESPECIAL, _new.type().getInternalName(), "", _new.getDescriptor(), false); + break; + } + default: + throw new CodeGenException("Unexpected value: " + expr); + } + } + + private void generateField(TargetField field) { + cw.visitField(field.access() | ACC_PUBLIC, field.name(), field.type().toSignature(), field.type().toGenericSignature(), null); + } + + private void generateConstructor(TargetConstructor constructor) { + MethodVisitor mv = cw.visitMethod(constructor.access() | ACC_PUBLIC, "", constructor.getDescriptor(), constructor.getSignature(), null); + mv.visitCode(); + var state = new State(mv, 1); + for (var param: constructor.parameters()) + state.createVariable(param.name(), param.type()); + + var stmts = constructor.block().statements(); + generate(state, stmts.get(0)); + if (constructor.fieldInitializer() != null) { + var stmts2 = constructor.fieldInitializer().statements(); + for (var i = 1; i < stmts2.size(); i++) { + generate(state, stmts2.get(i)); + } + } + for (var i = 1; i < stmts.size(); i++) + generate(state, stmts.get(i)); + + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + private void generateMethod(TargetMethod method) { + // TODO The older codegen has set ACC_PUBLIC for all methods, good for testing but bad for everything else + MethodVisitor mv = cw.visitMethod(method.access() | ACC_PUBLIC, method.name(), method.getDescriptor(), method.getSignature(), null); + mv.visitCode(); + var state = new State(mv, method.isStatic() ? 0 : 1); + for (var param: method.parameters()) + state.createVariable(param.name(), param.type()); + generate(state, method.block()); + if (method.returnType() == null) + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + private static String generateSignature(TargetClass clazz) { + String ret = "<"; + for (var generic : clazz.generics()) { + ret += generic.name() + ":" + generic.bound().toGenericSignature(); + } + ret += ">"; + ret += clazz.superType().toGenericSignature(); + + return ret; + } + + public byte[] generate() { + cw.visit(V1_8, clazz.modifiers() | ACC_PUBLIC, clazz.qualifiedName(), + generateSignature(clazz), clazz.superType() != null ? clazz.superType().getInternalName(): "java/lang/Object", + clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new) + ); + clazz.fields().forEach(this::generateField); + clazz.constructors().forEach(this::generateConstructor); + clazz.methods().forEach(this::generateMethod); + + cw.visitEnd(); + return cw.toByteArray(); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java new file mode 100644 index 00000000..61be1e8c --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -0,0 +1,727 @@ +package de.dhbwstuttgart.target.generate; + +import com.google.j2objc.annotations.LoopTranslation; +import de.dhbwstuttgart.bytecode.funN.FunNGenerator; +import de.dhbwstuttgart.core.JavaTXCompiler; +import de.dhbwstuttgart.parser.NullToken; +import de.dhbwstuttgart.syntaxtree.*; +import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; +import de.dhbwstuttgart.syntaxtree.statement.*; +import de.dhbwstuttgart.syntaxtree.type.*; +import de.dhbwstuttgart.target.tree.*; +import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import de.dhbwstuttgart.target.tree.expression.TargetExpression; +import de.dhbwstuttgart.target.tree.type.*; +import de.dhbwstuttgart.typeinference.result.*; +import de.dhbwstuttgart.target.ByteArrayClassLoader; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ASTToTargetAST { + + static RefType OBJECT = ASTFactory.createObjectType(); // TODO It would be better if I could call this directly but the hashcode seems to change + + protected List all; + protected Sigma sigma; + protected ClassOrInterface currentClass; // TODO This is only needed because of SuperCall, maybe there's a better way? + + private class Sigma { + Map>> computedGenericsOfMethods = new HashMap<>(); + Map> usedTPHsOfMethods = new HashMap<>(); + Map>> computedGenericsOfClasses = new HashMap<>(); + + Set simplifiedConstraints = new HashSet<>(); + Map concreteTypes = new HashMap<>(); + Map equality = new HashMap<>(); + + Sigma(ResultSet constraints) { + ASTToTargetAST.this.sigma = this; + + Set> equalitySet = new HashSet<>(); + Map> unified = new HashMap<>(); + + for (var constraint : constraints.results) { + if (constraint instanceof PairTPHEqualTPH p) { + if (unified.containsKey(p.getLeft())) { + var equals = unified.get(p.getLeft()); + equals.add(p.getRight()); + unified.put(p.getLeft(), equals); + } else if (unified.containsKey(p.getRight())) { + var equals = unified.get(p.getRight()); + equals.add(p.getLeft()); + unified.put(p.getRight(), equals); + } else { + List equals = new ArrayList<>(); + equals.add(p.getLeft()); + equals.add(p.getRight()); + unified.put(p.getLeft(), equals); + unified.put(p.getRight(), equals); + equalitySet.add(equals); + } + } + } + + for (var constraint : constraints.results) { + if (constraint instanceof PairTPHsmallerTPH p) { + var left = p.left; + var right = p.right; + if (unified.containsKey(left)) + left = unified.get(left).get(0); + if (unified.containsKey(right)) + right = unified.get(right).get(0); + + simplifiedConstraints.add(new PairTPHsmallerTPH(left, right)); + } + } + System.out.println("Simplified constraints: " + simplifiedConstraints); + + for (var equality : equalitySet) { + var first = equality.get(0); + for (var i = 1; i < equality.size(); i++) + this.equality.put(equality.get(i), first); + } + + for (var constraint : constraints.results) { + if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) { + concreteTypes.put(this.equality.getOrDefault(p.left, p.left), p.right); + } + } + } + + void findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type, Set typeVariables) { + if (type instanceof TypePlaceholder tph) { + tph = equality.getOrDefault(tph, tph); + if (concreteTypes.containsKey(tph)) { + findTypeVariables(concreteTypes.get(tph), typeVariables); + return; + } + typeVariables.add(tph); + } else if (type instanceof RefType refType) { + for (var t : refType.getParaList()) + findTypeVariables(t, typeVariables); + } + } + + boolean hasBound(TypePlaceholder name, Set> generics) { + return generics.stream().anyMatch(generic -> generic.getLeft().equals(name)); + } + + boolean containsRelation(Set> result, PairTPHsmallerTPH pair) { + // Check if both the right and the left are already part of a relation + var containsLeft = false; + for (var pair2 : result) { + if (pair2.getLeft().equals(pair.left)) { + containsLeft = true; + break; + } + } + var containsRight = false; + for (var pair2 : result) { + if (pair2.getRight().equals(pair.right)) { + containsRight = true; + break; + } + } + return containsLeft && containsRight; + } + + // Family of generated Generics + Set> generics(ClassOrInterface owner, Method method) { + if (computedGenericsOfMethods.containsKey(method)) + return computedGenericsOfMethods.get(method); + + Set> result = new HashSet<>(); + computedGenericsOfMethods.put(method, result); + + var genericsOfClass = generics(owner); + var simplifiedConstraints = new HashSet<>(this.simplifiedConstraints); + + HashSet typeVariables = new HashSet<>(); + HashSet typeVariablesOfFields = new HashSet<>(); + HashSet allTypeVariables = new HashSet<>(); + + for (var field : owner.getFieldDecl()) { + findTypeVariables(field.getType(), typeVariablesOfFields); + } + + //findTypeVariables(method.getReturnType(), typeVariables); + for (var arg : method.getParameterList().getFormalparalist()) { + findTypeVariables(arg.getType(), typeVariables); + } + + method.block.accept(new TracingStatementVisitor() { + @Override + public void visit(LocalVarDecl localVarDecl) { + findTypeVariables(localVarDecl.getType(), typeVariables); + } + + @Override + public void visit(MethodCall methodCall) { + super.visit(methodCall); + findTypeVariables(methodCall.getType(), typeVariables); + } + + @Override + public void visit(Assign assign) {} + }); + + // Type variables with bounds that are also type variables of the method + for (var typeVariable : new HashSet<>(typeVariables)) { + for (var pair : simplifiedConstraints) { + if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right)) { + result.add(new PairTPHsmallerTPH(pair.left, equality.getOrDefault(pair.right, pair.right))); + typeVariables.add(pair.right); + } + } + } + + var visitedMethods = new HashSet(); + method.block.accept(new TracingStatementVisitor() { + @Override + public void visit(MethodCall methodCall) { + super.visit(methodCall); + + if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) { + if (expressionReceiver.expr instanceof This) { + var optMethod = findMethod(owner, methodCall.name, methodCall.getArgumentList()); + if (optMethod.isEmpty()) return; + var method = optMethod.get(); + if (visitedMethods.contains(method)) return; + visitedMethods.add(method); + + var generics = generics(owner, method); + Set> all = new HashSet<>(generics); + + // Reflexive and Transitive closure + HashSet> toAdd = new HashSet<>(); + int sizeBefore; + do { + sizeBefore = all.size(); + toAdd.clear(); + for (var g1 : all) { + for (var g2 : all) { + if (g1 instanceof PairTPHsmallerTPH pair) { + if (g2.getLeft().equals(pair.getLeft()) && generics.stream().anyMatch(generic -> generic.getLeft().equals(pair.getRight()))) + toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) g1.getLeft(), (TypePlaceholder) g2.getRight())); + } + } + } + all.addAll(toAdd); + } while (sizeBefore < all.size()); + for (var generic : all) { + toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft())); + } + all.addAll(toAdd); + + HashSet newPairs = new HashSet<>(); + // Loop from hell + outer: + for (var tph : typeVariables) { + for (var generic : all) { + if (!(generic.getRight() instanceof TypePlaceholder type)) + continue; + + for (var pair : simplifiedConstraints) { + if (!(pair.left.equals(tph) && pair.right.equals(generic.getLeft()))) + continue; + + for (var tph2 : typeVariables) { + for (var pair2 : simplifiedConstraints) { + if (!(pair2.right.equals(tph2) && pair2.left.equals(type))) + continue; + if (tph.equals(tph2)) continue; + + var newPair = new PairTPHsmallerTPH(tph, tph2); + newPairs.add(newPair); + + if (!containsRelation(result, newPair)) + result.add(newPair); + continue outer; + } + } + } + } + } + simplifiedConstraints.addAll(newPairs); + } + } + } + }); + + // Type variables with bounds that are also type variables of fields + for (var typeVariable : new HashSet<>(typeVariables)) { + for (var pair : simplifiedConstraints) { + if (pair.left.equals(typeVariable) && typeVariablesOfFields.contains(pair.right)) { + result.add(new PairTPHsmallerTPH(pair.left, equality.getOrDefault(pair.right, pair.right))); + typeVariables.add(pair.right); + } + } + } + + // All unbounded type variables + outer: + for (var typeVariable : typeVariables) { + for (var pair : simplifiedConstraints) { + if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right)) + continue outer; + } + if (!hasBound(typeVariable, genericsOfClass)) + result.add(new PairTPHequalRefTypeOrWildcardType(typeVariable, OBJECT)); + } + + // All unbounded bounds + outer: + for (var pair : simplifiedConstraints) { + for (var pair2 : simplifiedConstraints) { + if (pair.right.equals(pair2.left)) + continue outer; + } + if (!hasBound(pair.right, genericsOfClass) && typeVariables.contains(pair.right)) + result.add(new PairTPHequalRefTypeOrWildcardType(pair.right, OBJECT)); + } + + eliminateCyclesAndInfima(result); + System.out.println(method.name + ": " + result); + + Set allUsedTPHs = new HashSet<>(); + allUsedTPHs.addAll(typeVariables); + allUsedTPHs.addAll(typeVariablesOfFields); + usedTPHsOfMethods.put(method, allUsedTPHs); + + return result; + } + + void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set> generics) { + if (type instanceof TypePlaceholder tph) { + tph = equality.getOrDefault(tph, tph); + + var concreteType = concreteTypes.get(tph); + if (concreteType != null) { + findAllBounds(concreteType, generics); + return; + } + + for (var rsp : simplifiedConstraints) { + var left = equality.getOrDefault(rsp.left, rsp.left); + var right = equality.getOrDefault(rsp.right, rsp.right); + if (left.equals(tph)) { + var pair = new PairTPHsmallerTPH(tph, right); + if (!generics.contains(pair)) { + generics.add(pair); + findAllBounds(right, generics); + } + return; + } + } + generics.add(new PairTPHequalRefTypeOrWildcardType(tph, OBJECT)); + } else if (type instanceof RefType refType) { + refType.getParaList().forEach(t -> findAllBounds(t, generics)); + } + } + + Set> generics(ClassOrInterface classOrInterface) { + if (computedGenericsOfClasses.containsKey(classOrInterface)) + return computedGenericsOfClasses.get(classOrInterface); + + Set> result = new HashSet<>(); + for (var field : classOrInterface.getFieldDecl()) { + findAllBounds(field.getType(), result); + } + computedGenericsOfClasses.put(classOrInterface, result); + eliminateCyclesAndInfima(result); + eliminateInnerTypeVariables(classOrInterface, result); + equalizeTypeVariables(result); + + System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + result); + return result; + } + + void equalizeTypeVariables(Set> input) { + for (var pair : new HashSet<>(input)) { + if (pair instanceof PairTPHsmallerTPH ptph) { + if (ptph.left.getVariance() == 1 && ptph.right.getVariance() == -1) { + equality.put(ptph.left, ptph.right); + input.remove(ptph); + for (var pair2 : new HashSet<>(input)) { + if (pair2 instanceof PairTPHsmallerTPH ptph2 && ptph2.right.equals(ptph.left)) { + input.remove(pair2); + input.add(new PairTPHsmallerTPH(ptph2.left, ptph.right)); + } + } + } + } + } + } + + void findTphs(RefTypeOrTPHOrWildcardOrGeneric type, Set tphs) { + if (type instanceof RefType refType) { + refType.getParaList().forEach(t -> findTphs(t, tphs)); + } else if (type instanceof TypePlaceholder tph) { + tph = equality.getOrDefault(tph, tph); + var concreteType = concreteTypes.get(tph); + if (concreteType != null) { + findTphs(concreteType, tphs); + return; + } + tphs.add(tph); + } + } + + void eliminateInnerTypeVariables(ClassOrInterface classOrInterface, Set> input) { + Set referenced = new HashSet<>(); + for (var field : classOrInterface.getFieldDecl()) { + findTphs(field.getType(), referenced); + } + for (var method : classOrInterface.getMethods()) { + generics(classOrInterface, method); + referenced.addAll(usedTPHsOfMethods.get(method)); + } + + var oldInput = new HashSet<>(input); + for (var pair : oldInput) { + if (!referenced.contains(pair.getLeft())) { + input.remove(pair); + for (var pair2 : oldInput) { + if (pair2.getRight().equals(pair.getLeft())) { + input.remove(pair2); + if (pair instanceof PairTPHsmallerTPH) + input.add(new PairTPHsmallerTPH((TypePlaceholder) pair2.getLeft(), (TypePlaceholder) pair.getRight())); + else + input.add(new PairTPHequalRefTypeOrWildcardType((TypePlaceholder) pair2.getLeft(), pair.getRight())); + } + } + } + } + } + + void eliminateCyclesAndInfima(Set> input) { + // Eliminate cycles + var cycles = findCycles(input); + for (var cycle : cycles) { + var newTph = TypePlaceholder.fresh(new NullToken()); + input.add(new PairTPHequalRefTypeOrWildcardType(newTph, OBJECT)); + cycle.add(cycle.get(0)); // Make it a complete cycle + for (var i = 0; i < cycle.size() - 1; i++) { + var left = cycle.get(i); + var right = cycle.get(i + 1); + var pair = new PairTPHsmallerTPH(left, right); + input.remove(pair); + equality.put(left, newTph); + } + } + // Eliminate infima + var foundInfima = false; + do { + foundInfima = false; + for (var constraint : new HashSet<>(input)) { + var left = (TypePlaceholder) constraint.getLeft(); + Set infima = new HashSet<>(); + for (var pair : input) { + if (pair instanceof PairTPHsmallerTPH stph) + if (pair.getLeft().equals(constraint.getLeft())) + infima.add(stph); + } + if (infima.size() > 1) { + foundInfima = true; + var newTph = TypePlaceholder.fresh(new NullToken()); + input.add(new PairTPHsmallerTPH(left, newTph)); + input.removeAll(infima); + for (var infimum : infima) { + equality.put(infimum.right, newTph); + new HashSet<>(input).forEach(pair -> { + if (pair.getLeft().equals(infimum.right)) { + input.remove(pair); + if (pair instanceof PairTPHsmallerTPH stph) { + input.add(new PairTPHsmallerTPH(newTph, stph.right)); + } else if (pair instanceof PairTPHequalRefTypeOrWildcardType rtph) { + input.add(new PairTPHequalRefTypeOrWildcardType(newTph, rtph.getRight())); + } + } else if (pair.getRight().equals(infimum.right)) { + input.remove(pair); + if (pair instanceof PairTPHsmallerTPH stph) { + input.add(new PairTPHsmallerTPH(stph.left, newTph)); + } + } + }); + } + } + } + } while (foundInfima); + } + + TargetType get(TypePlaceholder tph) { + if (equality.containsKey(tph)) { + return get(equality.get(tph)); + } + var type = concreteTypes.get(tph); + if (type == null) return new TargetGenericType(tph.getName()); + return convert(type); + } + } + + protected ByteArrayClassLoader classLoader; + protected SourceFile sourceFile; + + public ASTToTargetAST(List resultSets) { + this(resultSets, null, new ByteArrayClassLoader()); + } + + public ASTToTargetAST(List resultSets, SourceFile sourceFile, ByteArrayClassLoader classLoader) { + this.classLoader = classLoader; + this.sourceFile = sourceFile; + + all = new ArrayList<>(); + for (var set : resultSets) { + all.add(new Sigma(set)); + } + this.sigma = all.get(0); + } + + static Set allNodes(Set> input) { + return input.stream() + .filter(pair -> pair instanceof PairTPHsmallerTPH) + .flatMap(pair -> Stream.of((TypePlaceholder) pair.getLeft(), (TypePlaceholder) pair.getRight())).collect(Collectors.toSet()); + } + + static Set outgoingEdgesOf(TypePlaceholder tph, Set> input) { + return input.stream() + .filter(pair -> pair instanceof PairTPHsmallerTPH && pair.getLeft().equals(tph)) + .map(pair -> (TypePlaceholder) pair.getRight()).collect(Collectors.toSet()); + } + + static boolean containsEdge(TypePlaceholder a, TypePlaceholder b, Set> input) { + return input.stream().anyMatch(pair -> pair.getLeft().equals(a) && pair.getRight().equals(b)); + } + + // Tiernan simple cycles algorithm + // Adapted from https://github.com/jgrapht/jgrapht/blob/master/jgrapht-core/src/main/java/org/jgrapht/alg/cycle/TiernanSimpleCycles.java + static Set> findCycles(Set> input) { + Map indices = new HashMap<>(); + List path = new ArrayList<>(); + Set pathSet = new HashSet<>(); + Map> blocked = new HashMap<>(); + Set> cycles = new HashSet<>(); + + int index = 0; + for (var tph : allNodes(input)) { + blocked.put(tph, new HashSet<>()); + indices.put(tph, index++); + } + + var vertexIterator = allNodes(input).iterator(); + if (!vertexIterator.hasNext()) return cycles; + + TypePlaceholder startOfPath = null; + TypePlaceholder endOfPath = vertexIterator.next(); + TypePlaceholder temp = null; + int endIndex = 0; + boolean extensionFound = false; + path.add(endOfPath); + pathSet.add(endOfPath); + + while (true) { + do { + extensionFound = false; + for (TypePlaceholder n : outgoingEdgesOf(endOfPath, input)) { + int cmp = indices.get(n).compareTo(indices.get(path.get(0))); + if ((cmp > 0) && !pathSet.contains(n) && !blocked.get(endOfPath).contains(n)) { + path.add(n); + pathSet.add(n); + endOfPath = n; + extensionFound = true; + break; + } + } + } while (extensionFound); + + startOfPath = path.get(0); + if (containsEdge(endOfPath, startOfPath, input)) { + List cycle = new ArrayList<>(path); + cycles.add(cycle); + } + if (path.size() > 1) { + blocked.get(endOfPath).clear(); + endIndex = path.size() - 1; + path.remove(endIndex); + pathSet.remove(endOfPath); + --endIndex; + temp = endOfPath; + endOfPath = path.get(endIndex); + blocked.get(endOfPath).add(temp); + continue; + } + if (vertexIterator.hasNext()) { + path.clear(); + pathSet.clear(); + endOfPath = vertexIterator.next(); + path.add(endOfPath); + pathSet.add(endOfPath); + for (TypePlaceholder tph : blocked.keySet()) { + blocked.get(tph).clear(); + } + continue; + } + break; + } + return cycles; + } + + Optional findMethod(ClassOrInterface owner, String name, ArgumentList argumentList) { + return owner.getMethods().stream().filter( + m -> m.name.equals(name) && parameterEquals(m.getParameterList(), argumentList) + ).findFirst(); + } + + boolean parameterEquals(ParameterList parameterList, ArgumentList argumentList) { + var pars = parameterList.getFormalparalist(); + var arguments = argumentList.getArguments(); + if (pars.size() != arguments.size()) + return false; + + for (var i = 0; i < pars.size(); i++) { + var type1 = convert(pars.get(i).getType()); + var type2 = convert(arguments.get(i).getType()); + if (type2 instanceof TargetGenericType && type1 instanceof TargetGenericType) + return true; + if (!type1.equals(type2)) return false; + } + + return true; + } + + Set convert(Set> result) { + return result.stream().map(p -> { + if (p instanceof PairTPHsmallerTPH pair) { + return new TargetGeneric(pair.left.getName(), new TargetGenericType(pair.right.getName())); + } else if (p instanceof PairTPHequalRefTypeOrWildcardType pair) { + return new TargetGeneric(pair.left.getName(), convert(pair.right)); + } else { + throw new IllegalArgumentException(); + } + }).collect(Collectors.toSet()); + } + + public TargetClass convert(ClassOrInterface input) { + currentClass = input; + TargetBlock fieldInitializer = null; + if (input.getfieldInitializations().isPresent()) + fieldInitializer = convert(input.getfieldInitializations().get().block); + TargetBlock finalFieldInitializer = fieldInitializer; + + return new TargetClass(input.getModifiers(), input.getClassName().toString(), convert(input.getSuperClass()), + convert(sigma.generics(input)), + input.getSuperInterfaces().stream().map(this::convert).toList(), + input.getConstructors().stream().map(constructor -> this.convert(constructor, finalFieldInitializer)).flatMap(List::stream).toList(), + input.getFieldDecl().stream().map(this::convert).toList(), + input.getMethods().stream().map(this::convert).flatMap(List::stream).toList() + ); + } + + private List convert(ParameterList input) { + return input.getFormalparalist().stream() + .map(param -> new MethodParameter(convert(param.getType()), param.getName())).toList(); + } + + private List convert(Constructor input, TargetBlock fieldInitializer) { + sigma = all.get(0); + List result = new ArrayList<>(); + Set> parameterSet = new HashSet<>(); + + for (var s : all) { + sigma = s; + var generics = sigma.generics(currentClass, input); + List params = convert(input.getParameterList()); + if (parameterSet.stream().noneMatch(p -> p.equals(params))) { + result.add(new TargetConstructor(input.modifier, convert(generics), params, convert(input.block), fieldInitializer)); + parameterSet.add(params); + } + } + + return result; + } + + private List convert(Method input) { + sigma = all.get(0); + List result = new ArrayList<>(); + Set> parameterSet = new HashSet<>(); + + for (var s : all) { + sigma = s; + var generics = sigma.generics(currentClass, input); + List params = convert(input.getParameterList()); + if (parameterSet.stream().noneMatch(p -> p.equals(params))) { + result.add(new TargetMethod( + input.modifier, + input.name, convert(generics), params, + convert(input.getReturnType()), + convert(input.block) + )); + parameterSet.add(params); + } + } + + return result; + } + + protected TargetBlock convert(Block block) { + return new TargetBlock(block.statements.stream().map(this::convert).toList()); + } + + protected TargetExpression convert(Expression expr) { + var converter = new StatementToTargetExpression(this); + expr.accept(converter); + return converter.result; + } + + private TargetField convert(Field input) { + return new TargetField( + input.modifier, + convert(input.getType()), + input.getName() + ); + } + + private final Set usedFunN = new HashSet<>(); + + protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input) { + return input.acceptTV(new TypeVisitor<>() { + @Override + public TargetType visit(RefType refType) { + var name = refType.getName().toString(); + if (name.equals("void")) return null; + + var params = refType.getParaList().stream().map(ASTToTargetAST.this::convert).toList(); + if (name.matches("Fun\\d\\$\\$")) { // TODO This seems like a bad idea + if (!usedFunN.contains(params.size() - 1)) { + usedFunN.add(params.size() - 1); + classLoader.loadClass(FunNGenerator.getInstance().generateSuperBytecode(params.size() - 1)); + } + return new TargetFunNType(params.size() - 1, params); + } + return new TargetRefType(name, params); + } + + @Override + public TargetType visit(SuperWildcardType superWildcardType) { + return new TargetSuperWildcard(convert(superWildcardType.getInnerType())); + } + + @Override + public TargetType visit(TypePlaceholder typePlaceholder) { + return sigma.get(typePlaceholder); + } + + @Override + public TargetType visit(ExtendsWildcardType extendsWildcardType) { + return new TargetExtendsWildcard(convert(extendsWildcardType.getInnerType())); + } + + @Override + public TargetType visit(GenericRefType genericRefType) { + return new TargetGenericType(genericRefType.getParsedName()); + } + }); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java new file mode 100644 index 00000000..8ca6e052 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java @@ -0,0 +1,358 @@ +package de.dhbwstuttgart.target.generate; + +import de.dhbwstuttgart.exceptions.NotImplementedException; +import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal; +import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.syntaxtree.ClassOrInterface; +import de.dhbwstuttgart.syntaxtree.StatementVisitor; +import de.dhbwstuttgart.syntaxtree.statement.*; +import de.dhbwstuttgart.syntaxtree.type.RefType; +import de.dhbwstuttgart.target.tree.MethodParameter; +import de.dhbwstuttgart.target.tree.expression.*; +import de.dhbwstuttgart.target.tree.type.TargetFunNType; +import de.dhbwstuttgart.target.tree.type.TargetRefType; +import de.dhbwstuttgart.target.tree.type.TargetSpecializedType; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class StatementToTargetExpression implements StatementVisitor { + + public StatementToTargetExpression(ASTToTargetAST converter) { + this.converter = converter; + } + + public TargetExpression result; + private final ASTToTargetAST converter; + + @Override + public void visit(ArgumentList argumentList) { + throw new NotImplementedException(); + } + + @Override + public void visit(LambdaExpression lambdaExpression) { + var parameters = StreamSupport + .stream(lambdaExpression.params.spliterator(), false) + .map(p -> new MethodParameter(converter.convert(p.getType()), p.getName())) + .toList(); + + List captures = new ArrayList<>(); + lambdaExpression.methodBody.accept(new TracingStatementVisitor() { + // TODO The same mechanism is implemented in Codegen, maybe use it from there? + final Stack> localVariables = new Stack<>(); + { localVariables.push(new HashSet<>()); } + + boolean hasLocalVar(String name) { + for (var localVariables : this.localVariables) { + if (localVariables.contains(name)) return true; + } + return false; + } + + @Override + public void visit(Block block) { + localVariables.push(new HashSet<>()); + super.visit(block); + localVariables.pop(); + } + + @Override + public void visit(LocalVar localVar) { + super.visit(localVar); + var capture = new MethodParameter(converter.convert(localVar.getType()), localVar.name); + if (!hasLocalVar(localVar.name) && !parameters.contains(capture) && !captures.contains(capture)) + captures.add(capture); + } + + @Override + public void visit(LocalVarDecl varDecl) { + var localVariables = this.localVariables.peek(); + localVariables.add(varDecl.getName()); + } + + @Override + public void visit(LambdaExpression lambda) {} // Don't look at lambda expressions + }); + + result = new TargetLambdaExpression( + new TargetFunNType(parameters.size(), parameters.stream().map(MethodParameter::type).toList()), + captures, parameters, converter.convert(lambdaExpression.getReturnType()), converter.convert(lambdaExpression.methodBody) + ); + } + + @Override + public void visit(Assign assign) { + TargetExpression left; + if (assign.lefSide instanceof AssignToLocal) { + left = converter.convert(((AssignToLocal) assign.lefSide).localVar); + } else { + left = converter.convert(((AssignToField) assign.lefSide).field); + } + + result = new TargetAssign(converter.convert(assign.getType()), left, converter.convert(assign.rightSide)); + } + + @Override + public void visit(BinaryExpr binary) { + result = switch (binary.operation) { + case ADD -> new TargetBinaryOp.Add(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case SUB -> new TargetBinaryOp.Sub(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case MUL -> new TargetBinaryOp.Mul(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case MOD -> new TargetBinaryOp.Rem(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case AND -> new TargetBinaryOp.And(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case OR -> new TargetBinaryOp.Or(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case DIV -> new TargetBinaryOp.Div(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case LESSTHAN -> new TargetBinaryOp.Less(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case BIGGERTHAN -> new TargetBinaryOp.Greater(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case LESSEQUAL -> new TargetBinaryOp.LessOrEqual(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case BIGGEREQUAL -> new TargetBinaryOp.GreaterOrEqual(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case EQUAL -> new TargetBinaryOp.Equal(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + case NOTEQUAL -> new TargetBinaryOp.NotEqual(converter.convert(binary.getType()), converter.convert(binary.lexpr), converter.convert(binary.rexpr)); + }; + } + + @Override + public void visit(Block block) { + result = converter.convert(block); + } + + @Override + public void visit(CastExpr castExpr) { + result = new TargetCast(converter.convert(castExpr.getType()), converter.convert(castExpr.expr)); + } + + @Override + public void visit(EmptyStmt emptyStmt) { + result = null; + } + + @Override + public void visit(FieldVar fieldVar) { + result = new TargetFieldVar( + converter.convert(fieldVar.getType()), + converter.convert(fieldVar.receiver.getType()), + false, + converter.convert(fieldVar.receiver), + fieldVar.fieldVarName); + } + + @Override + public void visit(ForStmt forStmt) { + // TODO Doesn't seem to be fully implemented yet + throw new NotImplementedException(); + } + + @Override + public void visit(IfStmt ifStmt) { + result = new TargetIf( + converter.convert(ifStmt.expr), + converter.convert(ifStmt.then_block), + converter.convert(ifStmt.else_block) + ); + } + + @Override + public void visit(InstanceOf instanceOf) { + result = new TargetInstanceOf(converter.convert(instanceOf.lexpr), converter.convert(instanceOf.rexpr.getType())); + } + + @Override + public void visit(LocalVar localVar) { + result = new TargetLocalVar(converter.convert(localVar.getType()), localVar.name); + } + + @Override + public void visit(LocalVarDecl localVarDecl) { + // TODO No value, is this correct? + result = new TargetVarDecl(converter.convert(localVarDecl.getType()), localVarDecl.getName(), null); + } + + static boolean convertsTo(TargetType from, TargetType to) { + if (to.equals(TargetType.Object)) return true; // TODO Consider type coercion and suptyping + return to.equals(from); + } + + Method findMethod(JavaClassName className, String name, List args) { + if (converter.sourceFile != null && converter.sourceFile.imports.contains(className)) { + try { + var clazz = converter.classLoader.loadClass(className.toString()); + + outer: for (var method : clazz.getMethods()) { + if (method.getParameterTypes().length != args.size()) continue; + if (!method.getName().equals(name)) continue; + + for (var i = 0; i < method.getParameterTypes().length; i++) { + var param = method.getParameterTypes()[i]; + var arg = args.get(i); + if (param.isPrimitive()) { + arg = TargetType.toPrimitive(arg); + } + if (!convertsTo(arg, Objects.requireNonNull(TargetType.toTargetType(param)))) continue outer; + } + return method; + } + } catch (ClassNotFoundException ignored) {} + } + if (converter.sourceFile != null) { // TODO Multiple source files + var thisClass = converter.sourceFile.KlassenVektor.stream() + .filter(classOrInterface -> classOrInterface.getClassName().equals(className)).findFirst(); + + if (thisClass.isPresent()) { + var superClass = thisClass.get().getSuperClass().getName(); + return findMethod(superClass, name, args); + } + } + return null; + } + + @Override + public void visit(MethodCall methodCall) { + var receiverType = converter.convert(methodCall.receiver.getType()); + var isFunNType = receiverType instanceof TargetFunNType; + var returnType = isFunNType ? TargetType.Object : converter.convert(methodCall.getType()); + var receiverName = new JavaClassName(converter.convert(methodCall.receiver.getType()).name()); + var argList = methodCall.arglist.getArguments().stream().map(expr -> converter.convert(expr.getType())).toList(); + + Method foundMethod = null; + if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) { + var thisMethod = converter.findMethod(converter.currentClass, methodCall.name, methodCall.arglist); + if (thisMethod.isEmpty()) { + foundMethod = findMethod(converter.currentClass.getSuperClass().getName(), methodCall.name, argList); + } + } else { + foundMethod = findMethod(receiverName, methodCall.name, argList); + } + + if (foundMethod != null) { + returnType = TargetType.toTargetType(foundMethod.getReturnType()); + argList = Stream.of(foundMethod.getParameterTypes()).map(TargetType::toTargetType).toList(); + } + + result = new TargetMethodCall( + converter.convert(methodCall.getType()), + returnType, argList, + converter.convert(methodCall.receiver), + methodCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), + receiverType, + methodCall.name, false, isFunNType + ); + } + + @Override + public void visit(NewClass newClass) { + result = new TargetNew( + new TargetRefType(newClass.name), + newClass.getArgumentList().getArguments().stream().map(converter::convert).toList() + ); + } + + @Override + public void visit(NewArray newArray) { + // TODO + throw new NotImplementedException(); + } + + @Override + public void visit(Return aReturn) { + result = new TargetReturn(converter.convert(aReturn.retexpr)); + } + + @Override + public void visit(ReturnVoid aReturn) { + result = new TargetReturn(null); + } + + @Override + public void visit(StaticClassName staticClassName) { + result = new TargetClassName(converter.convert(staticClassName.getType())); + } + + @Override + public void visit(Super aSuper) { + result = new TargetSuper(converter.convert(aSuper.getType())); + } + + @Override + public void visit(This aThis) { + result = new TargetThis(converter.convert(aThis.getType())); + } + + @Override + public void visit(WhileStmt whileStmt) { + result = new TargetWhile(converter.convert(whileStmt.expr), converter.convert(whileStmt.loopBlock)); + } + + @Override + public void visit(DoStmt whileStmt) { + throw new NotImplementedException(); + } + + // TODO These two might not be necessary + @Override + public void visit(AssignToField assignLeftSide) { + result = converter.convert(assignLeftSide.field); + } + + @Override + public void visit(AssignToLocal assignLeftSide) { + result = converter.convert(assignLeftSide.localVar); + } + + @Override + public void visit(SuperCall superCall) { + var aSuper = converter.convert(converter.currentClass.getSuperClass()); + var type = converter.convert(superCall.getType()); + result = new TargetMethodCall( + type, type, + superCall.argTypes == null ? List.of() : superCall.argTypes.stream().map(converter::convert).toList(), + new TargetSuper(aSuper), + superCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), + aSuper, + superCall.name, false, false + ); + } + + @Override + public void visit(ExpressionReceiver expressionReceiver) { + result = converter.convert(expressionReceiver.expr); + } + + @Override + public void visit(UnaryExpr unaryExpr) { + result = switch (unaryExpr.operation) { + case NOT -> new TargetUnaryOp.Not(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + case MINUS -> new TargetUnaryOp.Negate(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + case PREINCREMENT -> new TargetUnaryOp.PreIncrement(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + case PREDECREMENT -> new TargetUnaryOp.PreDecrement(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + case POSTINCREMENT -> new TargetUnaryOp.PostIncrement(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + case PLUS -> new TargetUnaryOp.Add(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + case POSTDECREMENT -> new TargetUnaryOp.PostDecrement(converter.convert(unaryExpr.getType()), converter.convert(unaryExpr.expr)); + }; + } + + @Override + public void visit(Literal literal) { + if (literal.value instanceof Integer + || literal.value instanceof Short + || literal.value instanceof Byte) { + + result = new TargetLiteral.IntLiteral((int) literal.value); + } else if (literal.value instanceof Float) { + result = new TargetLiteral.FloatLiteral((float) literal.value); + } else if (literal.value instanceof Double) { + result = new TargetLiteral.DoubleLiteral((double) literal.value); + } else if (literal.value instanceof Long) { + result = new TargetLiteral.LongLiteral((long) literal.value); + } else if (literal.value instanceof Character) { + result = new TargetLiteral.CharLiteral((char) literal.value); + } else if (literal.value instanceof String) { + result = new TargetLiteral.StringLiteral((String) literal.value); + } else if (literal.value instanceof Boolean) { + result = new TargetLiteral.BooleanLiteral((boolean) literal.value); + } + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java b/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java new file mode 100644 index 00000000..6fc9c248 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java @@ -0,0 +1,159 @@ +package de.dhbwstuttgart.target.generate; + +import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal; +import de.dhbwstuttgart.syntaxtree.StatementVisitor; +import de.dhbwstuttgart.syntaxtree.statement.*; + + +// This visitor walks the entire tree, individual methods may be overridden +public abstract class TracingStatementVisitor implements StatementVisitor { + + @Override + public void visit(MethodCall methodCall) { + methodCall.receiver.accept(this); + methodCall.getArgumentList().accept(this); + } + + @Override + public void visit(ArgumentList argumentList) { + argumentList.getArguments().forEach(expr -> expr.accept(this)); + } + + @Override + public void visit(LambdaExpression lambdaExpression) { + lambdaExpression.methodBody.accept(this); + } + + @Override + public void visit(Assign assign) { + assign.rightSide.accept(this); + } + + @Override + public void visit(BinaryExpr binary) { + binary.lexpr.accept(this); + binary.rexpr.accept(this); + } + + @Override + public void visit(Block block) { + for (var expr : block.statements) + expr.accept(this); + } + + @Override + public void visit(CastExpr castExpr) { + + } + + @Override + public void visit(EmptyStmt emptyStmt) { + + } + + @Override + public void visit(FieldVar fieldVar) { + + } + + @Override + public void visit(ForStmt forStmt) { + forStmt.body_Loop_block.accept(this); + } + + @Override + public void visit(IfStmt ifStmt) { + ifStmt.then_block.accept(this); + ifStmt.else_block.accept(this); + } + + @Override + public void visit(InstanceOf instanceOf) { + + } + + @Override + public void visit(LocalVar localVar) { + + } + + @Override + public void visit(LocalVarDecl localVarDecl) { + + } + + @Override + public void visit(NewClass newClass) { + this.visit((MethodCall) newClass); + } + + @Override + public void visit(NewArray newArray) { + newArray.expr.forEach(expr -> expr.accept(this)); + } + + @Override + public void visit(Return aReturn) { + aReturn.retexpr.accept(this); + } + + @Override + public void visit(ReturnVoid aReturn) { + + } + + @Override + public void visit(StaticClassName staticClassName) { + + } + + @Override + public void visit(Super aSuper) { + + } + + @Override + public void visit(This aThis) { + + } + + @Override + public void visit(WhileStmt whileStmt) { + whileStmt.loopBlock.accept(this); + } + + @Override + public void visit(DoStmt whileStmt) { + whileStmt.loopBlock.accept(this); + } + + @Override + public void visit(AssignToField assignLeftSide) { + + } + + @Override + public void visit(AssignToLocal assignLeftSide) { + + } + + @Override + public void visit(SuperCall superCall) { + + } + + @Override + public void visit(ExpressionReceiver expressionReceiver) { + expressionReceiver.expr.accept(this); + } + + @Override + public void visit(UnaryExpr unaryExpr) { + unaryExpr.expr.accept(this); + } + + @Override + public void visit(Literal literal) { + + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/MethodParameter.java b/src/main/java/de/dhbwstuttgart/target/tree/MethodParameter.java new file mode 100644 index 00000000..4ae31828 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/MethodParameter.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record MethodParameter(TargetType type, String name) { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java new file mode 100644 index 00000000..50cb9f71 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java @@ -0,0 +1,47 @@ +package de.dhbwstuttgart.target.tree; + +import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import de.dhbwstuttgart.target.tree.type.TargetRefType; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public record TargetClass(int modifiers, String qualifiedName, TargetType superType, Set generics, List implementingInterfaces, + List constructors, List fields, List methods) { + + public TargetClass(int modifiers, String qualifiedName) { + this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + public TargetClass(int modifiers, String qualifiedName, List implementingInterfaces) { + this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + + public String getName() { + return qualifiedName.replaceAll("\\.", "/"); + } + + public void addMethod(int access, String name, Set generics, List parameterTypes, TargetType returnType, TargetBlock block) { + this.methods.add(new TargetMethod(access, name, generics, parameterTypes, returnType, block)); + } + + public void addMethod(int access, String name, List parameterTypes, TargetType returnType, TargetBlock block) { + addMethod(access, name, Set.of(), parameterTypes, returnType, block); + } + + public void addConstructor(int access, Set generics, List paramterTypes, TargetBlock block) { + this.constructors.add(new TargetConstructor(access, generics, paramterTypes, block, null)); + } + + public void addConstructor(int access, List paramterTypes, TargetBlock block) { + addConstructor(access, Set.of(), paramterTypes, block); + } + + public void addField(int access, TargetRefType type, String name) { + this.fields.add(new TargetField(access, type, name)); + } +} + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java new file mode 100644 index 00000000..4d2cca14 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java @@ -0,0 +1,19 @@ +package de.dhbwstuttgart.target.tree; + +import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.util.List; +import java.util.Set; + +public record TargetConstructor(int access, Set generics, List parameters, TargetBlock block, TargetBlock fieldInitializer) { + + public String getDescriptor() { + return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new)); + } + + public String getSignature() { + return TargetMethod.getSignature(generics, parameters, null); + } +} + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java new file mode 100644 index 00000000..3a22bade --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree; + +import de.dhbwstuttgart.target.tree.type.TargetType; +import org.objectweb.asm.Opcodes; + +public record TargetField(int access, TargetType type, String name) { + public boolean isStatic() { + return (access & Opcodes.ACC_STATIC) != 0; + } +} + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetGeneric.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetGeneric.java new file mode 100644 index 00000000..ad70ae71 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetGeneric.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetGeneric(String name, TargetType bound) { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java new file mode 100644 index 00000000..f15acad4 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree; + +import java.util.List; + +public record TargetInterface(String name, List methods, List extendedInterfaces) { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java new file mode 100644 index 00000000..2865603f --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java @@ -0,0 +1,49 @@ +package de.dhbwstuttgart.target.tree; + +import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import de.dhbwstuttgart.target.tree.type.TargetType; +import org.objectweb.asm.Opcodes; + +import java.util.List; +import java.util.Set; + +public record TargetMethod(int access, String name, Set generics, List parameters, TargetType returnType, TargetBlock block) { + public static String getDescriptor(TargetType returnType, TargetType... parameters) { + String ret = "("; + for (var parameterType : parameters) { + ret += parameterType.toSignature(); + } + ret += ")"; + if (returnType == null) ret += "V"; + else ret += returnType.toSignature(); + return ret; + } + + public static String getSignature(Set generics, List parameters, TargetType returnType) { + String ret = "<"; + for (var generic : generics) { + ret += generic.name() + ":" + generic.bound().toGenericSignature(); + } + ret += ">("; + for (var param : parameters) { + ret += param.type().toGenericSignature(); + } + ret += ")"; + if (returnType == null) ret += "V"; + else ret += returnType.toGenericSignature(); + return ret; + } + + public String getDescriptor() { + return getDescriptor(returnType, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new)); + } + + public String getSignature() { + return getSignature(generics, parameters, returnType); + } + + public boolean isStatic() { + return (access & Opcodes.ACC_STATIC) != 0; + } +} + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java new file mode 100644 index 00000000..609de796 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetAssign(TargetType type, TargetExpression left, TargetExpression right) implements TargetStatementExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java new file mode 100644 index 00000000..12d56d5b --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java @@ -0,0 +1,48 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.expression.TargetExpression; +import de.dhbwstuttgart.target.tree.type.TargetType; + +public sealed interface TargetBinaryOp extends TargetExpression { + TargetExpression left(); + TargetExpression right(); + + // Arithmetic + record Add(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Sub(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Div(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Mul(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Rem(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + + // Bitwise + record BAnd(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record BOr(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record XOr(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Shl(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Shr(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record UShr(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + + // Conditional + record And(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + record Or(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} + + sealed interface TargetRelationalOp extends TargetBinaryOp { + @Override + default TargetType type() { + return TargetType.Boolean; + } + TargetType exprType(); + } + + // Comparison + // exprType is the type that both arguments get converted to before comparison + record Equal(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {} + record Greater(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {} + record GreaterOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {} + record Less(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {} + record LessOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {} + record NotEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {} + +} + + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBlock.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBlock.java new file mode 100644 index 00000000..87cc3b5e --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBlock.java @@ -0,0 +1,12 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.util.List; + +public record TargetBlock(List statements) implements TargetExpression { + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBreak.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBreak.java new file mode 100644 index 00000000..ae918c44 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBreak.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetBreak() implements TargetExpression { + + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetCast.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetCast.java new file mode 100644 index 00000000..f95bc9a8 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetCast.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetCast(TargetType type, TargetExpression expr) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetClassName.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetClassName.java new file mode 100644 index 00000000..7131acac --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetClassName.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetClassName(TargetType type) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetContinue.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetContinue.java new file mode 100644 index 00000000..42ebc995 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetContinue.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetContinue() implements TargetExpression { + + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java new file mode 100644 index 00000000..64146f7e --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java @@ -0,0 +1,9 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.*; + +public sealed interface TargetExpression + permits TargetBinaryOp, TargetBlock, TargetBreak, TargetCast, TargetClassName, TargetContinue, TargetFieldVar, TargetFor, TargetForEach, TargetIf, TargetInstanceOf, TargetLambdaExpression, TargetLiteral, TargetLocalVar, TargetReturn, TargetStatementExpression, TargetSuper, TargetSwitch, TargetTernary, TargetThis, TargetUnaryOp, TargetVarDecl, TargetWhile { + + TargetType type(); +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java new file mode 100644 index 00000000..5b82f22d --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java @@ -0,0 +1,7 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetRefType; +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetFieldVar(TargetType type, TargetType owner, boolean isStatic, TargetExpression left, String right) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFor.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFor.java new file mode 100644 index 00000000..2ce402ab --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFor.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetFor(TargetExpression init, TargetExpression termination, TargetExpression increment, TargetExpression body) implements TargetExpression { + + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java new file mode 100644 index 00000000..392b84e4 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetForEach(TargetExpression vardecl, TargetExpression list) implements TargetExpression { + + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java new file mode 100644 index 00000000..78bc766b --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java @@ -0,0 +1,10 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetIf(TargetExpression cond, TargetExpression if_body, TargetExpression else_body) implements TargetExpression { + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetInstanceOf.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetInstanceOf.java new file mode 100644 index 00000000..c1f104e2 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetInstanceOf.java @@ -0,0 +1,12 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.syntaxtree.statement.Expression; +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetInstanceOf(TargetExpression left, TargetType right) implements TargetExpression { + + @Override + public TargetType type() { + return TargetType.Boolean; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLambdaExpression.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLambdaExpression.java new file mode 100644 index 00000000..718fb663 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLambdaExpression.java @@ -0,0 +1,10 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.MethodParameter; +import de.dhbwstuttgart.target.tree.TargetField; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.util.List; + +public record TargetLambdaExpression(TargetType type, List captures, List params, TargetType returnType, TargetBlock block) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLiteral.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLiteral.java new file mode 100644 index 00000000..c4673682 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLiteral.java @@ -0,0 +1,56 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public sealed interface TargetLiteral extends TargetExpression { + Object value(); + + record BooleanLiteral(Boolean value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.Boolean; + } + } + + record CharLiteral(Character value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.Char; + } + } + + record IntLiteral(Integer value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.Integer; + } + } + + record LongLiteral(Long value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.Long; + } + } + + record FloatLiteral(Float value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.Float; + } + } + + record DoubleLiteral(Double value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.Double; + } + } + + record StringLiteral(String value) implements TargetLiteral { + @Override + public TargetType type() { + return TargetType.String; + } + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLocalVar.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLocalVar.java new file mode 100644 index 00000000..e4fa977f --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetLocalVar.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetLocalVar(TargetType type, String name) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java new file mode 100644 index 00000000..50a31471 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java @@ -0,0 +1,19 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.MethodParameter; +import de.dhbwstuttgart.target.tree.TargetMethod; +import de.dhbwstuttgart.target.tree.type.TargetRefType; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.util.List; + +public record TargetMethodCall(TargetType type, TargetType returnType, List parameterTypes, TargetExpression expr, List args, TargetType owner, String name, boolean isStatic, boolean isInterface) implements TargetStatementExpression { + public TargetMethodCall(TargetType type, TargetExpression expr, List args, TargetType owner, String name, boolean isStatic, boolean isInterface) { + this(type, type, args.stream().map(TargetExpression::type).toList(), expr, args, owner, name, isStatic, isInterface); + } + + + public String getDescriptor() { + return TargetMethod.getDescriptor(returnType, parameterTypes.toArray(TargetType[]::new)); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java new file mode 100644 index 00000000..e9d4ce8c --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java @@ -0,0 +1,12 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.TargetMethod; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.util.List; + +public record TargetNew(TargetType type, List params) implements TargetStatementExpression { + public String getDescriptor() { + return TargetMethod.getDescriptor(null, params.stream().map(TargetExpression::type).toArray(TargetType[]::new)); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetReturn.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetReturn.java new file mode 100644 index 00000000..c419cc1c --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetReturn.java @@ -0,0 +1,10 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetReturn(TargetExpression expression) implements TargetExpression { + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetStatementExpression.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetStatementExpression.java new file mode 100644 index 00000000..2d804d6a --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetStatementExpression.java @@ -0,0 +1,4 @@ +package de.dhbwstuttgart.target.tree.expression; + +public sealed interface TargetStatementExpression extends TargetExpression permits TargetAssign, TargetMethodCall, TargetNew, TargetUnaryOp.PostDecrement, TargetUnaryOp.PostIncrement, TargetUnaryOp.PreDecrement, TargetUnaryOp.PreIncrement { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetSuper.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetSuper.java new file mode 100644 index 00000000..ddffe386 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetSuper.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetSuper(TargetType type) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetSwitch.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetSwitch.java new file mode 100644 index 00000000..a6823913 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetSwitch.java @@ -0,0 +1,16 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.syntaxtree.statement.Expression; +import de.dhbwstuttgart.target.tree.type.TargetType; + +import java.util.List; + +public record TargetSwitch(Expression expr, List cases, Expression default_) implements TargetExpression { + + @Override + public TargetType type() { + return null; + } + + record Case(Expression value, Expression body) {} +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetTernary.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetTernary.java new file mode 100644 index 00000000..17349489 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetTernary.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetTernary(TargetType type, TargetExpression cond, TargetExpression ifTrue, TargetExpression ifFalse) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetThis.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetThis.java new file mode 100644 index 00000000..fe9b722c --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetThis.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetThis(TargetType type) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java new file mode 100644 index 00000000..92e57647 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java @@ -0,0 +1,16 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public sealed interface TargetUnaryOp extends TargetExpression { + TargetExpression expr(); + + record Negate(TargetType type, TargetExpression expr) implements TargetUnaryOp {} + record Add(TargetType type, TargetExpression expr) implements TargetUnaryOp {} + record Not(TargetType type, TargetExpression expr) implements TargetUnaryOp {} + + record PreIncrement(TargetType type, TargetExpression expr) implements TargetStatementExpression, TargetUnaryOp {} + record PostIncrement(TargetType type, TargetExpression expr) implements TargetStatementExpression, TargetUnaryOp {} + record PreDecrement(TargetType type, TargetExpression expr) implements TargetStatementExpression, TargetUnaryOp {} + record PostDecrement(TargetType type, TargetExpression expr) implements TargetStatementExpression, TargetUnaryOp {} +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetVarDecl.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetVarDecl.java new file mode 100644 index 00000000..b1eda5e7 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetVarDecl.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetVarDecl(TargetType varType, String name, TargetExpression value) implements TargetExpression { + + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java new file mode 100644 index 00000000..62ff90dc --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java @@ -0,0 +1,11 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetWhile(TargetExpression cond, TargetExpression body) implements TargetExpression { + + @Override + public TargetType type() { + return null; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetExtendsWildcard.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetExtendsWildcard.java new file mode 100644 index 00000000..7e5ab002 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetExtendsWildcard.java @@ -0,0 +1,24 @@ +package de.dhbwstuttgart.target.tree.type; + +public record TargetExtendsWildcard(TargetType innerType) implements TargetType { + @Override + public String toSignature() { + return innerType.toSignature(); + } + + @Override + public String toGenericSignature() { + return innerType.toGenericSignature(); + } + + @Override + public String getInternalName() { + return innerType.getInternalName(); + } + + @Override + public String name() { + return innerType.name(); + } +} + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java new file mode 100644 index 00000000..e9379de6 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java @@ -0,0 +1,20 @@ +package de.dhbwstuttgart.target.tree.type; + +import java.util.List; + +public record TargetFunNType(int N, List params) implements TargetSpecializedType { + @Override + public String getInternalName() { + return "Fun" + N + "$$"; + } + + @Override + public String name() { + return getInternalName(); + } + + @Override + public String toSignature() { + return "L" + getInternalName() + ";"; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetGenericType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetGenericType.java new file mode 100644 index 00000000..15900fb4 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetGenericType.java @@ -0,0 +1,18 @@ +package de.dhbwstuttgart.target.tree.type; + +public record TargetGenericType(String name) implements TargetType { + @Override + public String toSignature() { + return "Ljava/lang/Object;"; // TODO Use bounds for this? + } + + @Override + public String toGenericSignature() { + return "T" + getInternalName() + ";"; + } + + @Override + public String getInternalName() { + return name; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetPrimitiveType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetPrimitiveType.java new file mode 100644 index 00000000..9d3bbf46 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetPrimitiveType.java @@ -0,0 +1,19 @@ +package de.dhbwstuttgart.target.tree.type; + +public record TargetPrimitiveType(String name) implements TargetType { + + @Override + public String toSignature() { + return getInternalName(); + } + + @Override + public String toGenericSignature() { + return toSignature(); + } + + @Override + public String getInternalName() { + return name; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java new file mode 100644 index 00000000..0d043555 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java @@ -0,0 +1,33 @@ +package de.dhbwstuttgart.target.tree.type; + +import java.util.List; +import java.util.Objects; + +public record TargetRefType(String name, List params) implements TargetSpecializedType { + public TargetRefType(String name) { + this(name, List.of()); + } + + public String getInternalName() { + return this.name.replaceAll("\\.", "/"); + } + + @Override + public String toSignature() { + return "L" + getInternalName() + ";"; + } + + // Type erasure means we need to override hashCode and equals to only consider the name + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + @Override + public boolean equals(Object other) { + if (other instanceof TargetRefType refType) { + return refType.name.equals(name); + } + return false; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetSpecializedType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetSpecializedType.java new file mode 100644 index 00000000..2a74307c --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetSpecializedType.java @@ -0,0 +1,21 @@ +package de.dhbwstuttgart.target.tree.type; + +import java.util.List; + +public sealed interface TargetSpecializedType extends TargetType permits TargetFunNType, TargetRefType { + List params(); + + @Override + default String toGenericSignature() { + String ret = "L" + getInternalName(); + if (params().size() > 0) { + ret += "<"; + for (var param : params()) { + ret += param.toGenericSignature(); + } + ret += ">"; + } + ret += ";"; + return ret; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetSuperWildcard.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetSuperWildcard.java new file mode 100644 index 00000000..bf16e143 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetSuperWildcard.java @@ -0,0 +1,25 @@ +package de.dhbwstuttgart.target.tree.type; + +public record TargetSuperWildcard(TargetType innerType) implements TargetType { + @Override + public String toSignature() { + return innerType.toSignature(); + } + + @Override + public String toGenericSignature() { + return innerType.toGenericSignature(); + } + + @Override + public String getInternalName() { + return innerType.getInternalName(); + } + + @Override + public String name() { + return innerType.name(); + } +} + + diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java new file mode 100644 index 00000000..36d71af1 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java @@ -0,0 +1,59 @@ +package de.dhbwstuttgart.target.tree.type; + +public sealed interface TargetType + permits TargetExtendsWildcard, TargetGenericType, TargetSpecializedType, TargetSuperWildcard, TargetPrimitiveType { + + // Builtin types + TargetRefType Boolean = new TargetRefType("java.lang.Boolean"); + TargetRefType Char = new TargetRefType("java.lang.Character"); + TargetRefType Byte = new TargetRefType("java.lang.Byte"); + TargetRefType Short = new TargetRefType("java.lang.Short"); + TargetRefType Integer = new TargetRefType("java.lang.Integer"); + TargetRefType Long = new TargetRefType("java.lang.Long"); + TargetRefType Float = new TargetRefType("java.lang.Float"); + TargetRefType Double = new TargetRefType("java.lang.Double"); + TargetRefType String = new TargetRefType("java.lang.String"); + TargetRefType Object = new TargetRefType("java.lang.Object"); + + // Builtin types + TargetPrimitiveType boolean_ = new TargetPrimitiveType("Z"); + TargetPrimitiveType char_ = new TargetPrimitiveType("C"); + TargetPrimitiveType byte_ = new TargetPrimitiveType("B"); + TargetPrimitiveType short_ = new TargetPrimitiveType("S"); + TargetPrimitiveType int_ = new TargetPrimitiveType("I"); + TargetPrimitiveType long_ = new TargetPrimitiveType("J"); + TargetPrimitiveType float_ = new TargetPrimitiveType("F"); + TargetPrimitiveType double_ = new TargetPrimitiveType("D"); + + static TargetType toPrimitive(TargetType type) { + if (type.equals(Boolean)) return boolean_; + if (type.equals(Char)) return char_; + if (type.equals(Byte)) return byte_; + if (type.equals(Short)) return short_; + if (type.equals(Integer)) return int_; + if (type.equals(Long)) return long_; + if (type.equals(Float)) return float_; + if (type.equals(Double)) return double_; + return type; + } + + static TargetType toTargetType(Class clazz) { + if (clazz.isPrimitive()) { + if (clazz.equals(boolean.class)) return boolean_; + if (clazz.equals(char.class)) return char_; + if (clazz.equals(byte.class)) return byte_; + if (clazz.equals(short.class)) return short_; + if (clazz.equals(int.class)) return int_; + if (clazz.equals(long.class)) return long_; + if (clazz.equals(float.class)) return float_; + if (clazz.equals(double.class)) return double_; + } + if (clazz.equals(void.class)) return null; + return new TargetRefType(clazz.getName()); + } + + String toSignature(); + String toGenericSignature(); + String getInternalName(); + String name(); +} diff --git a/src/test/java/bytecode/OLFun2Test.java b/src/test/java/bytecode/OLFun2Test.java new file mode 100644 index 00000000..6ec8701b --- /dev/null +++ b/src/test/java/bytecode/OLFun2Test.java @@ -0,0 +1,69 @@ +package bytecode; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.function.Function; + +import general.TestCleanUp; +import org.junit.*; +import de.dhbwstuttgart.core.JavaTXCompiler; +import static org.junit.Assert.*; + +/** + * //ToDo Etienne: Beschreiben + * + * @since Studienarbeit Type Erasure + * @author etiennezink + */ +public class OLFun2Test { + private static String path; + private static File fileToTest; + private static JavaTXCompiler compiler; + private static ClassLoader loader; + private static Class classToTest; + private static Class classFun1IntInt; + private static Class classFun1DoubleDouble; + private static Class classFun1StringString; + + private static String generatedByteCodeDirectory = System.getProperty("user.dir") + "/src/test/resources/testBytecode/generatedBC/"; + + //ToDo Etienne: Nach Anpassung des Bytecode die Tests hinzufügen + //ToDo Etienne: Aufruf von m testen + @BeforeClass + public static void setUp() throws Exception { + path = System.getProperty("user.dir")+"/src/test/resources/bytecode/javFiles/OLFun2.jav"; + fileToTest = new File(path); + compiler = new JavaTXCompiler(fileToTest); + compiler.generateBytecode(generatedByteCodeDirectory); + loader = new URLClassLoader(new URL[] {new URL("file://"+generatedByteCodeDirectory)}); + classToTest = loader.loadClass("OLFun2"); + classFun1IntInt = loader.loadClass("Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$"); + classFun1DoubleDouble = loader.loadClass("Fun1$$Ljava$lang$Double$_$Ljava$lang$Double$_$"); + classFun1StringString = loader.loadClass("Fun1$$Ljava$lang$String$_$Ljava$lang$String$_$"); + } + + @Test + public void mExistsWithInteger() throws Exception{ + Method m = classToTest.getDeclaredMethod("m", classFun1IntInt); + assertNotNull(m); + } + + @Test + public void mExistsWithDouble() throws Exception{ + Method m = classToTest.getDeclaredMethod("m", classFun1DoubleDouble); + assertNotNull(m); + } + + @Test + public void mExistsWithString() throws Exception{ + Method m = classToTest.getDeclaredMethod("m", classFun1StringString); + assertNotNull(m); + } + + //@AfterClass + public static void cleanUp(){ + TestCleanUp.cleanUpDirectory(new File(generatedByteCodeDirectory), f -> f.getName().contains(".class")); + } +} diff --git a/src/test/java/bytecode/OLFunTest.java b/src/test/java/bytecode/OLFunTest.java index 14e0885e..d1910e07 100644 --- a/src/test/java/bytecode/OLFunTest.java +++ b/src/test/java/bytecode/OLFunTest.java @@ -1,52 +1,68 @@ package bytecode; -import static org.junit.Assert.assertEquals; - import java.io.File; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; -import org.junit.Test; - +import general.TestCleanUp; +import org.junit.*; import de.dhbwstuttgart.core.JavaTXCompiler; +import static org.junit.Assert.*; +/** + * //ToDo Etienne: Beschreiben + * + * @since Studienarbeit Type Erasure + * @author etiennezink + */ public class OLFunTest { private static String path; private static File fileToTest; private static JavaTXCompiler compiler; private static ClassLoader loader; private static Class classToTest; - private static String pathToClassFile; - private static Object instanceOfClass; - - @Test - public void generateBC() throws Exception { - path = System.getProperty("user.dir")+"/resources/bytecode/javFiles/OLFun.jav"; + private static Class classFun1IntInt; + private static Class classFun1DoubleDouble; + private static Class classFun1StringString; + + private static String generatedByteCodeDirectory = System.getProperty("user.dir") + "/src/test/resources/testBytecode/generatedBC/"; + + //ToDo Etienne: Nach Anpassung des Bytecode die Tests hinzufügen + //ToDo Etienne: Aufruf von m testen + @BeforeClass + public static void setUp() throws Exception { + path = System.getProperty("user.dir")+"/src/test/resources/bytecode/javFiles/OLFun.jav"; fileToTest = new File(path); compiler = new JavaTXCompiler(fileToTest); - compiler.generateBytecode(System.getProperty("user.dir")+"/resources/testBytecode/generatedBC/"); - pathToClassFile = System.getProperty("user.dir")+"/resources/testBytecode/generatedBC/"; - loader = new URLClassLoader(new URL[] {new URL("file://"+pathToClassFile)}); + compiler.generateBytecode(generatedByteCodeDirectory); + loader = new URLClassLoader(new URL[] {new URL("file://"+generatedByteCodeDirectory)}); classToTest = loader.loadClass("OLFun"); - /* - instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); - - Method m = classToTest.getDeclaredMethod("m"); - Class lambda = m.invoke(instanceOfClass).getClass(); - Method apply = lambda.getMethod("apply", Object.class); - - // Damit man auf die Methode zugreifen kann - apply.setAccessible(true); - - Integer i = 77; - - Integer result = (Integer) apply.invoke(m.invoke(instanceOfClass), i); - - assertEquals(77, result); - */ + classFun1IntInt = loader.loadClass("Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$"); + classFun1DoubleDouble = loader.loadClass("Fun1$$Ljava$lang$Double$_$Ljava$lang$Double$_$"); + classFun1StringString = loader.loadClass("Fun1$$Ljava$lang$String$_$Ljava$lang$String$_$"); } + @Test + public void mExistsWithInteger() throws Exception{ + Method m = classToTest.getDeclaredMethod("m", classFun1IntInt ,Integer.class); + assertNotNull(m); + } + @Test + public void mExistsWithDouble() throws Exception{ + Method m = classToTest.getDeclaredMethod("m", classFun1DoubleDouble ,Double.class); + assertNotNull(m); + } + + @Test + public void mExistsWithString() throws Exception{ + Method m = classToTest.getDeclaredMethod("m", classFun1StringString ,String.class); + assertNotNull(m); + } + + //@AfterClass + public static void cleanUp(){ + TestCleanUp.cleanUpDirectory(new File(generatedByteCodeDirectory), f -> f.getName().contains(".class")); + } } diff --git a/src/test/java/general/TestCleanUp.java b/src/test/java/general/TestCleanUp.java new file mode 100644 index 00000000..f1b82cc8 --- /dev/null +++ b/src/test/java/general/TestCleanUp.java @@ -0,0 +1,14 @@ +package general; + +import java.io.File; +import java.io.FileFilter; + +public class TestCleanUp { + + public static void cleanUpDirectory(File directory, FileFilter fileFilter){ + if(!directory.isDirectory()) throw new RuntimeException("Directory for bytecode generation is wrong!"); + for (File file: directory.listFiles(fileFilter)) { + file.delete(); + } + } +} diff --git a/src/test/java/targetast/ASTToTypedTargetAST.java b/src/test/java/targetast/ASTToTypedTargetAST.java new file mode 100644 index 00000000..94b56bd4 --- /dev/null +++ b/src/test/java/targetast/ASTToTypedTargetAST.java @@ -0,0 +1,88 @@ +package targetast; + +import de.dhbwstuttgart.core.JavaTXCompiler; +import de.dhbwstuttgart.parser.NullToken; +import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.syntaxtree.*; +import de.dhbwstuttgart.syntaxtree.type.RefType; +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import de.dhbwstuttgart.target.generate.ASTToTargetAST; +import de.dhbwstuttgart.target.tree.TargetClass; +import de.dhbwstuttgart.typeinference.result.ResultSet; +import org.junit.Test; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import static org.junit.Assert.*; + +public class ASTToTypedTargetAST { + + @Test + public void emptyClass(){ + ClassOrInterface emptyClass = new ClassOrInterface(0, new JavaClassName("EmptyClass"), new ArrayList<>(), java.util.Optional.empty(), new ArrayList<>(), new ArrayList<>(), new GenericDeclarationList(new ArrayList<>(), new NullToken()), + new RefType(new JavaClassName("Object"), new NullToken()), false, new ArrayList<>(), new NullToken()); + ResultSet emptyResultSet = new ResultSet(new HashSet<>()); + TargetClass emptyTargetClass = new ASTToTargetAST(List.of(emptyResultSet)).convert(emptyClass); + assert emptyTargetClass.getName().equals("EmptyClass"); + assert emptyTargetClass.methods().size() == 0; + assert emptyTargetClass.fields().size() == 0; + } + + @Test + public void overloading() throws Exception { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/Overloading.jav").toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var converter = new ASTToTargetAST(resultSet); + var classes = compiler.sourceFiles.get(file).getClasses(); + + var classLoader = new ByteArrayClassLoader(); + var overloading = TestCodegen.generateClass(converter.convert(classes.get(0)), classLoader); + var overloading2 = TestCodegen.generateClass(converter.convert(classes.get(1)), classLoader); + + var test1 = overloading.getDeclaredMethod("test", overloading); + test1.setAccessible(true); + var test2 = overloading.getDeclaredMethod("test", overloading2); + test2.setAccessible(true); + Object overloadingInstance = overloading.getDeclaredConstructor().newInstance(); + Object overloading2Instance = overloading2.getDeclaredConstructor().newInstance(); + assertEquals(test1.invoke(overloadingInstance, overloadingInstance), "Overloading"); + assertEquals(test2.invoke(overloadingInstance, overloading2Instance), "Overloading2"); + } + + @Test + public void tphsAndGenerics() throws Exception { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/Tph2.jav").toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var converter = new ASTToTargetAST(resultSet); + var classes = compiler.sourceFiles.get(file).getClasses(); + + var tphAndGenerics = TestCodegen.generateClass(converter.convert(classes.get(0)), new ByteArrayClassLoader()); + } + + @Test + public void cycles() throws Exception { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/Cycle.jav").toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var converter = new ASTToTargetAST(resultSet); + var classes = compiler.sourceFiles.get(file).getClasses(); + + var cycle = TestCodegen.generateClass(converter.convert(classes.get(0)), new ByteArrayClassLoader()); + } + + @Test + public void infimum() throws Exception { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/Infimum.jav").toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var converter = new ASTToTargetAST(resultSet); + var classes = compiler.sourceFiles.get(file).getClasses(); + + var infimum = TestCodegen.generateClass(converter.convert(classes.get(0)), new ByteArrayClassLoader()); + } +} diff --git a/src/test/java/targetast/Fun1$$.java b/src/test/java/targetast/Fun1$$.java new file mode 100644 index 00000000..882112f2 --- /dev/null +++ b/src/test/java/targetast/Fun1$$.java @@ -0,0 +1,3 @@ +public interface Fun1$$ { + public R apply(T t); +} diff --git a/src/test/java/targetast/GreaterEqualTest.java b/src/test/java/targetast/GreaterEqualTest.java new file mode 100644 index 00000000..2a4c6b0d --- /dev/null +++ b/src/test/java/targetast/GreaterEqualTest.java @@ -0,0 +1,135 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GreaterEqualTest { + static Class classToTest; + static Object instance; + + @BeforeClass + public static void beforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("GreaterEqual.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("GreaterEqual"); + instance = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testIntegers() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Integer.class, Integer.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 7, 5); + assertTrue(result); + } + + @Test + public void testIntegers2() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Integer.class, Integer.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 5, 7); + assertFalse(result); + } + + @Test + public void testEqIntegers() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Integer.class, Integer.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 5, 5); + assertTrue(result); + } + + @Test + public void testLongs() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Method gE = classToTest.getDeclaredMethod("gE", Long.class, Long.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 10L, 7L); + assertTrue(result); + } + + @Test + public void testFloats() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Float.class, Float.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 5F, 7F); + assertFalse(result); + } + + @Test + public void testDoubles() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Double.class, Double.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 5.0, 7.0); + assertFalse(result); + } + + @Test + public void testLongInt() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Long.class, Integer.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 15L, 7); + assertTrue(result); + } + + @Test + public void testFloatInt() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Float.class, Integer.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 5F, 7); + assertFalse(result); + } + + @Test + public void testDoubleInt() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Double.class, Integer.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 25.0, 17); + assertTrue(result); + } + + @Test + public void testFloatLong() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Float.class, Long.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 75F, 70L); + assertTrue(result); + } + + @Test + public void testDoubleLong() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Double.class, Long.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 5.0, 7L); + assertFalse(result); + } + + @Test + public void testEqDoubleFloat() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Double.class, Float.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 7.0, 7F); + assertTrue(result); + } + + @Test + public void testDoubleFloat() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Double.class, Float.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 15.0, 7F); + assertTrue(result); + } + + @Test + public void testDoubleFloat3() throws Exception { + Method gE = classToTest.getDeclaredMethod("gE", Double.class, Float.class); + gE.setAccessible(true); + Boolean result = (Boolean) gE.invoke(instance, 9.0, 17F); + assertFalse(result); + } +} diff --git a/src/test/java/targetast/GreaterThanTest.java b/src/test/java/targetast/GreaterThanTest.java new file mode 100644 index 00000000..3d7ce1cf --- /dev/null +++ b/src/test/java/targetast/GreaterThanTest.java @@ -0,0 +1,133 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GreaterThanTest { + static Class classToTest; + static Object instance; + + @BeforeClass + public static void beforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("GreaterThan.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("GreaterThan"); + instance = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testIntegers() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Integer.class, Integer.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 7, 5); + assertTrue(result); + } + + @Test + public void testIntegers2() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Integer.class, Integer.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 5, 7); + assertFalse(result); + } + + @Test + public void testEqIntegers() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Integer.class, Integer.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 5, 5); + assertFalse(result); + } + + @Test + public void testLongs() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Long.class, Long.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 10L,7L); + assertTrue(result); + }@Test + + public void testFloats() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Float.class, Float.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 5F,7F); + assertFalse(result); + } + + @Test + public void testDoubles() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Double.class, Double.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 5.0,7.0); + assertFalse(result); + } + + @Test + public void testLongInt() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Long.class, Integer.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 15L,7); + assertTrue(result); + } + + @Test + public void testFloatInt() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Float.class, Integer.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 5F,7); + assertFalse(result); + } + + @Test + public void testDoubleInt() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Double.class, Integer.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 25.0,17); + assertTrue(result); + } + + @Test + public void testFloatLong() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Float.class, Long.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 75F,70L); + assertTrue(result); + } + + @Test + public void testDoubleLong() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Double.class, Long.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 5.0,7L); + assertFalse(result); + } + + @Test + public void testEqDoubleFloat() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Double.class, Float.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 7.0,7F); + assertFalse(result); + } + + @Test + public void testDoubleFloat() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Double.class, Float.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 15.0,7F); + assertTrue(result); + } + + @Test + public void testDoubleFloat3() throws Exception { + Method gT = classToTest.getDeclaredMethod("gT", Double.class, Float.class); + gT.setAccessible(true); + Boolean result = (Boolean) gT.invoke(instance, 9.0,17F); + assertFalse(result); + } +} diff --git a/src/test/java/targetast/InheritTest.java b/src/test/java/targetast/InheritTest.java new file mode 100644 index 00000000..7c99cc66 --- /dev/null +++ b/src/test/java/targetast/InheritTest.java @@ -0,0 +1,103 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.util.Vector; + +import static org.junit.Assert.assertEquals; + +public class InheritTest { + private static Class classToTest, classToTestAA, classToTestBB, classToTestCC, classToTestDD; + private static Object instanceOfClass, instanceOfClassAA, instanceOfClassBB, instanceOfClassCC, instanceOfClassDD; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classLoader = new ByteArrayClassLoader(); + classToTest = TestCodegen.generateClassFiles("Inherit.jav", classLoader).get("Inherit"); + classToTestAA = TestCodegen.generateClassFiles("AA.jav", classLoader).get("AA"); + classToTestBB = TestCodegen.generateClassFiles("BB.jav", classLoader).get("BB"); + classToTestCC = TestCodegen.generateClassFiles("CC.jav", classLoader).get("CC"); + classToTestDD = TestCodegen.generateClassFiles("DD.jav", classLoader).get("DD"); + + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + instanceOfClassAA = classToTestAA.getDeclaredConstructor().newInstance(); + instanceOfClassBB = classToTestBB.getDeclaredConstructor().newInstance(); + instanceOfClassCC = classToTestCC.getDeclaredConstructor().newInstance(); + instanceOfClassDD = classToTestDD.getDeclaredConstructor().newInstance(); + } + + @Test + public void testmainAA() throws Exception { + Method m = classToTestAA.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassAA, 5), "AA"); + Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class); + assertEquals(main.invoke(instanceOfClass, instanceOfClassAA, 5), "AA"); + } + + @Test + public void testmainBB() throws Exception { + Method m = classToTestAA.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassBB, 5), "AA"); + Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class); + assertEquals(main.invoke(instanceOfClass, instanceOfClassBB, 5), "AA"); + } + + @Test + public void testmainCC() throws Exception { + Method m = classToTestCC.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassCC, 5), "CC"); + Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class); + assertEquals(main.invoke(instanceOfClass, instanceOfClassCC, 5), "CC"); + } + + @Test + public void testmainDD() throws Exception { + Method m = classToTestCC.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassDD, 5), "CC"); + Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class); + assertEquals(main.invoke(instanceOfClass, instanceOfClassDD, 5), "CC"); + } + + @Test + public void testmainVectorAA() throws Exception { + Method m = classToTestAA.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassAA, 5), "AA"); + Vector v = new Vector<>(); + v.add(instanceOfClassAA); + Method main = classToTest.getDeclaredMethod("main", Vector.class, Integer.class); + assertEquals(main.invoke(instanceOfClass, v, 5), "AA"); + } + + @Test + public void testmainVectorBB() throws Exception { + Method m = classToTestAA.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassBB, 5), "AA"); + Vector v = new Vector<>(); + v.add(instanceOfClassBB); + Method main = classToTest.getDeclaredMethod("main", Vector.class, Integer.class); + assertEquals(main.invoke(instanceOfClass, v, 5), "AA"); + } + + @Test + public void testmainVectorCC() throws Exception { + Method m = classToTestCC.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassCC, 5), "CC"); + Vector v = new Vector<>(); + v.add(instanceOfClassCC); + Method main = classToTest.getDeclaredMethod("main", Vector.class, Integer.class); + assertEquals(main.invoke(instanceOfClass, v, 5), "CC"); + } + + @Test + public void testmainVectorDD() throws Exception { + Method m = classToTestCC.getDeclaredMethod("m", Integer.class); + assertEquals(m.invoke(instanceOfClassDD, 5), "CC"); + Vector v = new Vector<>(); + v.add(instanceOfClassDD); + Method main = classToTest.getDeclaredMethod("main", Vector.class, Integer.class); + assertEquals(main.invoke(instanceOfClass, v, 5), "CC"); + } +} diff --git a/src/test/java/targetast/InheritTest2.java b/src/test/java/targetast/InheritTest2.java new file mode 100644 index 00000000..c4c48ed5 --- /dev/null +++ b/src/test/java/targetast/InheritTest2.java @@ -0,0 +1,119 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.util.Vector; + +import static org.junit.Assert.assertEquals; + +public class InheritTest2 { + private static Class classToTest, classToTestAA, classToTestBB, classToTestCC, classToTestDD; + private static Object instanceOfClass, instanceOfClassAA, instanceOfClassBB, instanceOfClassCC, instanceOfClassDD; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classLoader = new ByteArrayClassLoader(); + classToTest = TestCodegen.generateClassFiles("Inherit2.jav", classLoader).get("Inherit2"); + classToTestAA = TestCodegen.generateClassFiles("AA.jav", classLoader).get("AA"); + classToTestBB = TestCodegen.generateClassFiles("BB.jav", classLoader).get("BB"); + classToTestCC = TestCodegen.generateClassFiles("CC.jav", classLoader).get("CC"); + classToTestDD = TestCodegen.generateClassFiles("DD.jav", classLoader).get("DD"); + + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + instanceOfClassAA = classToTestAA.getDeclaredConstructor().newInstance(); + instanceOfClassBB = classToTestBB.getDeclaredConstructor().newInstance(); + instanceOfClassCC = classToTestCC.getDeclaredConstructor().newInstance(); + instanceOfClassDD = classToTestDD.getDeclaredConstructor().newInstance(); + } + + @Test + public void testmainAA() throws Exception { + Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA); + assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA"); + Method main = classToTest.getDeclaredMethod("main", classToTestAA); + assertEquals(main.invoke(instanceOfClass, instanceOfClassAA), "AA"); + } + + @Test + public void testmainBB() throws Exception { + Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA); + assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA"); + Method main = classToTest.getDeclaredMethod("main", classToTestAA); + assertEquals(main.invoke(instanceOfClass, instanceOfClassBB), "AA"); + } + + @Test + public void testmainCC() throws Exception { + Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC); + assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC"); + Method main = classToTest.getDeclaredMethod("main", classToTestCC); + assertEquals(main.invoke(instanceOfClass, instanceOfClassCC), "CC"); + } + + @Test + public void testmainDD() throws Exception { + Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC); + assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC"); + Method main = classToTest.getDeclaredMethod("main", classToTestCC); + assertEquals(main.invoke(instanceOfClass, instanceOfClassDD), "CC"); + } + + + //PL 2020-05-12: Die folgenden Test funktionieren erst, wenn Generics im Bytecode implementiert sind + @Test + public void testmainVectorAA() throws Exception { + Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA); + assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA"); + Vector v = new Vector<>(); + v.add(instanceOfClassAA); + Method main = classToTest.getDeclaredMethod("main", Vector.class); + try { + assertEquals(main.invoke(instanceOfClass, v), "AA"); + } + catch (java.lang.reflect.InvocationTargetException e) { + testmainVectorCC(); + } + } + + @Test + public void testmainVectorBB() throws Exception { + Method m2 = classToTestAA.getDeclaredMethod("m2", classToTestAA); + assertEquals(m2.invoke(instanceOfClassAA, instanceOfClassAA), "AA"); + Vector v = new Vector<>(); + v.add(instanceOfClassBB); + Method main = classToTest.getDeclaredMethod("main", Vector.class); + try { + assertEquals(main.invoke(instanceOfClass, v), "AA"); + } + catch (java.lang.reflect.InvocationTargetException e) { + testmainVectorCC(); + } + } + + @Test + public void testmainVectorCC() throws Exception { + Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC); + assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC"); + Vector v = new Vector<>(); + v.add(instanceOfClassCC); + Method main = classToTest.getDeclaredMethod("main", Vector.class); + String erg; + assertEquals(erg= (String) main.invoke(instanceOfClass, v), + erg.equals("CC")? "CC": "AA"); + } + + @Test + public void testmainVectorDD() throws Exception { + Method m2 = classToTestCC.getDeclaredMethod("m2", classToTestCC); + assertEquals(m2.invoke(instanceOfClassCC, instanceOfClassCC), "CC"); + Vector v = new Vector<>(); + v.add(instanceOfClassDD); + Method main = classToTest.getDeclaredMethod("main", Vector.class); + String erg; + assertEquals(erg= (String) main.invoke(instanceOfClass, v), + erg.equals("CC")? "CC": "AA"); + } +} diff --git a/src/test/java/targetast/LessEqualTest.java b/src/test/java/targetast/LessEqualTest.java new file mode 100644 index 00000000..617a3f0b --- /dev/null +++ b/src/test/java/targetast/LessEqualTest.java @@ -0,0 +1,112 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class LessEqualTest { + static Class classToTest; + static Object instance; + + @BeforeClass + public static void beforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("LessEqual.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("LessEqual"); + instance = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testIntegers() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Integer.class, Integer.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5,7); + assertTrue(result); + } + + @Test + public void testEqualIntegers() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Integer.class, Integer.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5,5); + assertTrue(result); + } + + @Test + public void testLongs() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Long.class, Long.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5L,7L); + assertTrue(result); + }@Test + + public void testFloats() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Float.class, Float.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5F,7F); + assertTrue(result); + } + + @Test + public void testDoubles() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Double.class, Double.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5.0,7.0); + assertTrue(result); + } + + @Test + public void testLongInt() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Long.class, Integer.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5L,7); + assertTrue(result); + } + + @Test + public void testFloatInt() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Float.class, Integer.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5F,7); + assertTrue(result); + } + + @Test + public void testDoubleInt() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Double.class, Integer.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5.0,7); + assertTrue(result); + } + + @Test + public void testFloatLong() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Float.class, Long.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5F,7L); + assertTrue(result); + } + + @Test + public void testDoubleLong() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Double.class, Long.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5.0,7L); + assertTrue(result); + } + + @Test + public void testEqDoubleFloat() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Double.class, Float.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 7.0,7F); + assertTrue(result); + } + + @Test + public void testDoubleFloat() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Double.class, Float.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 5.0,7F); + assertTrue(result); + } + + @Test + public void testDoubleFloat3() throws Exception { + Method lessEqual = classToTest.getDeclaredMethod("lessEqual", Double.class, Float.class); + Boolean result = (Boolean) lessEqual.invoke(instance, 9.0,7F); + assertFalse(result); + } +} diff --git a/src/test/java/targetast/LessThanTest.java b/src/test/java/targetast/LessThanTest.java new file mode 100644 index 00000000..3ed46908 --- /dev/null +++ b/src/test/java/targetast/LessThanTest.java @@ -0,0 +1,120 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class LessThanTest { + static Class classToTest; + static Object instance; + + @BeforeClass + public static void beforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("LessThan.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("LessThan"); + instance = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testLessThanInt() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Integer.class,Integer.class); + Boolean result = (Boolean) lessThan.invoke(instance, 5, 7); + assertTrue(result); + } + + @Test + public void testLessThanInt2() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Integer.class, Integer.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7, 5); + assertFalse(result); + } + + @Test + public void testLessThanInt3() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Integer.class, Integer.class); + Boolean result = (Boolean) lessThan.invoke(instance, 5, 5); + assertFalse(result); + } + + @Test + public void testLessThanLong() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Long.class,Long.class); + Boolean result = (Boolean) lessThan.invoke(instance, 5L, 7L); + assertTrue(result); + } + + @Test + public void testLessThanLong2() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Long.class, Long.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7L, 5L); + assertFalse(result); + } + + @Test + public void testLessThanLong3() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Long.class, Long.class); + Boolean result = (Boolean) lessThan.invoke(instance, 5L, 5L); + assertFalse(result); + } + + @Test + public void testLessThanFloat() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Float.class, Float.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7F, 5F); + assertFalse(result); + } + + @Test + public void testLessThanDouble() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Double.class, Double.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7.0, 5.0); + assertFalse(result); + } + + @Test + public void testLessThanLongInt() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Long.class, Integer.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7L, 5); + assertFalse(result); + } + + @Test + public void testLessThanFloatInt() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Float.class, Integer.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7F, 5); + assertFalse(result); + } + + @Test + public void testLessThanDoubleInt() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Double.class, Integer.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7.0, 5); + assertFalse(result); + } + + @Test + public void testLessThanFloatLong() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Float.class, Long.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7F, 5L); + assertFalse(result); + } + + @Test + public void testLessThanDoubleLong() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Double.class, Long.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7.0, 5L); + assertFalse(result); + } + + @Test + public void testLessThanDoubleFloat() throws Exception { + Method lessThan = classToTest.getDeclaredMethod("lessThan", Double.class, Float.class); + Boolean result = (Boolean) lessThan.invoke(instance, 7.0, 5F); + assertFalse(result); + } +} diff --git a/src/test/java/targetast/OLTest.java b/src/test/java/targetast/OLTest.java new file mode 100644 index 00000000..30957a09 --- /dev/null +++ b/src/test/java/targetast/OLTest.java @@ -0,0 +1,72 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; + +public class OLTest { + private static Class classToTest; + private static Class classToTest1; + private static Object instanceOfClass; + private static Object instanceOfClass1; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("OL.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("OL"); + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + classToTest1 = classFiles.get("OLMain"); + instanceOfClass1 = classToTest1.getDeclaredConstructor().newInstance(); + } + + @Test + public void testmInt() throws Exception { + Method m = classToTest.getDeclaredMethod("m", Integer.class); + Integer result = (Integer) m.invoke(instanceOfClass, 5); + assertEquals(new Integer(10), result); + } + + @Test + public void testmDouble() throws Exception { + Method m = classToTest.getDeclaredMethod("m", Double.class); + Double result = (Double) m.invoke(instanceOfClass, 5.0); + assertEquals(new Double(10.0), result); + } + + @Test + public void testmString() throws Exception { + Method m = classToTest.getDeclaredMethod("m", String.class); + String result = (String) m.invoke(instanceOfClass, "xxx"); + assertEquals("xxxxxx", result); + } + + @Test + public void testOLMainClassName() { + assertEquals("OLMain", classToTest1.getName()); + } + + @Test + public void testmainInt() throws Exception { + Method main = classToTest1.getDeclaredMethod("main", Integer.class); + Integer result = (Integer) main.invoke(instanceOfClass1, 5); + assertEquals(Integer.valueOf(10), result); + } + + @Test + public void testmainDouble() throws Exception { + Method main = classToTest1.getDeclaredMethod("main", Double.class); + Double result = (Double) main.invoke(instanceOfClass1, 5.0); + assertEquals(Double.valueOf(10.0), result); + } + + @Test + public void testmainString() throws Exception { + Method main = classToTest1.getDeclaredMethod("main", String.class); + String result = (String) main.invoke(instanceOfClass1, "xxx"); + assertEquals("xxxxxx", result); + } +} diff --git a/src/test/java/targetast/PostIncTest.java b/src/test/java/targetast/PostIncTest.java new file mode 100644 index 00000000..9fbbb965 --- /dev/null +++ b/src/test/java/targetast/PostIncTest.java @@ -0,0 +1,50 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; + +public class PostIncTest { + private static Class classToTest; + private static Object instanceOfClass; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("PostIncDec.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("PostIncDec"); + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testM1() throws Exception { + Method m = classToTest.getDeclaredMethod("m"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(1), res); + } + + @Test + public void testM2() throws Exception { + Method m = classToTest.getDeclaredMethod("m2"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(0), res); + } + + @Test + public void testD1() throws Exception { + Method m = classToTest.getDeclaredMethod("d"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(-1), res); + } + + @Test + public void testD2() throws Exception { + Method m = classToTest.getDeclaredMethod("d2"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(0), res); + } + +} diff --git a/src/test/java/targetast/PreIncTest.java b/src/test/java/targetast/PreIncTest.java new file mode 100644 index 00000000..a48cea2c --- /dev/null +++ b/src/test/java/targetast/PreIncTest.java @@ -0,0 +1,50 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; + +public class PreIncTest { + private static Class classToTest; + private static Object instanceOfClass; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("PreInc.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("PreInc"); + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testM() throws Exception { + Method m = classToTest.getDeclaredMethod("m"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(1), res); + } + + @Test + public void testM2() throws Exception { + Method m = classToTest.getDeclaredMethod("m2"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(1), res); + } + + @Test + public void testD() throws Exception { + Method m = classToTest.getDeclaredMethod("d"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(-1), res); + } + + @Test + public void testD2() throws Exception { + Method m = classToTest.getDeclaredMethod("d2"); + Integer res = (Integer) m.invoke(instanceOfClass); + assertEquals(Integer.valueOf(-1), res); + } + +} diff --git a/src/test/java/targetast/PutTest.java b/src/test/java/targetast/PutTest.java new file mode 100644 index 00000000..a67ad03a --- /dev/null +++ b/src/test/java/targetast/PutTest.java @@ -0,0 +1,59 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.util.Stack; +import java.util.Vector; + +import static org.junit.Assert.assertEquals; + +public class PutTest { + private static Class classToTest; + private static Object instanceOfClass; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("Put.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("Put"); + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void testPutElementVector() throws Exception { + Method m = classToTest.getDeclaredMethod("putElement", Object.class, Vector.class); + Vector v_invoke = new Vector<>(); + m.invoke(instanceOfClass, 5, v_invoke); + Vector v = new Vector<>(); + v.add(5); + assertEquals(v, v_invoke); + } + + @Test + public void testPutElementStack() throws Exception { + Method m = classToTest.getDeclaredMethod("putElement", Object.class, Stack.class); + Stack s_invoke = new Stack<>(); + m.invoke(instanceOfClass, 5, s_invoke); + assertEquals(new Integer(5), s_invoke.pop()); + } + + @Test + public void testMainVector() throws Exception { + Method m = classToTest.getDeclaredMethod("main", Object.class, Vector.class); + Vector v_invoke = new Vector<>(); + m.invoke(instanceOfClass, 6, v_invoke); + Vector v = new Vector<>(); + v.add(6); + assertEquals(v, v_invoke); + } + + @Test + public void testMainStack() throws Exception { + Method m = classToTest.getDeclaredMethod("main", Object.class, Stack.class); + Stack s_invoke = new Stack<>(); + m.invoke(instanceOfClass, 6, s_invoke); + assertEquals(new Integer(6), s_invoke.pop()); + } +} diff --git a/src/test/java/targetast/TestCodegen.java b/src/test/java/targetast/TestCodegen.java new file mode 100644 index 00000000..46c4c834 --- /dev/null +++ b/src/test/java/targetast/TestCodegen.java @@ -0,0 +1,351 @@ +package targetast; + +import de.dhbwstuttgart.core.JavaTXCompiler; +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import de.dhbwstuttgart.target.bytecode.Codegen; +import de.dhbwstuttgart.target.generate.ASTToTargetAST; +import de.dhbwstuttgart.target.tree.MethodParameter; +import de.dhbwstuttgart.target.tree.TargetClass; +import de.dhbwstuttgart.target.tree.expression.*; +import de.dhbwstuttgart.target.tree.type.TargetFunNType; +import de.dhbwstuttgart.target.tree.type.TargetRefType; +import de.dhbwstuttgart.target.tree.type.TargetType; +import org.junit.Test; +import static org.junit.Assert.*; +import org.objectweb.asm.Opcodes; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + + +public class TestCodegen { + + public static Class generateClass(TargetClass clazz, ByteArrayClassLoader classLoader) throws IOException { + var codegen = new Codegen(clazz); + var bytes = codegen.generate(); + var path = Path.of(System.getProperty("user.dir"), "src/test/resources/target/"); + Files.createDirectories(path); + Files.write(path.resolve(clazz.qualifiedName() + ".class"), bytes, StandardOpenOption.CREATE); + return classLoader.loadClass(bytes); + } + + public static Map> generateClassFiles(String filename, ByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/", filename).toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var sourceFile = compiler.sourceFiles.get(file); + var converter = new ASTToTargetAST(resultSet, sourceFile, classLoader); + var classes = compiler.sourceFiles.get(file).getClasses(); + + return classes.stream().map(cli -> { + try { + return generateClass(converter.convert(cli), classLoader); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + }).collect(Collectors.toMap(Class::getName, Function.identity())); + } + + @Test + public void testEmptyClass() throws Exception { + var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Empty"); + clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), null, new TargetBlock(List.of())); + generateClass(clazz, new ByteArrayClassLoader()).getDeclaredMethod("main").invoke(null); + } + + @Test + public void testArithmetic() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Arithmetic"); + + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", + List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "sub", + List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Sub(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "div", + List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Div(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "mul", + List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Mul(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "rem", + List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Rem(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b"))) + )) + ); + + + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + assertEquals(clazz.getDeclaredMethod("add", Integer.class, Integer.class).invoke(null, 10, 10), 20); + assertEquals(clazz.getDeclaredMethod("sub", Integer.class, Integer.class).invoke(null, 20, 10), 10); + assertEquals(clazz.getDeclaredMethod("div", Integer.class, Integer.class).invoke(null, 20, 10), 2); + assertEquals(clazz.getDeclaredMethod("mul", Integer.class, Integer.class).invoke(null, 20, 10), 200); + assertEquals(clazz.getDeclaredMethod("rem", Integer.class, Integer.class).invoke(null, 10, 3), 1); + } + + @Test + public void testUnary() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Unary"); + + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "not", + List.of(new MethodParameter(TargetType.Integer, "a")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetUnaryOp.Not(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "neg", + List.of(new MethodParameter(TargetType.Integer, "a")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetUnaryOp.Negate(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", + List.of(new MethodParameter(TargetType.Integer, "a")), + TargetType.Integer, + new TargetBlock(List.of(new TargetReturn( + new TargetUnaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"))) + )) + ); + + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + assertEquals(clazz.getDeclaredMethod("not", Integer.class).invoke(null, 10), -11); + assertEquals(clazz.getDeclaredMethod("neg", Integer.class).invoke(null, 10), -10); + assertEquals(clazz.getDeclaredMethod("add", Integer.class).invoke(null, 10), 10); + + } + + @Test + public void testConditional() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Conditional"); + + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "and", + List.of(new MethodParameter(TargetType.Boolean, "a"), new MethodParameter(TargetType.Boolean, "b")), + TargetType.Boolean, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.And(TargetType.Boolean, new TargetLocalVar(TargetType.Boolean, "a"), new TargetLocalVar(TargetType.Boolean, "b"))) + )) + ); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "or", + List.of(new MethodParameter(TargetType.Boolean, "a"), new MethodParameter(TargetType.Boolean, "b")), + TargetType.Boolean, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Or(TargetType.Boolean, new TargetLocalVar(TargetType.Boolean, "a"), new TargetLocalVar(TargetType.Boolean, "b"))) + )) + ); + + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + var and = clazz.getDeclaredMethod("and", Boolean.class, Boolean.class); + var or = clazz.getDeclaredMethod("or", Boolean.class, Boolean.class); + assertEquals(and.invoke(null, true, false), false); + assertEquals(and.invoke(null, true, true), true); + assertEquals(or.invoke(null, false, false), false); + assertEquals(or.invoke(null, true, false), true); + } + + // When adding two numbers and the return type is Long it needs to convert both values to Long + @Test + public void testArithmeticConvert() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "ArithmeticConvert"); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", List.of(), TargetType.Long, + new TargetBlock(List.of(new TargetReturn( + new TargetBinaryOp.Add(TargetType.Long, new TargetLiteral.CharLiteral((char)10), new TargetLiteral.LongLiteral((long)20)) + ))) + ); + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + assertEquals(clazz.getDeclaredMethod("add").invoke(null), (long)30); + } + + @Test + public void testMethodCall() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "HelloWorld"); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null, + new TargetBlock(List.of(new TargetMethodCall(null, + new TargetFieldVar( + new TargetRefType("java.io.PrintStream"), + new TargetRefType("java.lang.System"), + true, + new TargetClassName(new TargetRefType("java.lang.System")), + "out" + ), + List.of(new TargetLiteral.StringLiteral("Hello World!")), + new TargetRefType("java.io.PrintStream"), + "println", + false, false + ))) + ); + + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + clazz.getDeclaredMethod("helloWorld").invoke(null); + } + + @Test + public void testIfStatement() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "IfStmt"); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "ifStmt", + List.of(new MethodParameter(TargetType.Integer, "val")), + TargetType.Integer, + new TargetBlock(List.of(new TargetIf( + new TargetBinaryOp.Equal(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(10)), + new TargetReturn(new TargetLiteral.IntLiteral(1)), + new TargetIf( + new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(5)), + new TargetReturn(new TargetLiteral.IntLiteral(2)), + new TargetReturn(new TargetLiteral.IntLiteral(3)) + ) + ))) + ); + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + var ifStmt = clazz.getDeclaredMethod("ifStmt", Integer.class); + assertEquals(ifStmt.invoke(null, 10), 1); + assertEquals(ifStmt.invoke(null, 3), 2); + assertEquals(ifStmt.invoke(null, 20), 3); + } + + @Test + public void testFor() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "For"); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer, + new TargetBlock(List.of( + new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)), + new TargetFor( + new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)), + new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)), + new TargetAssign(TargetType.Integer, + new TargetLocalVar(TargetType.Integer, "i"), + new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1))), + new TargetBlock(List.of( + new TargetAssign(TargetType.Integer, + new TargetLocalVar(TargetType.Integer, "sum"), + new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetLocalVar(TargetType.Integer, "i")) + ) + )) + ), + new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum")) + )) + ); + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + assertEquals(clazz.getDeclaredMethod("forLoop").invoke(null), 45); + } + + @Test + public void testWhile() throws Exception { + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "While"); + targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "whileLoop", List.of(), TargetType.Integer, + new TargetBlock(List.of( + new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)), + new TargetWhile( + new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)), + new TargetBlock(List.of( + new TargetAssign(TargetType.Integer, + new TargetLocalVar(TargetType.Integer, "i"), + new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1)) + ) + )) + ), + new TargetReturn(new TargetLocalVar(TargetType.Integer, "i")) + )) + ); + var clazz = generateClass(targetClass, new ByteArrayClassLoader()); + assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10); + } + + @Test + public void testNew() throws Exception { + var pointType = new TargetRefType("Point"); + var pointTarget = new TargetClass(Opcodes.ACC_PUBLIC, "Point"); + pointTarget.addField(Opcodes.ACC_PUBLIC, TargetType.Integer, "x"); + pointTarget.addField(Opcodes.ACC_PUBLIC, TargetType.Integer, "y"); + pointTarget.addConstructor(Opcodes.ACC_PUBLIC, + List.of(new MethodParameter(TargetType.Integer, "x"), new MethodParameter(TargetType.Integer, "y")), + new TargetBlock(List.of( + new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "", false, false), + new TargetAssign(TargetType.Integer, + new TargetFieldVar(TargetType.Integer, pointType, false, new TargetThis(pointType), "x"), + new TargetLocalVar(TargetType.Integer, "x") + ), + new TargetAssign(TargetType.Integer, + new TargetFieldVar(TargetType.Integer, pointType, false, new TargetThis(pointType), "y"), + new TargetLocalVar(TargetType.Integer, "y") + ) + )) + ); + + var mainTarget = new TargetClass(Opcodes.ACC_PUBLIC, "New"); + mainTarget.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "makePoint", + List.of(new MethodParameter(TargetType.Integer, "x"), new MethodParameter(TargetType.Integer, "y")), pointType, + new TargetBlock(List.of( + new TargetReturn(new TargetNew(pointType, List.of( + new TargetLocalVar(TargetType.Integer, "x"), + new TargetLocalVar(TargetType.Integer, "y") + ))) + )) + ); + + var classLoader = new ByteArrayClassLoader(); + var pointClass = generateClass(pointTarget, classLoader); + var mainClass = generateClass(mainTarget, classLoader); + + var point = mainClass.getDeclaredMethod("makePoint", Integer.class, Integer.class).invoke(null, 10, 20); + assertEquals(point.getClass().getDeclaredField("x").get(point), 10); + assertEquals(point.getClass().getDeclaredField("y").get(point), 20); + } + + @Test + public void testLambda() throws Exception { + var classLoader = new ByteArrayClassLoader(); + var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class")); + var interfaceType = new TargetFunNType(1, List.of()); + + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "CGLambda"); + targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of( + new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "", false, false) + ))); + targetClass.addMethod(Opcodes.ACC_PUBLIC, "lambda", List.of(), TargetType.Integer, + new TargetBlock(List.of( + new TargetVarDecl(interfaceType, "by2", + new TargetLambdaExpression(interfaceType, List.of(), List.of(new MethodParameter(TargetType.Integer, "num")), TargetType.Integer, + new TargetBlock(List.of( + new TargetReturn(new TargetBinaryOp.Mul(TargetType.Integer, + new TargetLocalVar(TargetType.Integer, "num"), + new TargetLiteral.IntLiteral(2) + )) + ) + )) + ), + new TargetReturn(new TargetCast(TargetType.Integer, new TargetMethodCall(TargetType.Object, TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of( + new TargetLiteral.IntLiteral(10) + ), interfaceType, "apply", false, true))) + )) + ); + var clazz = generateClass(targetClass, classLoader); + var instance = clazz.getConstructor().newInstance(); + assertEquals(clazz.getDeclaredMethod("lambda").invoke(instance), 20); + } +} diff --git a/src/test/java/targetast/TestComplete.java b/src/test/java/targetast/TestComplete.java new file mode 100644 index 00000000..cf8cdac8 --- /dev/null +++ b/src/test/java/targetast/TestComplete.java @@ -0,0 +1,468 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.Ignore; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; +import java.util.Vector; + +import static org.junit.Assert.*; +import static targetast.TestCodegen.generateClassFiles; + +public class TestComplete { + + @Test + public void applyLambdaTest() throws Exception { + var classFiles = generateClassFiles("applyLambda.jav", new ByteArrayClassLoader()); + var applyLambda = classFiles.get("applyLambda"); + var instance = applyLambda.getDeclaredConstructor().newInstance(); + var m = applyLambda.getDeclaredMethod("m"); + var result = m.invoke(instance); + + assertEquals(result.getClass(), classFiles.get("Apply")); + } + + @Test + public void binaryTest() throws Exception { + var classFiles = generateClassFiles("BinaryInMeth.jav", new ByteArrayClassLoader()); + var binaryInMeth = classFiles.get("BinaryInMeth"); + var instance = binaryInMeth.getDeclaredConstructor().newInstance(); + + var m2 = binaryInMeth.getDeclaredMethod("m2", Integer.class, Integer.class); + var m3 = binaryInMeth.getDeclaredMethod("m3", Integer.class); + + assertEquals(6, m2.invoke(instance, 2, 3)); + assertEquals(4, m3.invoke(instance, 2)); + } + + @Test + public void classGenLamTest() throws Exception { + var classFiles = generateClassFiles("ClassGenLam.jav", new ByteArrayClassLoader()); + classFiles.get("ClassGenLam").getDeclaredConstructor().newInstance(); + } + + @Test + public void facTest() throws Exception { + var classFiles = generateClassFiles("Fac.jav", new ByteArrayClassLoader()); + var fac = classFiles.get("Fac"); + var instance = fac.getDeclaredConstructor().newInstance(); + + var getFac = fac.getDeclaredMethod("getFac", Integer.class); + assertEquals(6, getFac.invoke(instance, 3)); + } + + @Test + public void facultyTest() throws Exception { + var classFiles = generateClassFiles("Faculty.jav", new ByteArrayClassLoader()); + + var fac = classFiles.get("Faculty"); + var constructor = fac.getDeclaredConstructor(); + constructor.setAccessible(true); + var instance = constructor.newInstance(); + + var getFact = fac.getDeclaredMethod("getFact", Integer.class); + assertEquals(6, getFact.invoke(instance, 3)); + } + + @Test + public void fieldTest() throws Exception { + var classFiles = generateClassFiles("Field.jav", new ByteArrayClassLoader()); + var field = classFiles.get("Field"); + var instance = field.getDeclaredConstructor().newInstance(); + assertEquals(1, field.getFields().length); + } + + @Test + public void fieldTph2Test() throws Exception { + var classFiles = generateClassFiles("FieldTph2.jav", new ByteArrayClassLoader()); + var fieldtph2 = classFiles.get("FieldTph2"); + var instance = fieldtph2.getDeclaredConstructor().newInstance(); + + var a = fieldtph2.getDeclaredField("a"); + var m2 = fieldtph2.getDeclaredMethod("m2", Object.class); + m2.invoke(instance, 1); + + var m = fieldtph2.getDeclaredMethod("m", Object.class); + assertEquals(1, m.invoke(instance, 1)); + } + + @Test + public void fieldTphConsMethTest() throws Exception { + var classFiles = generateClassFiles("FieldTphConsMeth.jav", new ByteArrayClassLoader()); + var fieldTphConsMeth = classFiles.get("FieldTphConsMeth"); + + var ctor = fieldTphConsMeth.getDeclaredConstructor(Object.class); + var instance = ctor.newInstance("C"); + var a = fieldTphConsMeth.getDeclaredField("a"); + var id = fieldTphConsMeth.getDeclaredMethod("id", Object.class); + + assertEquals(42, id.invoke(instance, 42)); + assertEquals("C", a.get(instance)); + } + + @Test + public void fieldTphMMethTest() throws Exception { + var classFiles = generateClassFiles("FieldTphMMeth.jav", new ByteArrayClassLoader()); + var fieldTphMMeth = classFiles.get("FieldTphMMeth"); + var ctor = fieldTphMMeth.getDeclaredConstructor(Object.class, Object.class, Boolean.class); + + var instance1 = ctor.newInstance("C", 42, true); + var instance2 = ctor.newInstance("C", 42, false); + + var m = fieldTphMMeth.getDeclaredMethod("m", Object.class, Object.class, Boolean.class); + assertEquals(42, m.invoke(instance1, "C", 42, false)); + + var a = fieldTphMMeth.getDeclaredField("a"); + assertEquals("C", a.get(instance1)); + assertEquals(42, a.get(instance2)); + } + + @Test + public void genTest() throws Exception { + var classFiles = generateClassFiles("Gen.jav", new ByteArrayClassLoader()); + } + + @Test + public void idTest() throws Exception { + var classFiles = generateClassFiles("Id.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Id").getDeclaredConstructor().newInstance(); + } + + @Test + public void infTest() throws Exception { + var classFiles = generateClassFiles("Inf.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Inf").getDeclaredConstructor().newInstance(); + } + + @Test + public void kompTphTest() throws Exception { + var classFiles = generateClassFiles("KompTph.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("KompTph").getDeclaredConstructor().newInstance(); + } + + @Test + public void lambdaCaptureTest() throws Exception { + var classFiles = generateClassFiles("LambdaCapture.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("LambdaCapture").getDeclaredConstructor().newInstance(); + } + + @Test + public void lambdaTest() throws Exception { + var classFiles = generateClassFiles("Lambda.jav", new ByteArrayClassLoader()); + var classToTest = classFiles.get("Lambda"); + var instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + + var m = classToTest.getDeclaredMethod("m"); + var lambda = m.invoke(instanceOfClass).getClass(); + var apply = lambda.getMethod("apply", Object.class); + apply.setAccessible(true); + + var i = Integer.valueOf(77); + assertEquals(i, apply.invoke(m.invoke(instanceOfClass), i)); + } + + @Test + public void mathStrucInteger() throws Exception { + var classFiles = generateClassFiles("mathStrucInteger.jav", new ByteArrayClassLoader()); + var mathStrucInteger = classFiles.get("mathStrucInteger"); + mathStrucInteger.getDeclaredConstructor(Integer.class).newInstance(10); + } + + @Test + public void mathStruc() throws Exception { + var classFiles = generateClassFiles("mathStruc.jav", new ByteArrayClassLoader()); + var mathStruc = classFiles.get("mathStruc"); + mathStruc.getDeclaredConstructor(Object.class).newInstance("A"); + } + + @Test + public void matrixOpTest() throws Exception { + var classFiles = generateClassFiles("MatrixOP.jav", new ByteArrayClassLoader()); + var matrixOP = classFiles.get("MatrixOP"); + + Vector> vv = new Vector<>(); + Vector v1 = new Vector<>(); + v1.addElement(2); + v1.addElement(2); + Vector v2 = new Vector<>(); + v2.addElement(3); + v2.addElement(3); + vv.addElement(v1); + vv.addElement(v2); + + var instanceOfClass_m1 = matrixOP.getDeclaredConstructor(Vector.class).newInstance(vv); + + Vector> vv1 = new Vector<>(); + Vector v3 = new Vector<>(); + v3.addElement(2); + v3.addElement(2); + Vector v4 = new Vector<>(); + v4.addElement(3); + v4.addElement(3); + vv1.addElement(v3); + vv1.addElement(v4); + + var instanceOfClass_m2 = matrixOP.getDeclaredConstructor(Vector.class).newInstance(vv1);//Matrix m2 = new Matrix(vv1); + + var mul = matrixOP.getField("mul"); + mul.setAccessible(true); + + var lambda = mul.get(instanceOfClass_m1).getClass(); + var apply = lambda.getMethod("apply", Object.class, Object.class); + apply.setAccessible(true); + + var result = apply.invoke(mul.get(instanceOfClass_m1), instanceOfClass_m1, instanceOfClass_m2); + System.out.println(instanceOfClass_m1.toString() + " * " + instanceOfClass_m2.toString() + " = " + result.toString()); + + Vector> res = new Vector<>(); + Vector v5 = new Vector<>(); + v5.addElement(10); + v5.addElement(10); + Vector v6 = new Vector<>(); + v6.addElement(15); + v6.addElement(15); + res.addElement(v5); + res.addElement(v6); + + var instanceOfClass_m3 = matrixOP.getDeclaredConstructor(Vector.class).newInstance(res); + assertEquals(result, instanceOfClass_m3); + } + + @Ignore("Thread blocking for some reason") + @Test + public void matrixTest() throws Exception { + var classFiles = generateClassFiles("Matrix.jav", new ByteArrayClassLoader()); + var matrix = classFiles.get("Matrix"); + + Vector> vv = new Vector<>(); + Vector v1 = new Vector<> (); + v1.addElement(2); + v1.addElement(2); + Vector v2 = new Vector<> (); + v2.addElement(3); + v2.addElement(3); + vv.addElement(v1); + vv.addElement(v2); + + var instanceOfClass_m1 = matrix.getDeclaredConstructor(Vector.class).newInstance(vv); + + Vector> vv1 = new Vector<>(); + Vector v3 = new Vector<> (); + v3.addElement(2); + v3.addElement(2); + Vector v4 = new Vector<> (); + v4.addElement(3); + v4.addElement(3); + vv1.addElement(v3); + vv1.addElement(v4); + + var instanceOfClass_m2 = matrix.getDeclaredConstructor(Vector.class).newInstance(vv1); + + var mul = matrix.getDeclaredMethod("mul", Vector.class); + var result = mul.invoke(instanceOfClass_m1, instanceOfClass_m2); + System.out.println(instanceOfClass_m1.toString() + " * " + instanceOfClass_m2.toString() + " = " + result.toString()); + + Vector> res = new Vector<>(); + Vector v5 = new Vector<> (); + v5.addElement(10); + v5.addElement(10); + Vector v6 = new Vector<> (); + v6.addElement(15); + v6.addElement(15); + res.addElement(v5); + res.addElement(v6); + + var instanceOfClass_m3 = matrix.getDeclaredConstructor(Vector.class).newInstance(res); + assertEquals(result, instanceOfClass_m3); + } + + @Test + public void mergeTest() throws Exception { + var classFiles = generateClassFiles("Merge.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Merge").getDeclaredConstructor().newInstance(); + } + + @Test + public void overloadingSortingTest() throws Exception { + var classFiles = generateClassFiles("Sorting.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Sorting").getDeclaredConstructor().newInstance(); + } + + @Test + public void overloadingTest() throws Exception { + var classFiles = generateClassFiles("Overloading.jav", new ByteArrayClassLoader()); + var overloading = classFiles.get("Overloading"); + var overloading2 = classFiles.get("Overloading2"); + var instance1 = overloading.getDeclaredConstructor().newInstance(); + var instance2 = overloading2.getDeclaredConstructor().newInstance(); + + var m1 = overloading.getDeclaredMethod("test", overloading); + assertEquals("Overloading", m1.invoke(instance1, instance1)); + var m2 = overloading.getDeclaredMethod("test", overloading2); + assertEquals("Overloading2", m2.invoke(instance1, instance2)); + } + + @Test + public void plusTest() throws Exception { + var classFiles = generateClassFiles("Plus.jav", new ByteArrayClassLoader()); + var plus = classFiles.get("Plus"); + var instance = plus.getDeclaredConstructor().newInstance(); + + var addInt = plus.getDeclaredMethod("m", Integer.class, Integer.class); + assertEquals(10, addInt.invoke(instance, 7, 3)); + + var addString = plus.getDeclaredMethod("m", String.class, String.class); + assertEquals("ByteCode", addString.invoke(instance, "Byte", "Code")); + } + + @Test + public void relOpsTest() throws Exception { + var classFiles = generateClassFiles("RelOps.jav", new ByteArrayClassLoader()); + var relOps = classFiles.get("RelOps"); + var instance = relOps.getDeclaredConstructor().newInstance(); + + var m = relOps.getDeclaredMethod("m", Integer.class,Integer.class); + assertFalse((Boolean) m.invoke(instance, 7, 3)); + } + + @Test + public void simpleCyclesTest() throws Exception { + var classFiles = generateClassFiles("SimpleCycle.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("SimpleCycle").getDeclaredConstructor().newInstance(); + } + + @Test + public void subMatTest() throws Exception { + var classFiles = generateClassFiles("SubMatrix.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("SubMatrix").getDeclaredConstructor().newInstance(); + } + + @Test + public void tph2Test() throws Exception { + var classFiles = generateClassFiles("Tph2.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Tph2").getDeclaredConstructor().newInstance(); + } + + @Test + public void tph3Test() throws Exception { + var classFiles = generateClassFiles("Tph3.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Tph3").getDeclaredConstructor().newInstance(); + } + + @Test + public void tph4Test() throws Exception { + var classFiles = generateClassFiles("Tph4.jav", new ByteArrayClassLoader()); + var tph4 = classFiles.get("Tph4"); + var instance = tph4.getDeclaredConstructor().newInstance(); + } + + @Test + public void tph5Test() throws Exception { + var classFiles = generateClassFiles("Tph5.jav", new ByteArrayClassLoader()); + var tph5 = classFiles.get("Tph5"); + var instance = tph5.getDeclaredConstructor().newInstance(); + var m = tph5.getDeclaredMethod("m", Object.class, Object.class); + } + + @Test + public void tph6Test() throws Exception { + var classFiles = generateClassFiles("Tph6.jav", new ByteArrayClassLoader()); + var tph5 = classFiles.get("Tph6"); + var instance = tph5.getDeclaredConstructor().newInstance(); + var m = tph5.getDeclaredMethod("m", Object.class, Object.class); + } + + + @Test + public void Tph7Test() throws Exception { + var classFiles = TestCodegen.generateClassFiles("Tph7.jav", new ByteArrayClassLoader()); + var classToTest = classFiles.get("Tph7"); + var instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + + //public DZU m(DZL, DZM); + Method m = classToTest.getDeclaredMethod("m", Object.class, Object.class); + + //System.out.println(m.toString()); + + //Argumenttypes of the method m + var paraTypes = m.getGenericParameterTypes(); + + //Typeparameters of the method m + var typeParaTypes = m.getTypeParameters(); + + //Typeparameters are extracted from the argumenttypes + //Conditions for the extracted typeparameters are set + + //paraTypes[0] = DLZ + var boundFstArg = Arrays.stream(typeParaTypes) + .filter(x -> x.equals(paraTypes[0])).findFirst().get().getBounds(); + + //Bound of DLZ has to be Object + assertEquals(Object.class, Arrays.stream(boundFstArg).findFirst().get()); + + //paraTypes[0] = DZM + var boundSndArg = Arrays.stream(typeParaTypes) + .filter(x -> x.equals(paraTypes[1])).findFirst().get().getBounds(); + + //Bound of DZM has to be the return type of m + assertEquals(Arrays.stream(boundSndArg).findFirst().get(), m.getGenericReturnType()); + + //Bound of the bound of DZM + var boundBoundSndArg = Arrays.stream(typeParaTypes).filter(x -> x.equals(Arrays.stream(boundSndArg) + .findFirst().get())).findFirst().get().getBounds(); + + //boundBoundSndArg have to be a type variable (type of the local variable c) + assertEquals(true, Arrays.stream(boundBoundSndArg).findFirst().get() instanceof TypeVariable); + m.getGenericParameterTypes(); + + //public DZU m2(DZU); + Method m2 = classToTest.getDeclaredMethod("m2", Object.class); + + //Argumenttypes of the method m2 + var paraTypesm2 = m2.getGenericParameterTypes(); + + //Typeparameters of the method m2 + var typeParaTypesm2 = m2.getTypeParameters(); + + //Typeparameters are extracted from the argumenttypes + //Conditions for the extracted typeparameters are set + + //paraTypes[0] = DZU + var fstArgm2 = Arrays.stream(typeParaTypesm2) + .filter(x -> x.equals(paraTypesm2[0])).findFirst().get(); + + //Bound of DZU has to be Object + assertEquals(Object.class, Arrays.stream(fstArgm2.getBounds()).findFirst().get()); + + //DZU has to be the return type of m + assertEquals(fstArgm2, m2.getGenericReturnType()); + } + + + @Test + public void typedIdTest() throws Exception { + var classFiles = generateClassFiles("TypedID.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("TypedID").getDeclaredConstructor().newInstance(); + } + + @Test + public void vectorAddTest() throws Exception { + var classFiles = generateClassFiles("VectorAdd.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("VectorAdd").getDeclaredConstructor().newInstance(); + } + + @Test + public void vectorSuperTest() throws Exception { + var classFiles = generateClassFiles("VectorSuper.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("VectorSuper").getDeclaredConstructor().newInstance(); + } + + @Test + public void yTest() throws Exception { + var classFiles = generateClassFiles("Y.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Y").getDeclaredConstructor().newInstance(); + } +} diff --git a/src/test/java/targetast/TphTest.java b/src/test/java/targetast/TphTest.java new file mode 100644 index 00000000..c3f29c7f --- /dev/null +++ b/src/test/java/targetast/TphTest.java @@ -0,0 +1,114 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; + +public class TphTest { + + private static Class classToTest; + private static Object instanceOfClass; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("Tph.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("Tph"); + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void test1() throws Exception { + var classFiles = TestCodegen.generateClassFiles("Tph7.jav", new ByteArrayClassLoader()); + var classToTest = classFiles.get("Tph7"); + var instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + + //public DZU m(DZL, DZM); + Method m = classToTest.getDeclaredMethod("m", Object.class, Object.class); + + //System.out.println(m.toString()); + + //Argumenttypes of the method m + var paraTypes = m.getGenericParameterTypes(); + + //Typeparameters of the method m + var typeParaTypes = m.getTypeParameters(); + + //Typeparameters are extracted from the argumenttypes + //Conditions for the extracted typeparameters are set + + //paraTypes[0] = DLZ + var boundFstArg = Arrays.stream(typeParaTypes) + .filter(x -> x.equals(paraTypes[0])).findFirst().get().getBounds(); + + //Bound of DLZ has to be Object + assertEquals(Object.class, Arrays.stream(boundFstArg).findFirst().get()); + + //paraTypes[0] = DZM + var boundSndArg = Arrays.stream(typeParaTypes) + .filter(x -> x.equals(paraTypes[1])).findFirst().get().getBounds(); + + //Bound of DZM has to be the return type of m + assertEquals(Arrays.stream(boundSndArg).findFirst().get(), m.getGenericReturnType()); + + //Bound of the bound of DZM + var boundBoundSndArg = Arrays.stream(typeParaTypes).filter(x -> x.equals(Arrays.stream(boundSndArg) + .findFirst().get())).findFirst().get().getBounds(); + + //boundBoundSndArg have to be a type variable (type of the local variable c) + assertEquals(true, Arrays.stream(boundBoundSndArg).findFirst().get() instanceof TypeVariable); + m.getGenericParameterTypes(); + + //public DZU m2(DZU); + Method m2 = classToTest.getDeclaredMethod("m2", Object.class); + + //Argumenttypes of the method m2 + var paraTypesm2 = m2.getGenericParameterTypes(); + + //Typeparameters of the method m2 + var typeParaTypesm2 = m2.getTypeParameters(); + + //Typeparameters are extracted from the argumenttypes + //Conditions for the extracted typeparameters are set + + //paraTypes[0] = DZU + var fstArgm2 = Arrays.stream(typeParaTypesm2) + .filter(x -> x.equals(paraTypesm2[0])).findFirst().get(); + + //Bound of DZU has to be Object + assertEquals(Object.class, Arrays.stream(fstArgm2.getBounds()).findFirst().get()); + + //DZU has to be the return type of m + assertEquals(fstArgm2, m2.getGenericReturnType()); + } + + @Test + public void test2() throws Exception { + Method m = classToTest.getDeclaredMethod("m", Object.class, Object.class); + Object result = m.invoke(instanceOfClass, 1, "sss"); + + assertEquals(1,result); + } + + @Test + public void test3() throws Exception { + Method m = classToTest.getDeclaredMethod("m2", Object.class); + Object result = m.invoke(instanceOfClass, 2); + + assertEquals(2,result); + } + + @Test + public void test4() throws Exception { + Method m = classToTest.getDeclaredMethod("m2", Object.class); + Object result = m.invoke(instanceOfClass,"xxx"); + + assertEquals("xxx",result); + } + +} diff --git a/src/test/java/targetast/WhileTest.java b/src/test/java/targetast/WhileTest.java new file mode 100644 index 00000000..0321c90f --- /dev/null +++ b/src/test/java/targetast/WhileTest.java @@ -0,0 +1,43 @@ +package targetast; + +import de.dhbwstuttgart.target.ByteArrayClassLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; + +public class WhileTest { + private static Class classToTest; + private static Object instanceOfClass; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + var classFiles = TestCodegen.generateClassFiles("While.jav", new ByteArrayClassLoader()); + classToTest = classFiles.get("While"); + instanceOfClass = classToTest.getDeclaredConstructor().newInstance(); + } + + @Test + public void test() throws Exception { + Method m = classToTest.getDeclaredMethod("m", Integer.class); + Integer result = (Integer) m.invoke(instanceOfClass, 0); + assertEquals(Integer.valueOf(2), result); + } + + @Test + public void testDouble() throws Exception { + Method m = classToTest.getDeclaredMethod("m", Double.class); + Double result = (Double) m.invoke(instanceOfClass, 0.0); + assertEquals(Double.valueOf(2.0), result); + } + + @Test + public void testLong() throws Exception { + Method m = classToTest.getDeclaredMethod("m", Long.class); + Long result = (Long) m.invoke(instanceOfClass, 0l); + assertEquals(Long.valueOf(2l), result); + } + +} diff --git a/src/test/resources/target/Test.java b/src/test/resources/target/Test.java new file mode 100644 index 00000000..f7e1a0bc --- /dev/null +++ b/src/test/resources/target/Test.java @@ -0,0 +1,9 @@ +public class Test { + public void lambda() { + Interface mul2 = (Integer a) -> a * 2; + } +} + +interface Interface { + R apply(T t); +} \ No newline at end of file