From 892ba5fff0ef70183c61d8afd31fc2e86fb81c59 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 19 Oct 2023 17:02:22 +0200 Subject: [PATCH] Add interfaces --- resources/bytecode/javFiles/Interfaces.jav | 33 +++ .../de/dhbwstuttgart/bytecode/Codegen.java | 45 ++- .../syntaxtree/ClassOrInterface.java | 9 +- .../syntaxtree/visual/OutputGenerator.java | 2 +- .../target/generate/ASTToTargetAST.java | 3 + .../target/generate/GenerateGenerics.java | 274 +++++++++--------- .../target/tree/TargetClass.java | 1 + .../target/tree/TargetInterface.java | 22 +- .../target/tree/TargetRecord.java | 2 - src/test/java/TestComplete.java | 8 + 10 files changed, 242 insertions(+), 157 deletions(-) create mode 100644 resources/bytecode/javFiles/Interfaces.jav diff --git a/resources/bytecode/javFiles/Interfaces.jav b/resources/bytecode/javFiles/Interfaces.jav new file mode 100644 index 00000000..0c4cd21c --- /dev/null +++ b/resources/bytecode/javFiles/Interfaces.jav @@ -0,0 +1,33 @@ +import java.lang.Integer; + +interface A { + void method1(); + default method2() { + } +} + +interface B { + void method3(); +} + +interface C { + Integer myInt(); +} + +class ClassX implements A { +} + +record ClassY(Integer myInt) implements C {} + +public class Interfaces implements A, B { + public void method1() { + } + public void method3() { + var intf = new Interfaces(); + intf = new ClassX(); + intf.method1(); + + C c = new ClassY(10); + c.myInt(); + } +} \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index 1c67de73..d02598d6 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -11,6 +11,7 @@ import de.dhbwstuttgart.target.tree.type.*; import org.objectweb.asm.*; import java.lang.invoke.*; +import java.lang.reflect.Modifier; import java.util.*; import static org.objectweb.asm.Opcodes.*; @@ -1345,45 +1346,61 @@ public class Codegen { } private void generateMethod(TargetMethod method) { + var access = method.access() | ACC_PUBLIC; + if (method.block() == null) + access |= ACC_ABSTRACT; + // 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); + MethodVisitor mv = cw.visitMethod(access, method.name(), method.getDescriptor(), method.getSignature(), null); if (method.txSignature() != null) { mv.visitAttribute(new JavaTXSignatureAttribute(method.getTXSignature())); } - mv.visitCode(); - var state = new State(method.signature().returnType(), mv, method.isStatic() ? 0 : 1); - for (var param : method.signature().parameters()) { - bindLocalVariables(state, param.pattern(), 1, 0); + if (method.block() != null) { + mv.visitCode(); + var state = new State(method.signature().returnType(), mv, method.isStatic() ? 0 : 1); + for (var param : method.signature().parameters()) { + bindLocalVariables(state, param.pattern(), 1, 0); + } + generate(state, method.block()); + if (method.signature().returnType() == null) + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); } - generate(state, method.block()); - if (method.signature().returnType() == null) - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); mv.visitEnd(); } private static String generateSignature(TargetStructure clazz, Set generics) { String ret = ""; - if (generics.size() > 0) { + if (!generics.isEmpty()) { ret += "<"; for (var generic : generics) { ret += generic.name() + ":" + generic.bound().toDescriptor(); } ret += ">"; } - ret += clazz.superType().toDescriptor(); + if (clazz.superType() != null) + ret += clazz.superType().toDescriptor(); + for (var intf : clazz.implementingInterfaces()) { + ret += intf.toSignature(); + } - return ret; + return ret.isEmpty() ? null : ret; } public byte[] generate() { var access = clazz.modifiers(); if ((access & ACC_PRIVATE) == 0 && (access & ACC_PROTECTED) == 0) // TODO Implement access modifiers properly access |= ACC_PUBLIC; + if (!(clazz instanceof TargetInterface)) + access |= ACC_SUPER; - cw.visit(V1_8, access | ACC_SUPER, clazz.qualifiedName().toString().replaceAll("\\.", "/"), generateSignature(clazz, clazz.generics()), clazz.superType() != null ? clazz.superType().getInternalName() : "java/lang/Object", clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)); - if (clazz.txGenerics() != null) + var signature = generateSignature(clazz, clazz.generics()); + var interfaces = clazz.implementingInterfaces().stream().map(TargetType::getInternalName).toArray(String[]::new); + var superType = clazz.superType() != null ? clazz.superType().getInternalName() : "java/lang/Object"; + + cw.visit(V1_8, access, clazz.qualifiedName().toString().replaceAll("\\.", "/"), signature, superType, interfaces); + if (clazz.txGenerics() != null && signature != null) cw.visitAttribute(new JavaTXSignatureAttribute(generateSignature(clazz, clazz.txGenerics()))); clazz.fields().forEach(this::generateField); diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/ClassOrInterface.java b/src/main/java/de/dhbwstuttgart/syntaxtree/ClassOrInterface.java index 5e9b8d8f..7dfd75e3 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/ClassOrInterface.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/ClassOrInterface.java @@ -40,8 +40,9 @@ public class ClassOrInterface extends SyntaxTreeNode implements TypeScope { public ClassOrInterface(int modifiers, JavaClassName name, List fielddecl, Optional fieldInitializations, List methods, List constructors, GenericDeclarationList genericClassParameters, RefType superClass, Boolean isInterface, List implementedInterfaces, List permittedSubtypes, Token offset) { super(offset); - if (isInterface && !Modifier.isInterface(modifiers)) - modifiers += Modifier.INTERFACE; + if (isInterface) { + modifiers |= Modifier.INTERFACE | Modifier.ABSTRACT; + } this.modifiers = modifiers; this.name = name; this.fields = fielddecl; @@ -72,6 +73,10 @@ public class ClassOrInterface extends SyntaxTreeNode implements TypeScope { this.constructors = new ArrayList<>(cl.constructors); } + public boolean isInterface() { + return (Modifier.INTERFACE & this.getModifiers()) != 0; + } + // Gets if it is added public Boolean areMethodsAdded() { return methodAdded; diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java b/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java index b4024723..1afcfdea 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java @@ -113,7 +113,7 @@ public class OutputGenerator implements ASTVisitor { @Override public void visit(ClassOrInterface classOrInterface) { - if ((Modifier.INTERFACE & classOrInterface.getModifiers()) == 1) { + if (classOrInterface.isInterface()) { out.append("interface "); } else { out.append("class "); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 2c394798..21745c13 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -146,6 +146,8 @@ public class ASTToTargetAST { if (input instanceof Record) return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); + else if (input.isInterface()) + return new TargetInterface(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, methods, superInterfaces); else return new TargetClass(input.getModifiers(), input.getClassName(), convert(input.getSuperClass(), generics.javaGenerics), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); } @@ -356,6 +358,7 @@ public class ASTToTargetAST { } protected TargetBlock convert(Block block) { + if (block == null) return null; return new TargetBlock(block.statements.stream().map(this::convert).toList()); } diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java index 3e26ff8f..a40f9e19 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java @@ -262,153 +262,154 @@ public abstract class GenerateGenerics { } } - method.block.accept(new TracingStatementVisitor() { + if (method.block != null) + method.block.accept(new TracingStatementVisitor() { - private RefTypeOrTPHOrWildcardOrGeneric superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + private RefTypeOrTPHOrWildcardOrGeneric superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); - @Override - public void visit(MethodCall methodCall) { - //Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg - //betrachtet werden muessen - //Definition 7.2 (Family of generated generics). T1 <. R1 <.^∗ R2 <. T2 - Set T1s = - methodCall.getArgumentList() - .getArguments() - .stream() - .map(TypableStatement::getType) - .collect(Collectors.toCollection(HashSet::new)) - .stream().filter(TypePlaceholder.class::isInstance) - .map(TypePlaceholder.class::cast) - .map(TPH::new) - .collect(Collectors.toCollection(HashSet::new)); - Set T2s = new HashSet<>(); - findTphs(superType, T2s); + @Override + public void visit(MethodCall methodCall) { + //Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg + //betrachtet werden muessen + //Definition 7.2 (Family of generated generics). T1 <. R1 <.^∗ R2 <. T2 + Set T1s = + methodCall.getArgumentList() + .getArguments() + .stream() + .map(TypableStatement::getType) + .collect(Collectors.toCollection(HashSet::new)) + .stream().filter(TypePlaceholder.class::isInstance) + .map(TypePlaceholder.class::cast) + .map(TPH::new) + .collect(Collectors.toCollection(HashSet::new)); + Set T2s = new HashSet<>(); + findTphs(superType, T2s); - System.out.println("T1s: " + T1s + " T2s: " + T2s); - //Ende + System.out.println("T1s: " + T1s + " T2s: " + T2s); + //Ende - superType = methodCall.receiverType; - methodCall.receiver.accept(this); - for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) { - superType = methodCall.arglist.getArguments().get(i).getType(); - methodCall.arglist.getArguments().get(i).accept(this); - } + superType = methodCall.receiverType; + methodCall.receiver.accept(this); + for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) { + superType = methodCall.arglist.getArguments().get(i).getType(); + methodCall.arglist.getArguments().get(i).accept(this); + } - if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) { - if (expressionReceiver.expr instanceof This) { - var optMethod = astToTargetAST.findMethod(owner, methodCall.name, methodCall.getArgumentList()); - if (optMethod.isEmpty()) return; - var method2 = optMethod.get(); - System.out.println("In: " + method.getName() + " Method: " + method2.getName()); - var generics = family(owner, method2); + if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) { + if (expressionReceiver.expr instanceof This) { + var optMethod = astToTargetAST.findMethod(owner, methodCall.name, methodCall.getArgumentList()); + if (optMethod.isEmpty()) return; + var method2 = optMethod.get(); + System.out.println("In: " + method.getName() + " Method: " + method2.getName()); + var generics = family(owner, method2); - // transitive and - var all = transitiveClosure(generics); - // reflexive - var toAdd = new HashSet(); - for (var generic : all) { - toAdd.add(new PairLT(generic.left, generic.left)); - } - all.addAll(toAdd); + // transitive and + var all = transitiveClosure(generics); + // reflexive + var toAdd = new HashSet(); + for (var generic : all) { + toAdd.add(new PairLT(generic.left, generic.left)); + } + all.addAll(toAdd); - HashSet newPairs = new HashSet<>(); + HashSet newPairs = new HashSet<>(); - // Loop from hell - outer: - for (var R1 : typeVariables) { - if (typeVariablesOfClass.contains(R1)) continue; - for (var generic : all) - if (generic instanceof PairLT ptph) { - for (var pair : simplifiedConstraints) { - if (!(pair.left.equals(R1) && pair.right.equals(ptph.left))) - continue; + // Loop from hell + outer: + for (var R1 : typeVariables) { + if (typeVariablesOfClass.contains(R1)) continue; + for (var generic : all) + if (generic instanceof PairLT ptph) { + for (var pair : simplifiedConstraints) { + if (!(pair.left.equals(R1) && pair.right.equals(ptph.left))) + continue; - for (var R2 : typeVariables) { - for (var pair2 : simplifiedConstraints) { + for (var R2 : typeVariables) { + for (var pair2 : simplifiedConstraints) { - if (!(pair2.right.equals(R2) && pair2.left.equals(ptph.right))) - continue; - if (R1.equals(R2)) continue; - if (!T1s.contains(R1) || !T2s.contains(R2)) continue; + if (!(pair2.right.equals(R2) && pair2.left.equals(ptph.right))) + continue; + if (R1.equals(R2)) continue; + if (!T1s.contains(R1) || !T2s.contains(R2)) continue; - var newPair = new PairLT(R1, R2); - System.out.println("New pair: " + newPair); - newPairs.add(newPair); + var newPair = new PairLT(R1, R2); + System.out.println("New pair: " + newPair); + newPairs.add(newPair); - if (!containsRelation(result, newPair)) - addToPairs(result, newPair); - continue outer; + if (!containsRelation(result, newPair)) + addToPairs(result, newPair); + continue outer; + } } } } - } + } + simplifiedConstraints.addAll(newPairs); } - simplifiedConstraints.addAll(newPairs); } } - } - @Override - public void visit(LambdaExpression lambdaExpression) { - superType = new Void(new NullToken()); - lambdaExpression.methodBody.accept(this); - } - - @Override - public void visit(Assign assign) { - superType = assign.rightSide.getType(); - assign.rightSide.accept(this); - } - - @Override - public void visit(BinaryExpr binary) { - superType = new Void(new NullToken()); - binary.lexpr.accept(this); - superType = new Void(new NullToken()); - binary.rexpr.accept(this); - } - - @Override - public void visit(Block block) { - for (var expr : block.statements) { - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); - expr.accept(this); + @Override + public void visit(LambdaExpression lambdaExpression) { + superType = new Void(new NullToken()); + lambdaExpression.methodBody.accept(this); } - } - @Override - public void visit(IfStmt ifStmt) { - superType = new Void(new NullToken()); - ifStmt.expr.accept(this); - superType = new Void(new NullToken()); - ifStmt.then_block.accept(this); - superType = new Void(new NullToken()); - if (ifStmt.else_block != null) - ifStmt.else_block.accept(this); - } - - @Override - public void visit(Return aReturn) { - superType = aReturn.getType(); - aReturn.retexpr.accept(this); - } - - @Override - public void visit(WhileStmt whileStmt) { - superType = new Void(new NullToken()); - whileStmt.expr.accept(this); - superType = new Void(new NullToken()); - whileStmt.loopBlock.accept(this); - } - - @Override - public void visit(ArgumentList arglist) { - for (int i = 0; i < arglist.getArguments().size(); i++) { - superType = arglist.getArguments().get(i).getType(); - arglist.getArguments().get(i).accept(this); + @Override + public void visit(Assign assign) { + superType = assign.rightSide.getType(); + assign.rightSide.accept(this); } - } - }); + + @Override + public void visit(BinaryExpr binary) { + superType = new Void(new NullToken()); + binary.lexpr.accept(this); + superType = new Void(new NullToken()); + binary.rexpr.accept(this); + } + + @Override + public void visit(Block block) { + for (var expr : block.statements) { + superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + expr.accept(this); + } + } + + @Override + public void visit(IfStmt ifStmt) { + superType = new Void(new NullToken()); + ifStmt.expr.accept(this); + superType = new Void(new NullToken()); + ifStmt.then_block.accept(this); + superType = new Void(new NullToken()); + if (ifStmt.else_block != null) + ifStmt.else_block.accept(this); + } + + @Override + public void visit(Return aReturn) { + superType = aReturn.getType(); + aReturn.retexpr.accept(this); + } + + @Override + public void visit(WhileStmt whileStmt) { + superType = new Void(new NullToken()); + whileStmt.expr.accept(this); + superType = new Void(new NullToken()); + whileStmt.loopBlock.accept(this); + } + + @Override + public void visit(ArgumentList arglist) { + for (int i = 0; i < arglist.getArguments().size(); i++) { + superType = arglist.getArguments().get(i).getType(); + arglist.getArguments().get(i).accept(this); + } + } + }); var closure = transitiveClosure(simplifiedConstraints); // Type variables with bounds that are also type variables of the class @@ -493,18 +494,19 @@ public abstract class GenerateGenerics { typeVariables.addAll(findTypeVariables(arg.getType())); } - method.block.accept(new TracingStatementVisitor() { - @Override - public void visit(LocalVarDecl localVarDecl) { - typeVariables.addAll(findTypeVariables(localVarDecl.getType())); - } + if (method.block != null) + method.block.accept(new TracingStatementVisitor() { + @Override + public void visit(LocalVarDecl localVarDecl) { + typeVariables.addAll(findTypeVariables(localVarDecl.getType())); + } - @Override - public void visit(MethodCall methodCall) { - super.visit(methodCall); - typeVariables.addAll(findTypeVariables(methodCall.getType())); - } - }); + @Override + public void visit(MethodCall methodCall) { + super.visit(methodCall); + typeVariables.addAll(findTypeVariables(methodCall.getType())); + } + }); } abstract void generics(ClassOrInterface owner, Method method, Set result, Set javaTypeVariablesOfClass); diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java index 700815d5..ab68ca37 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java @@ -1,6 +1,7 @@ package de.dhbwstuttgart.target.tree; import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.target.tree.expression.TargetBlock; import de.dhbwstuttgart.target.tree.type.TargetRefType; import de.dhbwstuttgart.target.tree.type.TargetType; diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java index f15acad4..c5442cce 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java @@ -1,6 +1,24 @@ package de.dhbwstuttgart.target.tree; -import java.util.List; +import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.target.tree.type.TargetType; -public record TargetInterface(String name, List methods, List extendedInterfaces) { +import java.util.List; +import java.util.Set; + +public record TargetInterface(int modifiers, JavaClassName qualifiedName, Set generics, Set txGenerics, List methods, List implementingInterfaces) implements TargetStructure { + @Override + public TargetType superType() { + return null; + } + + @Override + public List constructors() { + return List.of(); + } + + @Override + public List fields() { + return List.of(); + } } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java index a3dbac7b..a6571002 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java @@ -10,8 +10,6 @@ import java.util.Set; public record TargetRecord(int modifiers, JavaClassName qualifiedName, Set generics, Set txGenerics, List implementingInterfaces, List constructors, List fields, List methods) implements TargetStructure { public static final TargetType RECORD = new TargetRefType("java.lang.Record"); - - @Override public TargetType superType() { return RECORD; } diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index 9265e83a..0d61e0ba 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -713,4 +713,12 @@ public class TestComplete { assertEquals(m1.invoke(instance, pt), 30); assertEquals(m2.invoke(instance, 10), 10); } + + @Test + public void testInterfaces() throws Exception { + var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Interfaces.jav"); + var clazz = classFiles.get("Interfaces"); + var instance = clazz.getDeclaredConstructor().newInstance(); + System.out.println(Arrays.toString(clazz.getInterfaces())); + } }