Add generics to the generated class file

This commit is contained in:
Victorious3 2022-06-23 20:39:34 +02:00
parent 7e259e2597
commit a8be387dd9
12 changed files with 87 additions and 43 deletions

View File

@ -617,9 +617,11 @@ public class Codegen {
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:
@ -737,11 +739,7 @@ public class Codegen {
generate(state, e);
boxPrimitive(state, e.type());
}
if (call.expr() instanceof TargetThis)
mv.visitMethodInsn(INVOKESPECIAL, clazz.getName(), "<init>", call.getDescriptor(), false);
else if (call.expr() instanceof TargetSuper)
mv.visitMethodInsn(INVOKESPECIAL, clazz.superType().getName(), "<init>", call.getDescriptor(), false);
else mv.visitMethodInsn(call.isInterface() ? INVOKEINTERFACE : call.isStatic() ? INVOKESTATIC: INVOKEVIRTUAL,
mv.visitMethodInsn(call.isInterface() ? INVOKEINTERFACE : call.isStatic() ? INVOKESTATIC: call.name() == "<init>" ? INVOKESPECIAL : INVOKEVIRTUAL,
call.owner().getName(), call.name(), call.getDescriptor(), call.isInterface());
if (call.type() != null)
unboxPrimitive(state, call.type());
@ -766,29 +764,15 @@ public class Codegen {
}
private void generateField(TargetField field) {
cw.visitField(field.access(), field.name(), field.getDescriptor(), null, null);
}
private boolean hasThisOrSuperCall(TargetBlock block) {
if (block.statements().size() == 0) return false;
TargetExpression first = block.statements().get(0);
if (!(first instanceof TargetMethodCall)) return false;
var methodCall = (TargetMethodCall) first;
if (methodCall.expr() instanceof TargetThis || methodCall.expr() instanceof TargetSuper)
return true;
return false;
cw.visitField(field.access(), field.name(), field.type().toSignature(), field.type().toGenericSignature(), null);
}
private void generateConstructor(TargetConstructor constructor) {
MethodVisitor mv = cw.visitMethod(constructor.access(), "<init>", constructor.getDescriptor(), null, null);
MethodVisitor mv = cw.visitMethod(constructor.access(), "<init>", constructor.getDescriptor(), constructor.getSignature(), null);
mv.visitCode();
var state = new State(mv, 1);
for (var param: constructor.parameters())
state.createVariable(param.name(), param.type());
if (!hasThisOrSuperCall(constructor.block())) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, clazz.superType().getName(), "<init>", "()V", false);
}
generate(state, constructor.block());
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
@ -796,7 +780,7 @@ public class Codegen {
}
private void generateMethod(TargetMethod method) {
MethodVisitor mv = cw.visitMethod(method.access(), method.name(), method.getDescriptor(), null, null);
MethodVisitor mv = cw.visitMethod(method.access(), method.name(), method.getDescriptor(), method.getSignature(), null);
mv.visitCode();
var state = new State(mv, method.isStatic() ? 0 : 1);
for (var param: method.parameters())
@ -808,9 +792,20 @@ public class Codegen {
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(), clazz.qualifiedName(),
null, clazz.superType() != null ? clazz.superType().getName(): "java/lang/Object",
generateSignature(clazz), clazz.superType() != null ? clazz.superType().getName(): "java/lang/Object",
clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)
);
clazz.fields().forEach(this::generateField);

View File

@ -15,6 +15,7 @@ public class ASTToTargetAST {
protected List<Sigma> 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<Method, HashSet<TargetGeneric>> computedGenericsOfMethods = new HashMap<>();
@ -294,6 +295,7 @@ public class ASTToTargetAST {
}
private List<TargetConstructor> convert(ClassOrInterface owner, Constructor input) {
currentClass = owner;
sigma = all.get(0);
List<TargetConstructor> result = new ArrayList<>();
Set<List<MethodParameter>> parameterSet = new HashSet<>();
@ -311,6 +313,7 @@ public class ASTToTargetAST {
}
private List<TargetMethod> convert(ClassOrInterface owner, Method input) {
currentClass = owner;
sigma = all.get(0);
List<TargetMethod> result = new ArrayList<>();
Set<List<MethodParameter>> parameterSet = new HashSet<>();

View File

@ -127,13 +127,12 @@ public class StatementToTargetExpression implements StatementVisitor {
@Override
public void visit(MethodCall methodCall) {
var receiver = methodCall.receiver;
result = new TargetMethodCall(
converter.convert(methodCall.getType()),
methodCall.argTypes == null ? List.of() : methodCall.argTypes.stream().map(converter::convert).toList(),
converter.convert(receiver),
converter.convert(methodCall.receiver),
methodCall.getArgumentList().getArguments().stream().map(converter::convert).toList(),
converter.convert(receiver.getType()),
converter.convert(methodCall.receiver.getType()),
methodCall.name, false, false
);
}
@ -200,12 +199,13 @@ public class StatementToTargetExpression implements StatementVisitor {
@Override
public void visit(SuperCall superCall) {
var aSuper = converter.convert(converter.currentClass.getSuperClass());
result = new TargetMethodCall(
converter.convert(superCall.getType()),
superCall.argTypes == null ? List.of() : superCall.argTypes.stream().map(converter::convert).toList(),
new TargetSuper(null),
new TargetSuper(aSuper),
superCall.getArgumentList().getArguments().stream().map(converter::convert).toList(),
null,
aSuper,
superCall.name, false, false
);
}

View File

@ -11,5 +11,9 @@ public record TargetConstructor(int access, Set<TargetGeneric> generics, List<Me
public String getDescriptor() {
return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
}
public String getSignature() {
return TargetMethod.getSignature(generics, parameters, null);
}
}

View File

@ -4,10 +4,6 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes;
public record TargetField(int access, TargetType type, String name) {
public String getDescriptor() {
return type.toSignature();
}
public boolean isStatic() {
return (access & Opcodes.ACC_STATIC) != 0;
}

View File

@ -14,11 +14,23 @@ public record TargetMethod(int access, String name, Set<TargetGeneric> generics,
ret += parameterType.toSignature();
}
ret += ")";
if (returnType == null) {
ret += "V";
} else {
ret += returnType.toSignature();
if (returnType == null) ret += "V";
else ret += returnType.toSignature();
return ret;
}
public static String getSignature(Set<TargetGeneric> generics, List<MethodParameter> 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;
}
@ -26,6 +38,10 @@ public record TargetMethod(int access, String name, Set<TargetGeneric> generics,
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;
}

View File

@ -6,6 +6,11 @@ public record TargetExtendsWildcard(TargetType innerType) implements TargetType
return null;
}
@Override
public String toGenericSignature() {
return null;
}
@Override
public String getName() {
return null;

View File

@ -3,11 +3,16 @@ package de.dhbwstuttgart.target.tree.type;
public record TargetGenericType(String name) implements TargetType {
@Override
public String toSignature() {
return null;
return "Ljava/lang/Object;"; // TODO Use bounds for this?
}
@Override
public String toGenericSignature() {
return "T" + getName() + ";";
}
@Override
public String getName() {
return null;
return name;
}
}

View File

@ -4,4 +4,18 @@ import java.util.List;
public sealed interface TargetSpecializedType extends TargetType permits TargetFunNType, TargetRefType {
List<TargetType> params();
@Override
default String toGenericSignature() {
String ret = "L" + getName();
if (params().size() > 0) {
ret += "<";
for (var param : params()) {
ret += param.toGenericSignature();
}
ret += ">";
}
ret += ";";
return ret;
}
}

View File

@ -6,6 +6,11 @@ public record TargetSuperWildcard(TargetType innerType) implements TargetType {
return null;
}
@Override
public String toGenericSignature() {
return null;
}
@Override
public String getName() {
return null;

View File

@ -18,5 +18,6 @@ public sealed interface TargetType
TargetRefType Object = new TargetRefType("java.lang.Object");
String toSignature();
String toGenericSignature();
String getName();
}

View File

@ -59,6 +59,6 @@ public class ASTToTypedTargetAST {
var converter = new ASTToTargetAST(resultSet);
var classes = compiler.sourceFiles.get(file).getClasses();
converter.convert(classes.get(0));
var tphAndGenerics = TestCodegen.generateClass(converter.convert(classes.get(0)));
}
}