forked from JavaTX/JavaCompilerCore
Add generics to the generated class file
This commit is contained in:
parent
7e259e2597
commit
a8be387dd9
@ -617,9 +617,11 @@ public class Codegen {
|
|||||||
break;
|
break;
|
||||||
case TargetVarDecl varDecl: {
|
case TargetVarDecl varDecl: {
|
||||||
var local = state.createVariable(varDecl.name(), varDecl.varType());
|
var local = state.createVariable(varDecl.name(), varDecl.varType());
|
||||||
generate(state, varDecl.value());
|
if (varDecl.value() != null) {
|
||||||
boxPrimitive(state, varDecl.varType());
|
generate(state, varDecl.value());
|
||||||
mv.visitVarInsn(ASTORE, local.index());
|
boxPrimitive(state, varDecl.varType());
|
||||||
|
mv.visitVarInsn(ASTORE, local.index());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetBinaryOp op:
|
case TargetBinaryOp op:
|
||||||
@ -737,12 +739,8 @@ public class Codegen {
|
|||||||
generate(state, e);
|
generate(state, e);
|
||||||
boxPrimitive(state, e.type());
|
boxPrimitive(state, e.type());
|
||||||
}
|
}
|
||||||
if (call.expr() instanceof TargetThis)
|
mv.visitMethodInsn(call.isInterface() ? INVOKEINTERFACE : call.isStatic() ? INVOKESTATIC: call.name() == "<init>" ? INVOKESPECIAL : INVOKEVIRTUAL,
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, clazz.getName(), "<init>", call.getDescriptor(), false);
|
call.owner().getName(), call.name(), call.getDescriptor(), call.isInterface());
|
||||||
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,
|
|
||||||
call.owner().getName(), call.name(), call.getDescriptor(), call.isInterface());
|
|
||||||
if (call.type() != null)
|
if (call.type() != null)
|
||||||
unboxPrimitive(state, call.type());
|
unboxPrimitive(state, call.type());
|
||||||
break;
|
break;
|
||||||
@ -766,29 +764,15 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateField(TargetField field) {
|
private void generateField(TargetField field) {
|
||||||
cw.visitField(field.access(), field.name(), field.getDescriptor(), null, null);
|
cw.visitField(field.access(), field.name(), field.type().toSignature(), field.type().toGenericSignature(), 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateConstructor(TargetConstructor constructor) {
|
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();
|
mv.visitCode();
|
||||||
var state = new State(mv, 1);
|
var state = new State(mv, 1);
|
||||||
for (var param: constructor.parameters())
|
for (var param: constructor.parameters())
|
||||||
state.createVariable(param.name(), param.type());
|
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());
|
generate(state, constructor.block());
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
mv.visitMaxs(0, 0);
|
mv.visitMaxs(0, 0);
|
||||||
@ -796,7 +780,7 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateMethod(TargetMethod method) {
|
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();
|
mv.visitCode();
|
||||||
var state = new State(mv, method.isStatic() ? 0 : 1);
|
var state = new State(mv, method.isStatic() ? 0 : 1);
|
||||||
for (var param: method.parameters())
|
for (var param: method.parameters())
|
||||||
@ -808,9 +792,20 @@ public class Codegen {
|
|||||||
mv.visitEnd();
|
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() {
|
public byte[] generate() {
|
||||||
cw.visit(V1_8, clazz.modifiers(), clazz.qualifiedName(),
|
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.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)
|
||||||
);
|
);
|
||||||
clazz.fields().forEach(this::generateField);
|
clazz.fields().forEach(this::generateField);
|
||||||
|
@ -15,6 +15,7 @@ public class ASTToTargetAST {
|
|||||||
|
|
||||||
protected List<Sigma> all;
|
protected List<Sigma> all;
|
||||||
protected Sigma sigma;
|
protected Sigma sigma;
|
||||||
|
protected ClassOrInterface currentClass; // TODO This is only needed because of SuperCall, maybe there's a better way?
|
||||||
|
|
||||||
private class Sigma {
|
private class Sigma {
|
||||||
Map<Method, HashSet<TargetGeneric>> computedGenericsOfMethods = new HashMap<>();
|
Map<Method, HashSet<TargetGeneric>> computedGenericsOfMethods = new HashMap<>();
|
||||||
@ -294,6 +295,7 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<TargetConstructor> convert(ClassOrInterface owner, Constructor input) {
|
private List<TargetConstructor> convert(ClassOrInterface owner, Constructor input) {
|
||||||
|
currentClass = owner;
|
||||||
sigma = all.get(0);
|
sigma = all.get(0);
|
||||||
List<TargetConstructor> result = new ArrayList<>();
|
List<TargetConstructor> result = new ArrayList<>();
|
||||||
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
||||||
@ -311,6 +313,7 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<TargetMethod> convert(ClassOrInterface owner, Method input) {
|
private List<TargetMethod> convert(ClassOrInterface owner, Method input) {
|
||||||
|
currentClass = owner;
|
||||||
sigma = all.get(0);
|
sigma = all.get(0);
|
||||||
List<TargetMethod> result = new ArrayList<>();
|
List<TargetMethod> result = new ArrayList<>();
|
||||||
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
||||||
|
@ -127,13 +127,12 @@ public class StatementToTargetExpression implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodCall methodCall) {
|
public void visit(MethodCall methodCall) {
|
||||||
var receiver = methodCall.receiver;
|
|
||||||
result = new TargetMethodCall(
|
result = new TargetMethodCall(
|
||||||
converter.convert(methodCall.getType()),
|
converter.convert(methodCall.getType()),
|
||||||
methodCall.argTypes == null ? List.of() : methodCall.argTypes.stream().map(converter::convert).toList(),
|
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(),
|
methodCall.getArgumentList().getArguments().stream().map(converter::convert).toList(),
|
||||||
converter.convert(receiver.getType()),
|
converter.convert(methodCall.receiver.getType()),
|
||||||
methodCall.name, false, false
|
methodCall.name, false, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -200,12 +199,13 @@ public class StatementToTargetExpression implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(SuperCall superCall) {
|
public void visit(SuperCall superCall) {
|
||||||
|
var aSuper = converter.convert(converter.currentClass.getSuperClass());
|
||||||
result = new TargetMethodCall(
|
result = new TargetMethodCall(
|
||||||
converter.convert(superCall.getType()),
|
converter.convert(superCall.getType()),
|
||||||
superCall.argTypes == null ? List.of() : superCall.argTypes.stream().map(converter::convert).toList(),
|
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(),
|
superCall.getArgumentList().getArguments().stream().map(converter::convert).toList(),
|
||||||
null,
|
aSuper,
|
||||||
superCall.name, false, false
|
superCall.name, false, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,9 @@ public record TargetConstructor(int access, Set<TargetGeneric> generics, List<Me
|
|||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
|
return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return TargetMethod.getSignature(generics, parameters, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,6 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
|||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
public record TargetField(int access, TargetType type, String name) {
|
public record TargetField(int access, TargetType type, String name) {
|
||||||
public String getDescriptor() {
|
|
||||||
return type.toSignature();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
return (access & Opcodes.ACC_STATIC) != 0;
|
return (access & Opcodes.ACC_STATIC) != 0;
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,23 @@ public record TargetMethod(int access, String name, Set<TargetGeneric> generics,
|
|||||||
ret += parameterType.toSignature();
|
ret += parameterType.toSignature();
|
||||||
}
|
}
|
||||||
ret += ")";
|
ret += ")";
|
||||||
if (returnType == null) {
|
if (returnType == null) ret += "V";
|
||||||
ret += "V";
|
else ret += returnType.toSignature();
|
||||||
} else {
|
return ret;
|
||||||
ret += returnType.toSignature();
|
}
|
||||||
|
|
||||||
|
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;
|
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));
|
return getDescriptor(returnType, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSignature() {
|
||||||
|
return getSignature(generics, parameters, returnType);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
return (access & Opcodes.ACC_STATIC) != 0;
|
return (access & Opcodes.ACC_STATIC) != 0;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,11 @@ public record TargetExtendsWildcard(TargetType innerType) implements TargetType
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toGenericSignature() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -3,11 +3,16 @@ package de.dhbwstuttgart.target.tree.type;
|
|||||||
public record TargetGenericType(String name) implements TargetType {
|
public record TargetGenericType(String name) implements TargetType {
|
||||||
@Override
|
@Override
|
||||||
public String toSignature() {
|
public String toSignature() {
|
||||||
return null;
|
return "Ljava/lang/Object;"; // TODO Use bounds for this?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toGenericSignature() {
|
||||||
|
return "T" + getName() + ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return null;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,18 @@ import java.util.List;
|
|||||||
|
|
||||||
public sealed interface TargetSpecializedType extends TargetType permits TargetFunNType, TargetRefType {
|
public sealed interface TargetSpecializedType extends TargetType permits TargetFunNType, TargetRefType {
|
||||||
List<TargetType> params();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,11 @@ public record TargetSuperWildcard(TargetType innerType) implements TargetType {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toGenericSignature() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -18,5 +18,6 @@ public sealed interface TargetType
|
|||||||
TargetRefType Object = new TargetRefType("java.lang.Object");
|
TargetRefType Object = new TargetRefType("java.lang.Object");
|
||||||
|
|
||||||
String toSignature();
|
String toSignature();
|
||||||
|
String toGenericSignature();
|
||||||
String getName();
|
String getName();
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,6 @@ public class ASTToTypedTargetAST {
|
|||||||
var converter = new ASTToTargetAST(resultSet);
|
var converter = new ASTToTargetAST(resultSet);
|
||||||
var classes = compiler.sourceFiles.get(file).getClasses();
|
var classes = compiler.sourceFiles.get(file).getClasses();
|
||||||
|
|
||||||
converter.convert(classes.get(0));
|
var tphAndGenerics = TestCodegen.generateClass(converter.convert(classes.get(0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user