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 java.lang.invoke.*;
import java.lang.reflect.Modifier;
import java.util.*;
import static org.objectweb.asm.Opcodes.*;
@ -1345,12 +1346,17 @@ 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()));
}
if (method.block() != null) {
mv.visitCode();
var state = new State(method.signature().returnType(), mv, method.isStatic() ? 0 : 1);
for (var param : method.signature().parameters()) {
@ -1360,30 +1366,41 @@ public class Codegen {
if (method.signature().returnType() == null)
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
}
mv.visitEnd();
}
private static String generateSignature(TargetStructure clazz, Set<TargetGeneric> generics) {
String ret = "";
if (generics.size() > 0) {
if (!generics.isEmpty()) {
ret += "<";
for (var generic : generics) {
ret += generic.name() + ":" + generic.bound().toDescriptor();
}
ret += ">";
}
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);

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) {
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;

View File

@ -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 ");

View File

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

View File

@ -262,6 +262,7 @@ public abstract class GenerateGenerics {
}
}
if (method.block != null)
method.block.accept(new TracingStatementVisitor() {
private RefTypeOrTPHOrWildcardOrGeneric superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
@ -493,6 +494,7 @@ public abstract class GenerateGenerics {
typeVariables.addAll(findTypeVariables(arg.getType()));
}
if (method.block != null)
method.block.accept(new TracingStatementVisitor() {
@Override
public void visit(LocalVarDecl localVarDecl) {

View File

@ -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;

View File

@ -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<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 static final TargetType RECORD = new TargetRefType("java.lang.Record");
@Override
public TargetType superType() {
return RECORD;
}

View File

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