forked from JavaTX/JavaCompilerCore
Changes
This commit is contained in:
parent
6c584f92e9
commit
20f11a5bef
@ -1,17 +1,18 @@
|
|||||||
package de.dhbwstuttgart.target.bytecode;
|
package de.dhbwstuttgart.target.bytecode;
|
||||||
|
|
||||||
import de.dhbwstuttgart.target.tree.TargetClass;
|
import de.dhbwstuttgart.target.tree.*;
|
||||||
import de.dhbwstuttgart.target.tree.TargetConstructor;
|
|
||||||
import de.dhbwstuttgart.target.tree.TargetField;
|
|
||||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
|
||||||
import de.dhbwstuttgart.target.tree.expression.*;
|
import de.dhbwstuttgart.target.tree.expression.*;
|
||||||
|
import de.dhbwstuttgart.target.tree.type.TargetFunNType;
|
||||||
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;
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.*;
|
||||||
import org.objectweb.asm.Label;
|
|
||||||
import org.objectweb.asm.MethodVisitor;
|
|
||||||
|
|
||||||
|
import java.lang.invoke.CallSite;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.objectweb.asm.Opcodes.*;
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
@ -56,6 +57,7 @@ public class Codegen {
|
|||||||
private static class State {
|
private static class State {
|
||||||
Scope scope = new Scope(null);
|
Scope scope = new Scope(null);
|
||||||
int localCounter;
|
int localCounter;
|
||||||
|
int lambdaCounter;
|
||||||
MethodVisitor mv;
|
MethodVisitor mv;
|
||||||
|
|
||||||
State(MethodVisitor mv, int localCounter) {
|
State(MethodVisitor mv, int localCounter) {
|
||||||
@ -216,7 +218,8 @@ public class Codegen {
|
|||||||
else if (dest.equals(TargetType.Double))
|
else if (dest.equals(TargetType.Double))
|
||||||
mv.visitInsn(I2D);
|
mv.visitInsn(I2D);
|
||||||
} else {
|
} else {
|
||||||
mv.visitTypeInsn(CHECKCAST, ((TargetRefType) dest).getName());
|
mv.visitTypeInsn(CHECKCAST, dest.getName());
|
||||||
|
unboxPrimitive(state, dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,6 +518,38 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateLambdaExpression(State state, TargetLambdaExpression lambda) {
|
private void generateLambdaExpression(State state, TargetLambdaExpression lambda) {
|
||||||
|
var mv = state.mv;
|
||||||
|
var name = "lambda$" + state.lambdaCounter;
|
||||||
|
var impl = new TargetMethod(
|
||||||
|
ACC_PRIVATE, new TargetRefType(clazz.qualifiedName()), name,
|
||||||
|
lambda.params(), lambda.returnType(), lambda.block()
|
||||||
|
);
|
||||||
|
generateMethod(impl);
|
||||||
|
|
||||||
|
var mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
|
||||||
|
MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
|
||||||
|
|
||||||
|
var bootstrap = new Handle(H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory",
|
||||||
|
mt.toMethodDescriptorString(), false);
|
||||||
|
var handle = new Handle(
|
||||||
|
H_INVOKEVIRTUAL, clazz.getName(), name,
|
||||||
|
impl.getDescriptor(), false
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO maybe make this a function?
|
||||||
|
var desugared = "(";
|
||||||
|
for (var param : lambda.params())
|
||||||
|
desugared += "Ljava/lang/Object;";
|
||||||
|
desugared += ")";
|
||||||
|
if (lambda.returnType() != null)
|
||||||
|
desugared += "Ljava/lang/Object;";
|
||||||
|
else desugared += "V";
|
||||||
|
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitInvokeDynamicInsn("apply", TargetMethod.getDescriptor(lambda.type(), new TargetRefType(clazz.qualifiedName())),
|
||||||
|
bootstrap, Type.getType(desugared), handle, Type.getType(impl.getDescriptor()));
|
||||||
|
|
||||||
|
state.lambdaCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generate(State state, TargetExpression expr) {
|
private void generate(State state, TargetExpression expr) {
|
||||||
@ -542,7 +577,7 @@ public class Codegen {
|
|||||||
convertTo(state, cast.expr().type(), cast.type());
|
convertTo(state, cast.expr().type(), cast.type());
|
||||||
break;
|
break;
|
||||||
case TargetInstanceOf instanceOf:
|
case TargetInstanceOf instanceOf:
|
||||||
mv.visitTypeInsn(INSTANCEOF, ((TargetRefType) instanceOf.right()).getName());
|
mv.visitTypeInsn(INSTANCEOF, instanceOf.right().getName());
|
||||||
break;
|
break;
|
||||||
case TargetLiteral literal:
|
case TargetLiteral literal:
|
||||||
switch (literal) {
|
switch (literal) {
|
||||||
@ -595,13 +630,12 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
case TargetFieldVar dot: {
|
case TargetFieldVar dot: {
|
||||||
generate(state, dot.left());
|
generate(state, dot.left());
|
||||||
TargetRefType type = (TargetRefType) dot.type();
|
|
||||||
generate(state, assign.right());
|
generate(state, assign.right());
|
||||||
boxPrimitive(state, assign.right().type());
|
boxPrimitive(state, assign.right().type());
|
||||||
if (dot.isStatic())
|
if (dot.isStatic())
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DUP);
|
||||||
else mv.visitInsn(DUP_X1);
|
else mv.visitInsn(DUP_X1);
|
||||||
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.right(), type.getName(), type.toSignature());
|
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.owner().getName(), dot.right(), dot.type().toSignature());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -618,8 +652,7 @@ public class Codegen {
|
|||||||
case TargetFieldVar dot: {
|
case TargetFieldVar dot: {
|
||||||
if (!dot.isStatic())
|
if (!dot.isStatic())
|
||||||
generate(state, dot.left());
|
generate(state, dot.left());
|
||||||
var type = (TargetRefType) dot.left().type();
|
mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, dot.left().type().getName(), dot.right(), dot.type().toSignature());
|
||||||
mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, type.getName(), dot.right(), dot.type().toSignature());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetFor _for: {
|
case TargetFor _for: {
|
||||||
@ -685,7 +718,7 @@ public class Codegen {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetSuper _super: {
|
case TargetSuper _super: {
|
||||||
// TODO
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetMethodCall call: {
|
case TargetMethodCall call: {
|
||||||
@ -694,12 +727,29 @@ public class Codegen {
|
|||||||
generate(state, e);
|
generate(state, e);
|
||||||
boxPrimitive(state, e.type());
|
boxPrimitive(state, e.type());
|
||||||
}
|
}
|
||||||
mv.visitMethodInsn(call.isStatic() ? INVOKESTATIC: INVOKEVIRTUAL, call.owner().getName(), call.name(), call.descriptor(), false);
|
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,
|
||||||
|
call.owner().getName(), call.name(), call.getDescriptor(), call.isInterface());
|
||||||
|
if (call.type() != null)
|
||||||
|
unboxPrimitive(state, call.type());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetLambdaExpression lambda:
|
case TargetLambdaExpression lambda:
|
||||||
generateLambdaExpression(state, lambda);
|
generateLambdaExpression(state, lambda);
|
||||||
break;
|
break;
|
||||||
|
case TargetNew _new: {
|
||||||
|
mv.visitTypeInsn(NEW, _new.type().getName());
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
for (TargetExpression e : _new.params()) {
|
||||||
|
generate(state, e);
|
||||||
|
boxPrimitive(state, e.type());
|
||||||
|
}
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, _new.type().getName(), "<init>", _new.getDescriptor(), false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new CodeGenException("Unexpected value: " + expr);
|
throw new CodeGenException("Unexpected value: " + expr);
|
||||||
}
|
}
|
||||||
@ -709,12 +759,26 @@ public class Codegen {
|
|||||||
cw.visitField(field.access(), field.name(), field.getDescriptor(), null, null);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
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(), null, 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);
|
||||||
@ -736,20 +800,11 @@ public class Codegen {
|
|||||||
|
|
||||||
public byte[] generate() {
|
public byte[] generate() {
|
||||||
cw.visit(V1_8, clazz.modifiers(), clazz.qualifiedName(),
|
cw.visit(V1_8, clazz.modifiers(), clazz.qualifiedName(),
|
||||||
null, ((TargetRefType) clazz.superType()).getName(),
|
null, clazz.superType().getName(),
|
||||||
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);
|
||||||
if (clazz.constructors().size() == 0) {
|
clazz.constructors().forEach(this::generateConstructor);
|
||||||
var mv = cw.visitMethod(ACC_PROTECTED, "<init>", "()V", null, null);
|
|
||||||
mv.visitCode();
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, ((TargetRefType) clazz.superType()).getName(), "<init>", "()V", false);
|
|
||||||
mv.visitInsn(RETURN);
|
|
||||||
mv.visitMaxs(0, 0);
|
|
||||||
mv.visitEnd();
|
|
||||||
}
|
|
||||||
else clazz.constructors().forEach(this::generateConstructor);
|
|
||||||
clazz.methods().forEach(this::generateMethod);
|
clazz.methods().forEach(this::generateMethod);
|
||||||
|
|
||||||
cw.visitEnd();
|
cw.visitEnd();
|
||||||
|
@ -17,6 +17,10 @@ public record TargetClass(int modifiers, String qualifiedName, TargetType superT
|
|||||||
this(modifiers, qualifiedName, TargetType.Object, implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
this(modifiers, qualifiedName, TargetType.Object, implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return qualifiedName.replaceAll("\\.", "/");
|
||||||
|
}
|
||||||
|
|
||||||
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetType returnType, TargetBlock block) {
|
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetType returnType, TargetBlock block) {
|
||||||
this.methods.add(new TargetMethod(access, new TargetRefType(this.qualifiedName, List.of()), name, parameterTypes, returnType, block));
|
this.methods.add(new TargetMethod(access, new TargetRefType(this.qualifiedName, List.of()), name, parameterTypes, returnType, block));
|
||||||
}
|
}
|
||||||
@ -25,7 +29,7 @@ public record TargetClass(int modifiers, String qualifiedName, TargetType superT
|
|||||||
this.constructors.add(new TargetConstructor(access, new TargetRefType(this.qualifiedName, List.of()), paramterTypes, block));
|
this.constructors.add(new TargetConstructor(access, new TargetRefType(this.qualifiedName, List.of()), paramterTypes, block));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addField(int access, TargetType type, String name) {
|
public void addField(int access, TargetRefType type, String name) {
|
||||||
this.fields.add(new TargetField(access, new TargetRefType(this.qualifiedName, List.of()), type, name));
|
this.fields.add(new TargetField(access, new TargetRefType(this.qualifiedName, List.of()), type, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,14 @@ package de.dhbwstuttgart.target.tree;
|
|||||||
|
|
||||||
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
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 java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetConstructor(int access, TargetRefType owner, List<MethodParameter> parameters, TargetBlock block) {
|
public record TargetConstructor(int access, TargetType owner, List<MethodParameter> parameters, TargetBlock block) {
|
||||||
|
|
||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
// TODO
|
return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package de.dhbwstuttgart.target.tree;
|
package de.dhbwstuttgart.target.tree;
|
||||||
|
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
public record TargetField(int access, TargetRefType owner, TargetType type, String name) {
|
public record TargetField(int access, TargetType owner, TargetType type, String name) {
|
||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
// TODO
|
return type.toSignature();
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
|
@ -8,7 +8,7 @@ import org.objectweb.asm.Opcodes;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public record TargetMethod(int access, TargetRefType owner, String name, List<MethodParameter> parameters, TargetType returnType, TargetBlock block) {
|
public record TargetMethod(int access, TargetType owner, String name, List<MethodParameter> parameters, TargetType returnType, TargetBlock block) {
|
||||||
public static String getDescriptor(TargetType returnType, TargetType... parameters) {
|
public static String getDescriptor(TargetType returnType, TargetType... parameters) {
|
||||||
String ret = "(";
|
String ret = "(";
|
||||||
for (var parameterType : parameters) {
|
for (var parameterType : parameters) {
|
||||||
|
@ -35,6 +35,7 @@ public sealed interface TargetBinaryOp extends TargetExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
|
// exprType is the type that both arguments get converted to before comparison
|
||||||
record Equal(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
record Equal(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||||
record Greater(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
record Greater(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||||
record GreaterOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
record GreaterOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.dhbwstuttgart.target.tree.expression;
|
package de.dhbwstuttgart.target.tree.expression;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
|
|
||||||
public record TargetFieldVar(TargetType type, boolean isStatic, TargetExpression left, String right) implements TargetExpression {
|
public record TargetFieldVar(TargetType type, TargetRefType owner, boolean isStatic, TargetExpression left, String right) implements TargetExpression {
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,5 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetLambdaExpression(TargetType type, List<MethodParameter> params, TargetExpression block) implements TargetExpression {
|
public record TargetLambdaExpression(TargetType type, List<MethodParameter> params, TargetType returnType, TargetBlock block) implements TargetExpression {
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
package de.dhbwstuttgart.target.tree.expression;
|
package de.dhbwstuttgart.target.tree.expression;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.target.tree.MethodParameter;
|
||||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||||
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;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetMethodCall(TargetType type, TargetExpression expr, List<TargetExpression> args, TargetRefType owner, String name, String descriptor, boolean isStatic) implements TargetStatementExpression {
|
public record TargetMethodCall(TargetType type, List<TargetType> parameterTypes, TargetExpression expr, List<TargetExpression> args, TargetType owner, String name, boolean isStatic, boolean isInterface) implements TargetStatementExpression {
|
||||||
|
public TargetMethodCall(TargetType type, TargetExpression expr, List<TargetExpression> args, TargetType owner, String name, boolean isStatic, boolean isInterface) {
|
||||||
|
this(type, args.stream().map(TargetExpression::type).toList(), expr, args, owner, name, isStatic, isInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getDescriptor() {
|
||||||
|
return TargetMethod.getDescriptor(type, parameterTypes.toArray(TargetType[]::new));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package de.dhbwstuttgart.target.tree.expression;
|
package de.dhbwstuttgart.target.tree.expression;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetNew(TargetType type, List<TargetExpression> params) implements TargetStatementExpression {
|
public record TargetNew(TargetType type, List<TargetExpression> params) implements TargetStatementExpression {
|
||||||
|
public String getDescriptor() {
|
||||||
|
return TargetMethod.getDescriptor(null, params.stream().map(TargetExpression::type).toArray(TargetType[]::new));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package de.dhbwstuttgart.target.tree.type;
|
package de.dhbwstuttgart.target.tree.type;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public record TargetExtendsWildcard(TargetType innerType) implements TargetType {
|
public record TargetExtendsWildcard(TargetType innerType) implements TargetType {
|
||||||
@Override
|
@Override
|
||||||
public String toSignature() {
|
public String toSignature() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,14 @@ package de.dhbwstuttgart.target.tree.type;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetFunNType(int N, List<TargetRefType> params) implements TargetType {
|
public record TargetFunNType(int N, List<TargetType> params) implements TargetType {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Fun" + N + "$$";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toSignature() {
|
public String toSignature() {
|
||||||
return "Fun$$" + N;
|
return "L" + getName() + ";";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,9 @@ public record TargetGenericType(String name) implements TargetType {
|
|||||||
public String toSignature() {
|
public String toSignature() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.lang.String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,9 @@ public record TargetSuperWildcard(TargetType innerType) implements TargetType {
|
|||||||
public String toSignature() {
|
public String toSignature() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,5 @@ public sealed interface TargetType
|
|||||||
TargetRefType Object = new TargetRefType("java.lang.Object");
|
TargetRefType Object = new TargetRefType("java.lang.Object");
|
||||||
|
|
||||||
String toSignature();
|
String toSignature();
|
||||||
|
String getName();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package targetast;
|
package targetast;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class ByteArrayClassLoader extends ClassLoader {
|
public class ByteArrayClassLoader extends ClassLoader {
|
||||||
public Class loadClass(byte[] code) {
|
public Class loadClass(byte[] code) {
|
||||||
return this.defineClass(null, code, 0, code.length);
|
return this.defineClass(null, code, 0, code.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class loadClass(Path path) throws IOException {
|
||||||
|
var code = Files.readAllBytes(path);
|
||||||
|
return this.defineClass(null, code, 0, code.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
3
src/test/java/targetast/Fun1$$.java
Normal file
3
src/test/java/targetast/Fun1$$.java
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
public interface Fun1$$<R, T> {
|
||||||
|
public R apply(T t);
|
||||||
|
}
|
@ -3,7 +3,6 @@ package targetast;
|
|||||||
import de.dhbwstuttgart.target.bytecode.Codegen;
|
import de.dhbwstuttgart.target.bytecode.Codegen;
|
||||||
import de.dhbwstuttgart.target.tree.MethodParameter;
|
import de.dhbwstuttgart.target.tree.MethodParameter;
|
||||||
import de.dhbwstuttgart.target.tree.TargetClass;
|
import de.dhbwstuttgart.target.tree.TargetClass;
|
||||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
|
||||||
import de.dhbwstuttgart.target.tree.expression.*;
|
import de.dhbwstuttgart.target.tree.expression.*;
|
||||||
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;
|
||||||
@ -20,13 +19,15 @@ import java.util.List;
|
|||||||
|
|
||||||
public class TestCodegen {
|
public class TestCodegen {
|
||||||
|
|
||||||
|
private static ByteArrayClassLoader loader = new ByteArrayClassLoader();
|
||||||
|
|
||||||
private static Class generateClass(TargetClass clazz) throws IOException {
|
private static Class generateClass(TargetClass clazz) throws IOException {
|
||||||
var codegen = new Codegen(clazz);
|
var codegen = new Codegen(clazz);
|
||||||
var bytes = codegen.generate();
|
var bytes = codegen.generate();
|
||||||
var path = Path.of(System.getProperty("user.dir"), "src/test/resources/target/");
|
var path = Path.of(System.getProperty("user.dir"), "src/test/resources/target/");
|
||||||
Files.createDirectories(path);
|
Files.createDirectories(path);
|
||||||
Files.write(path.resolve(clazz.qualifiedName() + ".class"), bytes, StandardOpenOption.CREATE);
|
Files.write(path.resolve(clazz.qualifiedName() + ".class"), bytes, StandardOpenOption.CREATE);
|
||||||
return new ByteArrayClassLoader().loadClass(bytes);
|
return loader.loadClass(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -85,6 +86,34 @@ public class TestCodegen {
|
|||||||
assertEquals(clazz.getDeclaredMethod("rem", Integer.class, Integer.class).invoke(null, 10, 3), 1);
|
assertEquals(clazz.getDeclaredMethod("rem", Integer.class, Integer.class).invoke(null, 10, 3), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConditional() throws Exception {
|
||||||
|
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Conditional");
|
||||||
|
|
||||||
|
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "and",
|
||||||
|
List.of(new MethodParameter(TargetType.Boolean, "a"), new MethodParameter(TargetType.Boolean, "b")),
|
||||||
|
TargetType.Boolean,
|
||||||
|
new TargetBlock(List.of(new TargetReturn(
|
||||||
|
new TargetBinaryOp.And(TargetType.Boolean, new TargetLocalVar(TargetType.Boolean, "a"), new TargetLocalVar(TargetType.Boolean, "b")))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "or",
|
||||||
|
List.of(new MethodParameter(TargetType.Boolean, "a"), new MethodParameter(TargetType.Boolean, "b")),
|
||||||
|
TargetType.Boolean,
|
||||||
|
new TargetBlock(List.of(new TargetReturn(
|
||||||
|
new TargetBinaryOp.Or(TargetType.Boolean, new TargetLocalVar(TargetType.Boolean, "a"), new TargetLocalVar(TargetType.Boolean, "b")))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
var clazz = generateClass(targetClass);
|
||||||
|
var and = clazz.getDeclaredMethod("and", Boolean.class, Boolean.class);
|
||||||
|
var or = clazz.getDeclaredMethod("or", Boolean.class, Boolean.class);
|
||||||
|
assertEquals(and.invoke(null, true, false), false);
|
||||||
|
assertEquals(and.invoke(null, true, true), true);
|
||||||
|
assertEquals(or.invoke(null, false, false), false);
|
||||||
|
assertEquals(or.invoke(null, true, false), true);
|
||||||
|
}
|
||||||
|
|
||||||
// When adding two numbers and the return type is Long it needs to convert both values to Long
|
// When adding two numbers and the return type is Long it needs to convert both values to Long
|
||||||
@Test
|
@Test
|
||||||
public void testArithmeticConvert() throws Exception {
|
public void testArithmeticConvert() throws Exception {
|
||||||
@ -104,15 +133,16 @@ public class TestCodegen {
|
|||||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null,
|
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null,
|
||||||
new TargetBlock(List.of(new TargetMethodCall(null,
|
new TargetBlock(List.of(new TargetMethodCall(null,
|
||||||
new TargetFieldVar(
|
new TargetFieldVar(
|
||||||
new TargetRefType("java.io.PrintStream"), true,
|
new TargetRefType("java.io.PrintStream"),
|
||||||
|
new TargetRefType("java.lang.System"),
|
||||||
|
true,
|
||||||
new TargetClassName(new TargetRefType("java.lang.System")),
|
new TargetClassName(new TargetRefType("java.lang.System")),
|
||||||
"out"
|
"out"
|
||||||
),
|
),
|
||||||
List.of(new TargetLiteral.StringLiteral("Hello World!")),
|
List.of(new TargetLiteral.StringLiteral("Hello World!")),
|
||||||
new TargetRefType("java.io.PrintStream"),
|
new TargetRefType("java.io.PrintStream"),
|
||||||
"println",
|
"println",
|
||||||
TargetMethod.getDescriptor(null, TargetType.String),
|
false, false
|
||||||
false
|
|
||||||
)))
|
)))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -190,4 +220,72 @@ public class TestCodegen {
|
|||||||
var clazz = generateClass(targetClass);
|
var clazz = generateClass(targetClass);
|
||||||
assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10);
|
assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNew() throws Exception {
|
||||||
|
var pointType = new TargetRefType("Point");
|
||||||
|
var pointTarget = new TargetClass(Opcodes.ACC_PUBLIC, "Point");
|
||||||
|
pointTarget.addField(Opcodes.ACC_PUBLIC, TargetType.Integer, "x");
|
||||||
|
pointTarget.addField(Opcodes.ACC_PUBLIC, TargetType.Integer, "y");
|
||||||
|
pointTarget.addConstructor(Opcodes.ACC_PUBLIC,
|
||||||
|
List.of(new MethodParameter(TargetType.Integer, "x"), new MethodParameter(TargetType.Integer, "y")),
|
||||||
|
new TargetBlock(List.of(
|
||||||
|
new TargetAssign(TargetType.Integer,
|
||||||
|
new TargetFieldVar(TargetType.Integer, pointType, false, new TargetThis(pointType), "x"),
|
||||||
|
new TargetLocalVar(TargetType.Integer, "x")
|
||||||
|
),
|
||||||
|
new TargetAssign(TargetType.Integer,
|
||||||
|
new TargetFieldVar(TargetType.Integer, pointType, false, new TargetThis(pointType), "y"),
|
||||||
|
new TargetLocalVar(TargetType.Integer, "y")
|
||||||
|
)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
var mainTarget = new TargetClass(Opcodes.ACC_PUBLIC, "New");
|
||||||
|
mainTarget.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "makePoint",
|
||||||
|
List.of(new MethodParameter(TargetType.Integer, "x"), new MethodParameter(TargetType.Integer, "y")), pointType,
|
||||||
|
new TargetBlock(List.of(
|
||||||
|
new TargetReturn(new TargetNew(pointType, List.of(
|
||||||
|
new TargetLocalVar(TargetType.Integer, "x"),
|
||||||
|
new TargetLocalVar(TargetType.Integer, "y")
|
||||||
|
)))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
var pointClass = generateClass(pointTarget);
|
||||||
|
var mainClass = generateClass(mainTarget);
|
||||||
|
|
||||||
|
var point = mainClass.getDeclaredMethod("makePoint", Integer.class, Integer.class).invoke(null, 10, 20);
|
||||||
|
assertEquals(point.getClass().getDeclaredField("x").get(point), 10);
|
||||||
|
assertEquals(point.getClass().getDeclaredField("y").get(point), 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLambda() throws Exception {
|
||||||
|
var fun = loader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class"));
|
||||||
|
var interfaceType = new TargetRefType("Fun1$$");
|
||||||
|
|
||||||
|
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Lambda");
|
||||||
|
targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of()));
|
||||||
|
targetClass.addMethod(Opcodes.ACC_PUBLIC, "lambda", List.of(), TargetType.Integer,
|
||||||
|
new TargetBlock(List.of(
|
||||||
|
new TargetVarDecl(interfaceType, "by2",
|
||||||
|
new TargetLambdaExpression(interfaceType, List.of(new MethodParameter(TargetType.Integer, "num")), TargetType.Integer,
|
||||||
|
new TargetBlock(List.of(
|
||||||
|
new TargetReturn(new TargetBinaryOp.Mul(TargetType.Integer,
|
||||||
|
new TargetLocalVar(TargetType.Integer, "num"),
|
||||||
|
new TargetLiteral.IntLiteral(2)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
),
|
||||||
|
new TargetReturn(new TargetCast(TargetType.Integer, new TargetMethodCall(TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of(
|
||||||
|
new TargetLiteral.IntLiteral(10)
|
||||||
|
), interfaceType, "apply", false, true)))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
var clazz = generateClass(targetClass);
|
||||||
|
var instance = clazz.getConstructor().newInstance();
|
||||||
|
assertEquals(clazz.getDeclaredMethod("lambda").invoke(instance), 20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
src/test/resources/target/Test.java
Normal file
9
src/test/resources/target/Test.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
public class Test {
|
||||||
|
public void lambda() {
|
||||||
|
Interface<Integer, Integer> mul2 = (Integer a) -> a * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Interface<R, T> {
|
||||||
|
R apply(T t);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user