From 786e0a7a2329514039f86c1432fc0c7820b4a0a5 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 21 Mar 2024 11:49:16 +0100 Subject: [PATCH] Fix #300 and also call private methods correctly --- resources/bytecode/javFiles/Bug300.jav | 17 +++++++++++ .../de/dhbwstuttgart/bytecode/Codegen.java | 7 ++++- .../de/dhbwstuttgart/core/JavaTXCompiler.java | 1 + .../StatementGenerator.java | 2 +- .../syntaxtree/statement/Super.java | 9 ++++-- .../syntaxtree/statement/This.java | 5 ++-- .../generate/StatementToTargetExpression.java | 8 ++++-- .../tree/expression/TargetMethodCall.java | 8 ++---- .../TypeInferenceBlockInformation.java | 8 ++++++ .../typeinference/typeAlgo/TYPEStmt.java | 28 ++++++++++--------- src/test/java/TestComplete.java | 7 +++++ src/test/java/targetast/TestCodegen.java | 6 ++-- 12 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 resources/bytecode/javFiles/Bug300.jav diff --git a/resources/bytecode/javFiles/Bug300.jav b/resources/bytecode/javFiles/Bug300.jav new file mode 100644 index 00000000..728aa3f7 --- /dev/null +++ b/resources/bytecode/javFiles/Bug300.jav @@ -0,0 +1,17 @@ +import java.lang.String; + +class Base { + toString() { + return "Base"; + } +} + +public class Bug300 extends Base { + public m() { + return super.toString(); + } + + toString() { + return "Derived"; + } +} \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index 8c92abfb..dda6aadd 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -1045,7 +1045,12 @@ public class Codegen { if (call.owner() instanceof TargetFunNType) // Decay FunN descriptor = TargetMethod.getDescriptor(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("") ? INVOKESPECIAL : INVOKEVIRTUAL, call.owner().getInternalName(), call.name(), descriptor, call.isInterface()); + int insn = INVOKEVIRTUAL; + if (call.isInterface()) insn = INVOKEINTERFACE; + else if (call.isStatic()) insn = INVOKESTATIC; + else if (call.name().equals("") || call.expr() instanceof TargetSuper || call.isPrivate()) insn = INVOKESPECIAL; + + mv.visitMethodInsn(insn, call.owner().getInternalName(), call.name(), descriptor, call.isInterface()); if (call.type() != null && call.returnType() != null && !(call.returnType() instanceof TargetPrimitiveType)) { if (!call.returnType().equals(call.type()) && !(call.type() instanceof TargetGenericType)) diff --git a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java index 7debd5e3..fb56e8c5 100644 --- a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java +++ b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java @@ -131,6 +131,7 @@ public class JavaTXCompiler { public ConstraintSet getConstraints() throws ClassNotFoundException, IOException { Set allClasses = new HashSet<>();// environment.getAllAvailableClasses(); ClassOrInterface objectClass = ASTFactory.createClass(classLoader.loadClass(new JavaClassName("java.lang.Object").toString())); + allClasses.add(objectClass); // Alle Importierten Klassen in allen geparsten Sourcefiles kommen ins FC for (Entry source : sourceFiles.entrySet()) { var importedClasses = new ArrayList(); diff --git a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java index 2c7044d3..a4ca0246 100644 --- a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java +++ b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java @@ -1028,7 +1028,7 @@ public class StatementGenerator { case PrimaryThisContext primthis: return new This(primthis.getStart()); case PrimarySuperContext primsuper: - throw new NotImplementedException(); + return new Super(primsuper.getStart()); case PrimaryLiteralContext primliteral: return convert(primliteral.literal()); case PrimaryIdentifierContext primidentifier: diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/statement/Super.java b/src/main/java/de/dhbwstuttgart/syntaxtree/statement/Super.java index e56e2fe4..e7a615ec 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/statement/Super.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/statement/Super.java @@ -3,6 +3,7 @@ package de.dhbwstuttgart.syntaxtree.statement; import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.syntaxtree.StatementVisitor; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; +import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.Void; import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceBlockInformation; import de.dhbwstuttgart.typeinference.constraints.ConstraintSet; @@ -10,8 +11,12 @@ import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceInformation; import org.antlr.v4.runtime.Token; import de.dhbwstuttgart.exceptions.NotImplementedException; -public class Super extends Expression -{ +public class Super extends Expression { + + public Super(Token offset) { + super(TypePlaceholder.fresh(offset), offset); + } + public Super(RefTypeOrTPHOrWildcardOrGeneric type, Token offset) { super(type, offset); diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/statement/This.java b/src/main/java/de/dhbwstuttgart/syntaxtree/statement/This.java index cd7ef5ec..276017ef 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/statement/This.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/statement/This.java @@ -8,9 +8,8 @@ import de.dhbwstuttgart.exceptions.NotImplementedException; public class This extends Expression { - public This(Token offset) - { - super(TypePlaceholder.fresh(offset),offset); + public This(Token offset) { + super(TypePlaceholder.fresh(offset), offset); } public ArgumentList arglist; diff --git a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java index acfaabab..3014ae46 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java @@ -199,6 +199,7 @@ public class StatementToTargetExpression implements ASTVisitor { Method foundMethod = null; var isStatic = false; var isInterface = true; + var isPrivate = false; var signature = methodCall.signatureArguments().stream().map(converter::convert).toList(); var receiverClass = converter.currentClass; @@ -214,10 +215,11 @@ public class StatementToTargetExpression implements ASTVisitor { returnType = converter.convert(foundMethod.getReturnType()); argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> converter.convert(e.getType())).toList(); isStatic = Modifier.isStatic(foundMethod.modifier); + isPrivate = Modifier.isPrivate(foundMethod.modifier); isInterface = receiverClass.isInterface(); } - result = new TargetMethodCall(converter.convert(methodCall.getType()), returnType, argList, converter.convert(methodCall.receiver), methodCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), receiverType, methodCall.name, isStatic, isInterface); + result = new TargetMethodCall(converter.convert(methodCall.getType()), returnType, argList, converter.convert(methodCall.receiver), methodCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), receiverType, methodCall.name, isStatic, isInterface, isPrivate); } @Override @@ -288,7 +290,7 @@ public class StatementToTargetExpression implements ASTVisitor { var type = converter.convert(superCall.getType()); var parameters = superCall.arglist.getArguments().stream().map(par -> converter.convert(par.getType())).toList(); - result = new TargetMethodCall(type, type, parameters, new TargetSuper(aSuper), superCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), aSuper, superCall.name, false, false); + result = new TargetMethodCall(type, type, parameters, new TargetSuper(aSuper), superCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), aSuper, superCall.name, false, false, false); } @Override @@ -297,7 +299,7 @@ public class StatementToTargetExpression implements ASTVisitor { var type = converter.convert(thisCall.getType()); var parameters = thisCall.arglist.getArguments().stream().map(par -> converter.convert(par.getType())).toList(); - result = new TargetMethodCall(type, type, parameters, new TargetThis(aThis), thisCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), aThis, thisCall.name, false, false); + result = new TargetMethodCall(type, type, parameters, new TargetThis(aThis), thisCall.getArgumentList().getArguments().stream().map(converter::convert).toList(), aThis, thisCall.name, false, false, false); } @Override diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java index 50a31471..7b41a5f2 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java @@ -1,15 +1,13 @@ 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, TargetType returnType, List parameterTypes, TargetExpression expr, List args, TargetType owner, String name, boolean isStatic, boolean isInterface) implements TargetStatementExpression { - public TargetMethodCall(TargetType type, TargetExpression expr, List args, TargetType owner, String name, boolean isStatic, boolean isInterface) { - this(type, type, args.stream().map(TargetExpression::type).toList(), expr, args, owner, name, isStatic, isInterface); +public record TargetMethodCall(TargetType type, TargetType returnType, List parameterTypes, TargetExpression expr, List args, TargetType owner, String name, boolean isStatic, boolean isInterface, boolean isPrivate) implements TargetStatementExpression { + public TargetMethodCall(TargetType type, TargetExpression expr, List args, TargetType owner, String name, boolean isStatic, boolean isInterface, boolean isPrivate) { + this(type, type, args.stream().map(TargetExpression::type).toList(), expr, args, owner, name, isStatic, isInterface, isPrivate); } diff --git a/src/main/java/de/dhbwstuttgart/typeinference/assumptions/TypeInferenceBlockInformation.java b/src/main/java/de/dhbwstuttgart/typeinference/assumptions/TypeInferenceBlockInformation.java index df280d48..94460472 100644 --- a/src/main/java/de/dhbwstuttgart/typeinference/assumptions/TypeInferenceBlockInformation.java +++ b/src/main/java/de/dhbwstuttgart/typeinference/assumptions/TypeInferenceBlockInformation.java @@ -30,6 +30,14 @@ public class TypeInferenceBlockInformation extends TypeInferenceInformation { public ClassOrInterface getCurrentClass() { return currentClass; } + + public ClassOrInterface getSuperClass() { + for (var clazz : getAvailableClasses()) { + if (clazz.getClassName().equals(currentClass.getSuperClass().getName())) + return clazz; + } + return null; + } public TypeScope getCurrentTypeScope() { return methodContext; } diff --git a/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java b/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java index 479eedb3..35626c69 100644 --- a/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java +++ b/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java @@ -499,7 +499,13 @@ public class TYPEStmt implements StatementVisitor { @Override public void visit(Super aSuper) { - throw new NotImplementedException(); + var superClass = info.getSuperClass(); + var params = new ArrayList(); + for (var gtv : superClass.getGenerics()) { + params.add(new GenericRefType(gtv.getName(), aSuper.getOffset())); + } + var superType = new RefType(superClass.getClassName(), params, aSuper.getOffset()); + constraintsSet.addUndConstraint(new Pair(aSuper.getType(), superType, PairOperator.EQUALSDOT, loc(aSuper.getOffset()))); } @Override @@ -559,19 +565,15 @@ public class TYPEStmt implements StatementVisitor { @Override public void visit(SuperCall superCall) { Set> methodConstraints = new HashSet<>(); - for (var clazz : info.getAvailableClasses()) { - if (clazz.getClassName().equals(info.getCurrentClass().getSuperClass().getName())) { - for (var ctor : clazz.getConstructors()) { - var params = convertParams(ctor.getParameterList(), info); - if (params.size() != superCall.arglist.getArguments().size()) continue; - var assumption = new MethodAssumption(null, new Void(new NullToken()), params, createTypeScope(clazz, ctor), ctor.isInherited); + var clazz = info.getSuperClass(); + for (var ctor : clazz.getConstructors()) { + var params = convertParams(ctor.getParameterList(), info); + if (params.size() != superCall.arglist.getArguments().size()) continue; + var assumption = new MethodAssumption(null, new Void(new NullToken()), params, createTypeScope(clazz, ctor), ctor.isInherited); - GenericsResolver resolver = getResolverInstance(); - Set> oneMethodConstraints = generateConstraint(superCall, assumption, info, resolver); - methodConstraints.addAll(oneMethodConstraints); - } - break; - } + GenericsResolver resolver = getResolverInstance(); + Set> oneMethodConstraints = generateConstraint(superCall, assumption, info, resolver); + methodConstraints.addAll(oneMethodConstraints); } constraintsSet.addOderConstraint(methodConstraints); } diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index c3d6a39c..50049a90 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -980,4 +980,11 @@ public class TestComplete { var clazz = classFiles.get("Bug298"); var instance = clazz.getDeclaredConstructor().newInstance(); } + @Test + public void testBug300() throws Exception { + var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug300.jav"); + var clazz = classFiles.get("Bug300"); + var instance = clazz.getDeclaredConstructor().newInstance(); + assertEquals(clazz.getDeclaredMethod("m").invoke(instance), "Base"); + } } diff --git a/src/test/java/targetast/TestCodegen.java b/src/test/java/targetast/TestCodegen.java index d737bf53..cb9a8e02 100644 --- a/src/test/java/targetast/TestCodegen.java +++ b/src/test/java/targetast/TestCodegen.java @@ -174,7 +174,7 @@ public class TestCodegen { @Test public void testMethodCall() throws Exception { var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("HelloWorld")); - 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"), 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", false, false)))); + 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"), 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", false, false, false)))); var clazz = generateClass(targetClass, new ByteArrayClassLoader()); clazz.getDeclaredMethod("helloWorld").invoke(null); @@ -271,8 +271,8 @@ public class TestCodegen { var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer)); var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("CGLambda")); - targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "", false, false)))); - targetClass.addMethod(Opcodes.ACC_PUBLIC, "lambda", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(interfaceType, "by2", new TargetLambdaExpression(interfaceType, List.of(), 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, TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of(new TargetLiteral.IntLiteral(10)), interfaceType, "apply", false, true)))))); + targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "", false, false, false)))); + targetClass.addMethod(Opcodes.ACC_PUBLIC, "lambda", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(interfaceType, "by2", new TargetLambdaExpression(interfaceType, List.of(), 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, TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of(new TargetLiteral.IntLiteral(10)), interfaceType, "apply", false, true, false)))))); var clazz = generateClass(targetClass, classLoader); var instance = clazz.getConstructor().newInstance(); assertEquals(clazz.getDeclaredMethod("lambda").invoke(instance), 20);