1
0

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

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

@ -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:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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.*;

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

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