This commit is contained in:
Victorious3 2022-05-24 14:35:30 +02:00
parent 6c584f92e9
commit 20f11a5bef
19 changed files with 256 additions and 48 deletions

View File

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

View File

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

View File

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

View File

@ -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() {

View File

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

View File

@ -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 {}

View File

@ -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 {
} }

View File

@ -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 {
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
public interface Fun1$$<R, T> {
public R apply(T t);
}

View File

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

View 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);
}