Add interfaces

This commit is contained in:
Daniel Holle 2023-10-19 17:02:22 +02:00
parent 628f1631e8
commit 892ba5fff0
10 changed files with 242 additions and 157 deletions

View File

@ -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();
}
}

View File

@ -11,6 +11,7 @@ import de.dhbwstuttgart.target.tree.type.*;
import org.objectweb.asm.*; import org.objectweb.asm.*;
import java.lang.invoke.*; import java.lang.invoke.*;
import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.Opcodes.*;
@ -1345,45 +1346,61 @@ public class Codegen {
} }
private void generateMethod(TargetMethod method) { 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 // 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) { if (method.txSignature() != null) {
mv.visitAttribute(new JavaTXSignatureAttribute(method.getTXSignature())); mv.visitAttribute(new JavaTXSignatureAttribute(method.getTXSignature()));
} }
mv.visitCode(); if (method.block() != null) {
var state = new State(method.signature().returnType(), mv, method.isStatic() ? 0 : 1); mv.visitCode();
for (var param : method.signature().parameters()) { var state = new State(method.signature().returnType(), mv, method.isStatic() ? 0 : 1);
bindLocalVariables(state, param.pattern(), 1, 0); 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(); mv.visitEnd();
} }
private static String generateSignature(TargetStructure clazz, Set<TargetGeneric> generics) { private static String generateSignature(TargetStructure clazz, Set<TargetGeneric> generics) {
String ret = ""; String ret = "";
if (generics.size() > 0) { if (!generics.isEmpty()) {
ret += "<"; ret += "<";
for (var generic : generics) { for (var generic : generics) {
ret += generic.name() + ":" + generic.bound().toDescriptor(); ret += generic.name() + ":" + generic.bound().toDescriptor();
} }
ret += ">"; 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() { public byte[] generate() {
var access = clazz.modifiers(); var access = clazz.modifiers();
if ((access & ACC_PRIVATE) == 0 && (access & ACC_PROTECTED) == 0) // TODO Implement access modifiers properly if ((access & ACC_PRIVATE) == 0 && (access & ACC_PROTECTED) == 0) // TODO Implement access modifiers properly
access |= ACC_PUBLIC; 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)); var signature = generateSignature(clazz, clazz.generics());
if (clazz.txGenerics() != null) 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()))); cw.visitAttribute(new JavaTXSignatureAttribute(generateSignature(clazz, clazz.txGenerics())));
clazz.fields().forEach(this::generateField); clazz.fields().forEach(this::generateField);

View File

@ -40,8 +40,9 @@ public class ClassOrInterface extends SyntaxTreeNode implements TypeScope {
public ClassOrInterface(int modifiers, JavaClassName name, List<Field> fielddecl, Optional<Constructor> fieldInitializations, List<Method> methods, List<Constructor> constructors, GenericDeclarationList genericClassParameters, RefType superClass, Boolean isInterface, List<RefType> implementedInterfaces, List<RefType> permittedSubtypes, Token offset) { public ClassOrInterface(int modifiers, JavaClassName name, List<Field> fielddecl, Optional<Constructor> fieldInitializations, List<Method> methods, List<Constructor> constructors, GenericDeclarationList genericClassParameters, RefType superClass, Boolean isInterface, List<RefType> implementedInterfaces, List<RefType> permittedSubtypes, Token offset) {
super(offset); super(offset);
if (isInterface && !Modifier.isInterface(modifiers)) if (isInterface) {
modifiers += Modifier.INTERFACE; modifiers |= Modifier.INTERFACE | Modifier.ABSTRACT;
}
this.modifiers = modifiers; this.modifiers = modifiers;
this.name = name; this.name = name;
this.fields = fielddecl; this.fields = fielddecl;
@ -72,6 +73,10 @@ public class ClassOrInterface extends SyntaxTreeNode implements TypeScope {
this.constructors = new ArrayList<>(cl.constructors); this.constructors = new ArrayList<>(cl.constructors);
} }
public boolean isInterface() {
return (Modifier.INTERFACE & this.getModifiers()) != 0;
}
// Gets if it is added // Gets if it is added
public Boolean areMethodsAdded() { public Boolean areMethodsAdded() {
return methodAdded; return methodAdded;

View File

@ -113,7 +113,7 @@ public class OutputGenerator implements ASTVisitor {
@Override @Override
public void visit(ClassOrInterface classOrInterface) { public void visit(ClassOrInterface classOrInterface) {
if ((Modifier.INTERFACE & classOrInterface.getModifiers()) == 1) { if (classOrInterface.isInterface()) {
out.append("interface "); out.append("interface ");
} else { } else {
out.append("class "); out.append("class ");

View File

@ -146,6 +146,8 @@ public class ASTToTargetAST {
if (input instanceof Record) if (input instanceof Record)
return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); 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); 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) { protected TargetBlock convert(Block block) {
if (block == null) return null;
return new TargetBlock(block.statements.stream().map(this::convert).toList()); return new TargetBlock(block.statements.stream().map(this::convert).toList());
} }

View File

@ -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 @Override
public void visit(MethodCall methodCall) { public void visit(MethodCall methodCall) {
//Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg //Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg
//betrachtet werden muessen //betrachtet werden muessen
//Definition 7.2 (Family of generated generics). T1 <. R1 <.^ R2 <. T2 //Definition 7.2 (Family of generated generics). T1 <. R1 <.^ R2 <. T2
Set<TPH> T1s = Set<TPH> T1s =
methodCall.getArgumentList() methodCall.getArgumentList()
.getArguments() .getArguments()
.stream() .stream()
.map(TypableStatement::getType) .map(TypableStatement::getType)
.collect(Collectors.toCollection(HashSet::new)) .collect(Collectors.toCollection(HashSet::new))
.stream().filter(TypePlaceholder.class::isInstance) .stream().filter(TypePlaceholder.class::isInstance)
.map(TypePlaceholder.class::cast) .map(TypePlaceholder.class::cast)
.map(TPH::new) .map(TPH::new)
.collect(Collectors.toCollection(HashSet::new)); .collect(Collectors.toCollection(HashSet::new));
Set<TPH> T2s = new HashSet<>(); Set<TPH> T2s = new HashSet<>();
findTphs(superType, T2s); findTphs(superType, T2s);
System.out.println("T1s: " + T1s + " T2s: " + T2s); System.out.println("T1s: " + T1s + " T2s: " + T2s);
//Ende //Ende
superType = methodCall.receiverType; superType = methodCall.receiverType;
methodCall.receiver.accept(this); methodCall.receiver.accept(this);
for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) { for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) {
superType = methodCall.arglist.getArguments().get(i).getType(); superType = methodCall.arglist.getArguments().get(i).getType();
methodCall.arglist.getArguments().get(i).accept(this); methodCall.arglist.getArguments().get(i).accept(this);
} }
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) { if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
if (expressionReceiver.expr instanceof This) { if (expressionReceiver.expr instanceof This) {
var optMethod = astToTargetAST.findMethod(owner, methodCall.name, methodCall.getArgumentList()); var optMethod = astToTargetAST.findMethod(owner, methodCall.name, methodCall.getArgumentList());
if (optMethod.isEmpty()) return; if (optMethod.isEmpty()) return;
var method2 = optMethod.get(); var method2 = optMethod.get();
System.out.println("In: " + method.getName() + " Method: " + method2.getName()); System.out.println("In: " + method.getName() + " Method: " + method2.getName());
var generics = family(owner, method2); var generics = family(owner, method2);
// transitive and // transitive and
var all = transitiveClosure(generics); var all = transitiveClosure(generics);
// reflexive // reflexive
var toAdd = new HashSet<Pair>(); var toAdd = new HashSet<Pair>();
for (var generic : all) { for (var generic : all) {
toAdd.add(new PairLT(generic.left, generic.left)); toAdd.add(new PairLT(generic.left, generic.left));
} }
all.addAll(toAdd); all.addAll(toAdd);
HashSet<PairLT> newPairs = new HashSet<>(); HashSet<PairLT> newPairs = new HashSet<>();
// Loop from hell // Loop from hell
outer: outer:
for (var R1 : typeVariables) { for (var R1 : typeVariables) {
if (typeVariablesOfClass.contains(R1)) continue; if (typeVariablesOfClass.contains(R1)) continue;
for (var generic : all) for (var generic : all)
if (generic instanceof PairLT ptph) { if (generic instanceof PairLT ptph) {
for (var pair : simplifiedConstraints) { for (var pair : simplifiedConstraints) {
if (!(pair.left.equals(R1) && pair.right.equals(ptph.left))) if (!(pair.left.equals(R1) && pair.right.equals(ptph.left)))
continue; continue;
for (var R2 : typeVariables) { for (var R2 : typeVariables) {
for (var pair2 : simplifiedConstraints) { for (var pair2 : simplifiedConstraints) {
if (!(pair2.right.equals(R2) && pair2.left.equals(ptph.right))) if (!(pair2.right.equals(R2) && pair2.left.equals(ptph.right)))
continue; continue;
if (R1.equals(R2)) continue; if (R1.equals(R2)) continue;
if (!T1s.contains(R1) || !T2s.contains(R2)) continue; if (!T1s.contains(R1) || !T2s.contains(R2)) continue;
var newPair = new PairLT(R1, R2); var newPair = new PairLT(R1, R2);
System.out.println("New pair: " + newPair); System.out.println("New pair: " + newPair);
newPairs.add(newPair); newPairs.add(newPair);
if (!containsRelation(result, newPair)) if (!containsRelation(result, newPair))
addToPairs(result, newPair); addToPairs(result, newPair);
continue outer; continue outer;
}
} }
} }
} }
} }
simplifiedConstraints.addAll(newPairs);
} }
simplifiedConstraints.addAll(newPairs);
} }
} }
}
@Override @Override
public void visit(LambdaExpression lambdaExpression) { public void visit(LambdaExpression lambdaExpression) {
superType = new Void(new NullToken()); superType = new Void(new NullToken());
lambdaExpression.methodBody.accept(this); 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 @Override
public void visit(IfStmt ifStmt) { public void visit(Assign assign) {
superType = new Void(new NullToken()); superType = assign.rightSide.getType();
ifStmt.expr.accept(this); assign.rightSide.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(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); var closure = transitiveClosure(simplifiedConstraints);
// Type variables with bounds that are also type variables of the class // 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())); typeVariables.addAll(findTypeVariables(arg.getType()));
} }
method.block.accept(new TracingStatementVisitor() { if (method.block != null)
@Override method.block.accept(new TracingStatementVisitor() {
public void visit(LocalVarDecl localVarDecl) { @Override
typeVariables.addAll(findTypeVariables(localVarDecl.getType())); public void visit(LocalVarDecl localVarDecl) {
} typeVariables.addAll(findTypeVariables(localVarDecl.getType()));
}
@Override @Override
public void visit(MethodCall methodCall) { public void visit(MethodCall methodCall) {
super.visit(methodCall); super.visit(methodCall);
typeVariables.addAll(findTypeVariables(methodCall.getType())); typeVariables.addAll(findTypeVariables(methodCall.getType()));
} }
}); });
} }
abstract void generics(ClassOrInterface owner, Method method, Set<Pair> result, Set<TPH> javaTypeVariablesOfClass); abstract void generics(ClassOrInterface owner, Method method, Set<Pair> result, Set<TPH> javaTypeVariablesOfClass);

View File

@ -1,6 +1,7 @@
package de.dhbwstuttgart.target.tree; package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.parser.scope.JavaClassName; 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.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;

View File

@ -1,6 +1,24 @@
package de.dhbwstuttgart.target.tree; 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<TargetMethod> methods, List<TargetInterface> extendedInterfaces) { import java.util.List;
import java.util.Set;
public record TargetInterface(int modifiers, JavaClassName qualifiedName, Set<TargetGeneric> generics, Set<TargetGeneric> txGenerics, List<TargetMethod> methods, List<TargetType> implementingInterfaces) implements TargetStructure {
@Override
public TargetType superType() {
return null;
}
@Override
public List<TargetConstructor> constructors() {
return List.of();
}
@Override
public List<TargetField> fields() {
return List.of();
}
} }

View File

@ -10,8 +10,6 @@ import java.util.Set;
public record TargetRecord(int modifiers, JavaClassName qualifiedName, Set<TargetGeneric> generics, Set<TargetGeneric> txGenerics, List<TargetType> implementingInterfaces, List<TargetConstructor> constructors, List<TargetField> fields, List<TargetMethod> methods) implements TargetStructure { public record TargetRecord(int modifiers, JavaClassName qualifiedName, Set<TargetGeneric> generics, Set<TargetGeneric> txGenerics, List<TargetType> implementingInterfaces, List<TargetConstructor> constructors, List<TargetField> fields, List<TargetMethod> methods) implements TargetStructure {
public static final TargetType RECORD = new TargetRefType("java.lang.Record"); public static final TargetType RECORD = new TargetRefType("java.lang.Record");
@Override
public TargetType superType() { public TargetType superType() {
return RECORD; return RECORD;
} }

View File

@ -713,4 +713,12 @@ public class TestComplete {
assertEquals(m1.invoke(instance, pt), 30); assertEquals(m1.invoke(instance, pt), 30);
assertEquals(m2.invoke(instance, 10), 10); 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()));
}
} }