Changes
This commit is contained in:
parent
6c584f92e9
commit
20f11a5bef
@ -1,17 +1,18 @@
|
||||
package de.dhbwstuttgart.target.bytecode;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.TargetClass;
|
||||
import de.dhbwstuttgart.target.tree.TargetConstructor;
|
||||
import de.dhbwstuttgart.target.tree.TargetField;
|
||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||
import de.dhbwstuttgart.target.tree.*;
|
||||
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.TargetType;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
@ -56,6 +57,7 @@ public class Codegen {
|
||||
private static class State {
|
||||
Scope scope = new Scope(null);
|
||||
int localCounter;
|
||||
int lambdaCounter;
|
||||
MethodVisitor mv;
|
||||
|
||||
State(MethodVisitor mv, int localCounter) {
|
||||
@ -216,7 +218,8 @@ public class Codegen {
|
||||
else if (dest.equals(TargetType.Double))
|
||||
mv.visitInsn(I2D);
|
||||
} 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) {
|
||||
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) {
|
||||
@ -542,7 +577,7 @@ public class Codegen {
|
||||
convertTo(state, cast.expr().type(), cast.type());
|
||||
break;
|
||||
case TargetInstanceOf instanceOf:
|
||||
mv.visitTypeInsn(INSTANCEOF, ((TargetRefType) instanceOf.right()).getName());
|
||||
mv.visitTypeInsn(INSTANCEOF, instanceOf.right().getName());
|
||||
break;
|
||||
case TargetLiteral literal:
|
||||
switch (literal) {
|
||||
@ -595,13 +630,12 @@ public class Codegen {
|
||||
}
|
||||
case TargetFieldVar dot: {
|
||||
generate(state, dot.left());
|
||||
TargetRefType type = (TargetRefType) dot.type();
|
||||
generate(state, assign.right());
|
||||
boxPrimitive(state, assign.right().type());
|
||||
if (dot.isStatic())
|
||||
mv.visitInsn(DUP);
|
||||
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;
|
||||
}
|
||||
default:
|
||||
@ -618,8 +652,7 @@ public class Codegen {
|
||||
case TargetFieldVar dot: {
|
||||
if (!dot.isStatic())
|
||||
generate(state, dot.left());
|
||||
var type = (TargetRefType) dot.left().type();
|
||||
mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, type.getName(), dot.right(), dot.type().toSignature());
|
||||
mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, dot.left().type().getName(), dot.right(), dot.type().toSignature());
|
||||
break;
|
||||
}
|
||||
case TargetFor _for: {
|
||||
@ -685,7 +718,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case TargetSuper _super: {
|
||||
// TODO
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
break;
|
||||
}
|
||||
case TargetMethodCall call: {
|
||||
@ -694,12 +727,29 @@ public class Codegen {
|
||||
generate(state, e);
|
||||
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;
|
||||
}
|
||||
case TargetLambdaExpression lambda:
|
||||
generateLambdaExpression(state, lambda);
|
||||
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:
|
||||
throw new CodeGenException("Unexpected value: " + expr);
|
||||
}
|
||||
@ -709,12 +759,26 @@ public class Codegen {
|
||||
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) {
|
||||
MethodVisitor mv = cw.visitMethod(constructor.access(), "<init>", constructor.getDescriptor(), null, 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);
|
||||
@ -736,20 +800,11 @@ public class Codegen {
|
||||
|
||||
public byte[] generate() {
|
||||
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.fields().forEach(this::generateField);
|
||||
if (clazz.constructors().size() == 0) {
|
||||
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.constructors().forEach(this::generateConstructor);
|
||||
clazz.methods().forEach(this::generateMethod);
|
||||
|
||||
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<>());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return qualifiedName.replaceAll("\\.", "/");
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ package de.dhbwstuttgart.target.tree;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
|
||||
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() {
|
||||
// TODO
|
||||
return null;
|
||||
return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
package de.dhbwstuttgart.target.tree;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
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() {
|
||||
// TODO
|
||||
return null;
|
||||
return type.toSignature();
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
|
@ -8,7 +8,7 @@ import org.objectweb.asm.Opcodes;
|
||||
import java.util.List;
|
||||
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) {
|
||||
String ret = "(";
|
||||
for (var parameterType : parameters) {
|
||||
|
@ -35,6 +35,7 @@ public sealed interface TargetBinaryOp extends TargetExpression {
|
||||
}
|
||||
|
||||
// Comparison
|
||||
// exprType is the type that both arguments get converted to before comparison
|
||||
record Equal(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 {}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package de.dhbwstuttgart.target.tree.expression;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.MethodParameter;
|
||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
|
||||
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;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record TargetExtendsWildcard(TargetType innerType) implements TargetType {
|
||||
@Override
|
||||
public String toSignature() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,14 @@ package de.dhbwstuttgart.target.tree.type;
|
||||
|
||||
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
|
||||
public String toSignature() {
|
||||
return "Fun$$" + N;
|
||||
return "L" + getName() + ";";
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,9 @@ public record TargetGenericType(String name) implements TargetType {
|
||||
public String toSignature() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.lang.String getName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,9 @@ public record TargetSuperWildcard(TargetType innerType) implements TargetType {
|
||||
public String toSignature() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -18,4 +18,5 @@ public sealed interface TargetType
|
||||
TargetRefType Object = new TargetRefType("java.lang.Object");
|
||||
|
||||
String toSignature();
|
||||
String getName();
|
||||
}
|
||||
|
@ -1,9 +1,16 @@
|
||||
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 loadClass(byte[] code) {
|
||||
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.tree.MethodParameter;
|
||||
import de.dhbwstuttgart.target.tree.TargetClass;
|
||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||
import de.dhbwstuttgart.target.tree.expression.*;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
@ -20,13 +19,15 @@ import java.util.List;
|
||||
|
||||
public class TestCodegen {
|
||||
|
||||
private static ByteArrayClassLoader loader = new ByteArrayClassLoader();
|
||||
|
||||
private static Class generateClass(TargetClass clazz) throws IOException {
|
||||
var codegen = new Codegen(clazz);
|
||||
var bytes = codegen.generate();
|
||||
var path = Path.of(System.getProperty("user.dir"), "src/test/resources/target/");
|
||||
Files.createDirectories(path);
|
||||
Files.write(path.resolve(clazz.qualifiedName() + ".class"), bytes, StandardOpenOption.CREATE);
|
||||
return new ByteArrayClassLoader().loadClass(bytes);
|
||||
return loader.loadClass(bytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -85,6 +86,34 @@ public class TestCodegen {
|
||||
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
|
||||
@Test
|
||||
public void testArithmeticConvert() throws Exception {
|
||||
@ -104,15 +133,16 @@ public class TestCodegen {
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null,
|
||||
new TargetBlock(List.of(new TargetMethodCall(null,
|
||||
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")),
|
||||
"out"
|
||||
),
|
||||
List.of(new TargetLiteral.StringLiteral("Hello World!")),
|
||||
new TargetRefType("java.io.PrintStream"),
|
||||
"println",
|
||||
TargetMethod.getDescriptor(null, TargetType.String),
|
||||
false
|
||||
false, false
|
||||
)))
|
||||
);
|
||||
|
||||
@ -190,4 +220,72 @@ public class TestCodegen {
|
||||
var clazz = generateClass(targetClass);
|
||||
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