Find functions in class files and primitive types

This commit is contained in:
Victorious3 2022-08-02 18:10:52 +02:00
parent 6e1786ec7c
commit c21104f646
24 changed files with 174 additions and 50 deletions

View File

@ -1,4 +1,4 @@
package targetast;
package de.dhbwstuttgart.target;
import java.io.IOException;
import java.nio.file.Files;

View File

@ -3,6 +3,7 @@ package de.dhbwstuttgart.target.bytecode;
import de.dhbwstuttgart.target.tree.*;
import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.TargetFunNType;
import de.dhbwstuttgart.target.tree.type.TargetPrimitiveType;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.*;
@ -13,6 +14,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.IntStream;
@ -86,21 +88,21 @@ public class Codegen {
private void boxPrimitive(State state, TargetType type) {
var mv = state.mv;
if (type.equals(TargetType.Boolean)) {
if (type.equals(TargetType.Boolean) || type.equals(TargetType.boolean_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
} else if (type.equals(TargetType.Byte)) {
} else if (type.equals(TargetType.Byte) || type.equals(TargetType.byte_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
} else if (type.equals(TargetType.Double)) {
} else if (type.equals(TargetType.Double) || type.equals(TargetType.double_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
} else if (type.equals(TargetType.Long)) {
} else if (type.equals(TargetType.Long) || type.equals(TargetType.long_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
} else if (type.equals(TargetType.Integer)) {
} else if (type.equals(TargetType.Integer) || type.equals(TargetType.int_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
} else if (type.equals(TargetType.Float)) {
} else if (type.equals(TargetType.Float) || type.equals(TargetType.float_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
} else if (type.equals(TargetType.Short)) {
} else if (type.equals(TargetType.Short) || type.equals(TargetType.short_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
} else if (type.equals(TargetType.Char)) {
} else if (type.equals(TargetType.Char) || type.equals(TargetType.char_)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Char", "valueOf", "(C)Ljava/lang/Char;", false);
}
}
@ -909,23 +911,28 @@ public class Codegen {
}
case TargetMethodCall call: {
generate(state, call.expr());
for (TargetExpression e : call.args()) {
for (var i = 0; i < call.args().size(); i++) {
var e = call.args().get(i);
var arg = call.parameterTypes().get(i);
generate(state, e);
boxPrimitive(state, e.type());
if (!(arg instanceof TargetPrimitiveType))
boxPrimitive(state, e.type());
}
var descriptor = call.getDescriptor();
if (call.owner() instanceof TargetFunNType) // Decay FunN
descriptor = TargetMethod.getDescriptor(
call.type() == null ? null : TargetType.Object,
call.returnType() == null ? null : TargetType.Object,
call.parameterTypes().stream().map(x -> TargetType.Object).toArray(TargetType[]::new)
);
mv.visitMethodInsn(call.isInterface() ? INVOKEINTERFACE : call.isStatic() ? INVOKESTATIC: call.name().equals("<init>") ? INVOKESPECIAL : INVOKEVIRTUAL,
call.owner().getName(), call.name(), descriptor, call.isInterface());
if (call.owner() instanceof TargetFunNType)
mv.visitTypeInsn(CHECKCAST, call.type().getName());
if (call.type() != null)
unboxPrimitive(state, call.type());
if (call.returnType() != null && !(call.returnType() instanceof TargetPrimitiveType)) {
if (!call.returnType().equals(call.type()))
mv.visitTypeInsn(CHECKCAST, call.type().getName());
else unboxPrimitive(state, call.type());
}
break;
}
case TargetLambdaExpression lambda:

View File

@ -11,6 +11,7 @@ import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.expression.TargetExpression;
import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import java.util.*;
import java.util.stream.Collectors;
@ -142,8 +143,9 @@ public class ASTToTargetAST {
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
if (expressionReceiver.expr instanceof This) {
// TODO This is going to fail spectacularly for overloaded methods
var optMethod = owner.getMethods().stream().filter(m -> m.name.equals(methodCall.name)).findFirst();
var optMethod = owner.getMethods().stream().filter(
m -> m.name.equals(methodCall.name) && parameterEquals(methodCall.getArgumentList(), methodCall.getArgumentList().getArguments())
).findFirst();
if (optMethod.isEmpty()) return;
var method = optMethod.get();
var generics = generics(owner, method);
@ -411,7 +413,30 @@ public class ASTToTargetAST {
}
}
private boolean parameterEquals(ArgumentList argumentList, List<Expression> arguments) {
if (argumentList.getArguments().size() != arguments.size())
return false;
for (var i = 0; i < argumentList.getArguments().size(); i++) {
var type1 = convert(argumentList.getArguments().get(i).getType());
var type2 = convert(arguments.get(i).getType());
if (!type1.equals(type2)) return false;
}
return true;
}
protected ByteArrayClassLoader classLoader;
protected SourceFile sourceFile;
public ASTToTargetAST(List<ResultSet> resultSets) {
this(resultSets, null, new ByteArrayClassLoader());
}
public ASTToTargetAST(List<ResultSet> resultSets, SourceFile sourceFile, ByteArrayClassLoader classLoader) {
this.classLoader = classLoader;
this.sourceFile = sourceFile;
all = new ArrayList<>();
for (var set : resultSets) {
all.add(new Sigma(set));
@ -596,11 +621,7 @@ public class ASTToTargetAST {
);
}
private Set<Integer> usedFunN = new HashSet<>();
public List<byte[]> generateUsedFunNTypes() {
return usedFunN.stream().map(n -> FunNGenerator.getInstance().generateSuperBytecode(n)).toList();
}
private final Set<Integer> usedFunN = new HashSet<>();
protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input) {
return input.acceptTV(new TypeVisitor<>() {
@ -611,7 +632,10 @@ public class ASTToTargetAST {
var params = refType.getParaList().stream().map(ASTToTargetAST.this::convert).toList();
if (name.matches("Fun\\d\\$\\$")) { // TODO This seems like a bad idea
usedFunN.add(params.size() - 1);
if (!usedFunN.contains(params.size() - 1)) {
usedFunN.add(params.size() - 1);
classLoader.loadClass(FunNGenerator.getInstance().generateSuperBytecode(params.size() - 1));
}
return new TargetFunNType(params.size() - 1, params);
}
return new TargetRefType(name, params);

View File

@ -2,8 +2,11 @@ package de.dhbwstuttgart.target.generate;
import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.TargetFunNType;
@ -11,6 +14,7 @@ import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class StatementToTargetExpression implements StatementVisitor {
@ -131,10 +135,41 @@ public class StatementToTargetExpression implements StatementVisitor {
var receiverType = converter.convert(methodCall.receiver.getType());
var isFunNType = receiverType instanceof TargetFunNType;
var returnType = converter.convert(methodCall.getType());
var receiverName = new JavaClassName(((TargetRefType) converter.convert(methodCall.receiver.getType())).name());
var argList = methodCall.arglist.getArguments().stream().map(expr -> converter.convert(expr.getType())).toList();
java.lang.reflect.Method foundMethod = null;
if (converter.sourceFile.imports.contains(receiverName)) {
try {
var clazz = converter.classLoader.loadClass(receiverName.toString());
outer: for (var method : clazz.getMethods()) {
if (method.getParameterTypes().length != argList.size()) continue;
if (!method.getName().equals(methodCall.name)) continue;
for (var i = 0; i < method.getParameterTypes().length; i++) {
var param = method.getParameterTypes()[i];
var arg = argList.get(i);
if (param.isPrimitive()) {
arg = TargetType.toPrimitive(arg);
}
if (!TargetType.toTargetType(param).equals(arg)) continue outer;
}
foundMethod = method;
break;
}
} catch (ClassNotFoundException ignored) {}
}
if (foundMethod != null) {
returnType = TargetType.toTargetType(foundMethod.getReturnType());
argList = Stream.of(foundMethod.getParameterTypes()).map(TargetType::toTargetType).toList();
}
result = new TargetMethodCall(
returnType,
methodCall.arglist.getArguments().stream().map(expr -> converter.convert(expr.getType())).toList(),
converter.convert(methodCall.getType()),
returnType, argList,
converter.convert(methodCall.receiver),
methodCall.getArgumentList().getArguments().stream().map(converter::convert).toList(),
receiverType,
@ -205,8 +240,9 @@ public class StatementToTargetExpression implements StatementVisitor {
@Override
public void visit(SuperCall superCall) {
var aSuper = converter.convert(converter.currentClass.getSuperClass());
var type = converter.convert(superCall.getType());
result = new TargetMethodCall(
converter.convert(superCall.getType()),
type, type,
superCall.argTypes == null ? List.of() : superCall.argTypes.stream().map(converter::convert).toList(),
new TargetSuper(aSuper),
superCall.getArgumentList().getArguments().stream().map(converter::convert).toList(),

View File

@ -7,13 +7,13 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List;
public record TargetMethodCall(TargetType type, List<TargetType> parameterTypes, TargetExpression expr, List<TargetExpression> args, TargetType owner, String name, boolean isStatic, boolean isInterface) implements TargetStatementExpression {
public record TargetMethodCall(TargetType type, TargetType returnType, 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);
this(type, type, args.stream().map(TargetExpression::type).toList(), expr, args, owner, name, isStatic, isInterface);
}
public String getDescriptor() {
return TargetMethod.getDescriptor(type, parameterTypes.toArray(TargetType[]::new));
return TargetMethod.getDescriptor(returnType, parameterTypes.toArray(TargetType[]::new));
}
}

View File

@ -13,7 +13,7 @@ public record TargetExtendsWildcard(TargetType innerType) implements TargetType
@Override
public String getName() {
return null;
return innerType.getName();
}
}

View File

@ -0,0 +1,19 @@
package de.dhbwstuttgart.target.tree.type;
public record TargetPrimitiveType(String name) implements TargetType {
@Override
public String toSignature() {
return getName();
}
@Override
public String toGenericSignature() {
return toSignature();
}
@Override
public String getName() {
return name;
}
}

View File

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

View File

@ -3,7 +3,7 @@ package de.dhbwstuttgart.target.tree.type;
import java.util.List;
public sealed interface TargetType
permits TargetExtendsWildcard, TargetGenericType, TargetSpecializedType, TargetSuperWildcard {
permits TargetExtendsWildcard, TargetGenericType, TargetSpecializedType, TargetSuperWildcard, TargetPrimitiveType {
// Builtin types
TargetRefType Boolean = new TargetRefType("java.lang.Boolean");
@ -17,6 +17,42 @@ public sealed interface TargetType
TargetRefType String = new TargetRefType("java.lang.String");
TargetRefType Object = new TargetRefType("java.lang.Object");
// Builtin types
TargetPrimitiveType boolean_ = new TargetPrimitiveType("Z");
TargetPrimitiveType char_ = new TargetPrimitiveType("C");
TargetPrimitiveType byte_ = new TargetPrimitiveType("B");
TargetPrimitiveType short_ = new TargetPrimitiveType("S");
TargetPrimitiveType int_ = new TargetPrimitiveType("I");
TargetPrimitiveType long_ = new TargetPrimitiveType("J");
TargetPrimitiveType float_ = new TargetPrimitiveType("F");
TargetPrimitiveType double_ = new TargetPrimitiveType("D");
static TargetType toPrimitive(TargetType type) {
if (type.equals(Boolean)) return boolean_;
if (type.equals(Char)) return char_;
if (type.equals(Byte)) return byte_;
if (type.equals(Short)) return short_;
if (type.equals(Integer)) return int_;
if (type.equals(Long)) return long_;
if (type.equals(Float)) return float_;
if (type.equals(Double)) return double_;
return type;
}
static TargetType toTargetType(Class<?> clazz) {
if (clazz.isPrimitive()) {
if (clazz.equals(boolean.class)) return boolean_;
if (clazz.equals(char.class)) return char_;
if (clazz.equals(byte.class)) return byte_;
if (clazz.equals(short.class)) return short_;
if (clazz.equals(int.class)) return int_;
if (clazz.equals(long.class)) return long_;
if (clazz.equals(float.class)) return float_;
if (clazz.equals(double.class)) return double_;
}
return new TargetRefType(clazz.getName());
}
String toSignature();
String toGenericSignature();
String getName();

View File

@ -5,6 +5,7 @@ import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.tree.TargetClass;
import de.dhbwstuttgart.typeinference.result.ResultSet;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;
@ -15,7 +16,7 @@ public class InheritTest2 {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
var classLoader = new ByteArrayClassLoader();
classToTest = TestCodegen.generateClassFiles("Inherit.jav", classLoader).get("Inherit2");
classToTest = TestCodegen.generateClassFiles("Inherit2.jav", classLoader).get("Inherit2");
classToTestAA = TestCodegen.generateClassFiles("AA.jav", classLoader).get("AA");
classToTestBB = TestCodegen.generateClassFiles("BB.jav", classLoader).get("BB");
classToTestCC = TestCodegen.generateClassFiles("CC.jav", classLoader).get("CC");

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,5 +1,6 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;

View File

@ -1,6 +1,7 @@
package targetast;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import de.dhbwstuttgart.target.bytecode.Codegen;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.tree.MethodParameter;
@ -38,13 +39,10 @@ public class TestCodegen {
var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/", filename).toFile();
var compiler = new JavaTXCompiler(file);
var resultSet = compiler.typeInference();
var converter = new ASTToTargetAST(resultSet);
var sourceFile = compiler.sourceFiles.get(file);
var converter = new ASTToTargetAST(resultSet, sourceFile, classLoader);
var classes = compiler.sourceFiles.get(file).getClasses();
for (var bytes : converter.generateUsedFunNTypes()) {
classLoader.loadClass(bytes);
}
return classes.stream().map(cli -> {
try {
return generateClass(converter.convert(cli), classLoader);
@ -341,7 +339,7 @@ public class TestCodegen {
)
))
),
new TargetReturn(new TargetCast(TargetType.Integer, new TargetMethodCall(TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of(
new TargetReturn(new TargetCast(TargetType.Integer, new TargetMethodCall(TargetType.Object, TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of(
new TargetLiteral.IntLiteral(10)
), interfaceType, "apply", false, true)))
))

View File

@ -1,9 +1,9 @@
package targetast;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.Ignore;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Vector;
import static org.junit.Assert.*;

View File

@ -1,13 +1,10 @@
package targetast;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import static org.junit.Assert.assertEquals;

View File

@ -1,14 +1,10 @@
package targetast;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.target.ByteArrayClassLoader;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import static org.junit.Assert.assertEquals;