From 99f219de3b40d8e94024984f0e63ef396d7396d9 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Mon, 27 May 2024 12:14:00 +0200 Subject: [PATCH 1/4] Work on #338, partially fixed --- resources/bytecode/javFiles/Bug338.jav | 11 +++++++++++ .../dhbwstuttgart/target/generate/ASTToTargetAST.java | 8 +++++--- src/test/java/TestComplete.java | 7 +++++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 resources/bytecode/javFiles/Bug338.jav diff --git a/resources/bytecode/javFiles/Bug338.jav b/resources/bytecode/javFiles/Bug338.jav new file mode 100644 index 00000000..fa846e95 --- /dev/null +++ b/resources/bytecode/javFiles/Bug338.jav @@ -0,0 +1,11 @@ +import java.util.List; +import java.lang.Integer; +import java.lang.String; +import java.lang.Object; +import java.util.List; + +public class Bug338 { + public hashCode() { + return List.of(42); + } +} \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 1c485d7e..e62fbf80 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -366,7 +366,6 @@ public class ASTToTargetAST { for (var i = 0; i < params.size(); i++) { var a = TargetType.toPrimitive(params.get(i).pattern().type()); var b = convert(sParams.getFormalparalist().get(i).getType()); - System.out.println(a + " " + b); if (!Objects.equals(a, b)) return false; } return true; @@ -387,8 +386,11 @@ public class ASTToTargetAST { var superMethod = findSuperMethodToOverride(currentClass, method.getName(), params); if (superMethod.isPresent()) { // If we find a super method to override, use its parameters and return types - returnType = convert(superMethod.get().getReturnType(), this.generics.javaGenerics); - params = convert(superMethod.get().getParameterList(), method.getParameterList(), this.generics.javaGenerics); + var newReturnType = convert(superMethod.get().getReturnType(), this.generics.javaGenerics); + if (newReturnType instanceof TargetPrimitiveType && TargetType.toPrimitive(returnType).equals(newReturnType)) { + returnType = newReturnType; + params = convert(superMethod.get().getParameterList(), method.getParameterList(), this.generics.javaGenerics); + } } List finalParams = params; diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index 93cfb387..3058ac14 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -1084,4 +1084,11 @@ public class TestComplete { var res = clazz.getDeclaredMethod("convert", List.class).invoke(instance, list); assertEquals(res, List.of(6, 7, 8)); } + + @Test + public void testBug338() throws Exception { + var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug338.jav"); + var clazz = classFiles.get("Bug338"); + var instance = clazz.getDeclaredConstructor().newInstance(); + } } From 4880527d4d5ac17a7383643684fa22651fce3122 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Mon, 27 May 2024 15:51:48 +0200 Subject: [PATCH 2/4] Give an exception if a method has been duplicated --- .../target/generate/ASTToTargetAST.java | 31 +++++++++++++------ .../target/tree/TargetMethod.java | 12 +++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index e62fbf80..bddeeca7 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -1,5 +1,6 @@ package de.dhbwstuttgart.target.generate; +import de.dhbwstuttgart.bytecode.CodeGenException; import de.dhbwstuttgart.bytecode.FunNGenerator; import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.environment.ByteArrayClassLoader; @@ -160,11 +161,11 @@ public class ASTToTargetAST { var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics)).toList(); var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer)).flatMap(List::stream).toList(); var fields = input.getFieldDecl().stream().map(this::convert).toList(); - var methods = groupOverloads(input.getMethods()).stream().map(m -> convert(input, m)).flatMap(List::stream).toList(); + var methods = groupOverloads(input.getMethods()).stream().map(m -> convert(input, m)).flatMap(Set::stream).toList(); TargetMethod staticConstructor = null; if (input.getStaticInitializer().isPresent()) - staticConstructor = this.convert(input, input.getStaticInitializer().get()).get(0); + staticConstructor = this.convert(input, input.getStaticInitializer().get()).stream().findFirst().orElseThrow(); if (input instanceof Record) return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods); @@ -289,11 +290,11 @@ public class ASTToTargetAST { return res.toString(); } - private List convert(ClassOrInterface clazz, List overloadedMethods) { + private Set convert(ClassOrInterface clazz, List overloadedMethods) { if (overloadedMethods.size() == 1) { - return convert(clazz, overloadedMethods.get(0)); + return convert(clazz, overloadedMethods.getFirst()); } - var res = new ArrayList(); + var methods = new ArrayList(); for (var method : overloadedMethods) { var newMethod = new Method( method.modifier, @@ -305,7 +306,7 @@ public class ASTToTargetAST { method.getGenerics(), method.getOffset() ); - res.add(newMethod); + methods.add(newMethod); } // TODO Record overloading @@ -328,7 +329,15 @@ public class ASTToTargetAST { var entryPoint = new Method(template.modifier, template.name, template.getReturnType(), params, block, template.getGenerics(), new NullToken()); res.add(entryPoint); // TODO*/ - return res.stream().map(m -> convert(clazz, m)).flatMap(List::stream).toList(); + var res = new HashSet(); + for (var method : methods) { + var overloads = convert(clazz, method); + for (var overload : overloads) { + if (res.contains(overload)) throw new CodeGenException("Duplicate method found: " + overload.name() + " with signature " + overload.signature().getSignature()); + res.add(overload); + } + } + return res; } private Expression makeRecordSwitch(RefTypeOrTPHOrWildcardOrGeneric returnType, ParameterList params, List overloadedMethods) { @@ -372,9 +381,9 @@ public class ASTToTargetAST { }).findFirst(); } - private List convert(ClassOrInterface currentClass, Method method) { + private Set convert(ClassOrInterface currentClass, Method method) { generics = all.get(0); - List result = new ArrayList<>(); + Set result = new HashSet<>(); Set> parameterSet = new HashSet<>(); for (var s : all) { @@ -402,10 +411,12 @@ public class ASTToTargetAST { var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType); var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), this.generics.txGenerics)); - result.add(new TargetMethod(method.modifier, method.name, convert(method.block), javaSignature, txSignature)); + var newMethod = new TargetMethod(method.modifier, method.name, convert(method.block), javaSignature, txSignature); + result.add(newMethod); parameterSet.add(params); } } + return result; } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java index 5a8b9368..bdabc81e 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java @@ -6,6 +6,7 @@ import de.dhbwstuttgart.target.tree.type.TargetType; import org.objectweb.asm.Opcodes; import java.util.List; +import java.util.Objects; import java.util.Set; public record TargetMethod(int access, String name, TargetBlock block, Signature signature, Signature txSignature) { @@ -64,5 +65,16 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature public boolean isStatic() { return (access & Opcodes.ACC_STATIC) != 0; } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TargetMethod otherMethod)) return false; + return otherMethod.signature.equals(this.signature) && otherMethod.name.equals(this.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, signature); + } } From 7650813bb72f5a0f197d9ec23d2e82d083bb4827 Mon Sep 17 00:00:00 2001 From: Ruben Date: Thu, 6 Jun 2024 12:06:28 +0200 Subject: [PATCH 3/4] feat: changes in Grammar and Parser so typeless Recs get recognised --- resources/bytecode/javFiles/SwitchInfered.jav | 17 +++++++++++++++++ .../dhbwstuttgart/parser/antlr/Java17Parser.g4 | 6 ++++-- .../SyntaxTreeGenerator/StatementGenerator.java | 2 +- src/test/java/TestComplete.java | 9 +++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 resources/bytecode/javFiles/SwitchInfered.jav diff --git a/resources/bytecode/javFiles/SwitchInfered.jav b/resources/bytecode/javFiles/SwitchInfered.jav new file mode 100644 index 00000000..d11c760d --- /dev/null +++ b/resources/bytecode/javFiles/SwitchInfered.jav @@ -0,0 +1,17 @@ +import java.lang.Integer; +import java.lang.Object; +import java.lang.Float; + +public record Rec(Integer a, Object b) {} + +public class SwitchInfered { + public main(o) { + return switch (o) { + case Rec(a, b) -> a + b; + case Rec(Integer a, Float b) -> a + 10; + case Rec(Integer a, Rec(Integer b, Integer c)) -> a + b + c; + case Integer i -> i; + default -> 0; + }; + } +} \ No newline at end of file diff --git a/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 b/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 index be87c4d7..891c47b3 100644 --- a/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 +++ b/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 @@ -658,10 +658,12 @@ primaryPattern recordPattern : typeType recordStructurePattern identifier? + //| recordStructurePattern identifier? ; typePattern : variableModifier* typeType identifier + | variableModifier* identifier ; recordStructurePattern @@ -717,10 +719,10 @@ switchLabeledRule ; switchLabelCase - : CASE expressionList (ARROW | COLON) #labeledRuleExprList - | CASE NULL_LITERAL (ARROW | COLON) #labeledRuleNull + : CASE NULL_LITERAL (ARROW | COLON) #labeledRuleNull | CASE pattern (ARROW | COLON) #labeledRulePattern | DEFAULT (ARROW | COLON) #labeledRuleDefault + | CASE expressionList (ARROW | COLON) #labeledRuleExprList ; // Java20 diff --git a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java index f4587b6b..11716318 100644 --- a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java +++ b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java @@ -457,7 +457,7 @@ public class StatementGenerator { case TPatternContext tPattern: TypePatternContext typePattern = tPattern.typePattern(); var text = typePattern.identifier().getText(); - var type = TypeGenerator.convert(typePattern.typeType(), reg, generics); + var type = typePattern.typeType() == null ? TypePlaceholder.fresh(typePattern.getStart()) : TypeGenerator.convert(typePattern.typeType(), reg, generics); localVars.put(text, type); return new FormalParameter(text, type, typePattern.getStart()); case RPatternContext rPattern: diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index fd4b8b5e..9d318ba6 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -673,6 +673,15 @@ public class TestComplete { assertEquals(swtch.invoke(instance, "Some string"), 0); } + @Ignore("Not implemented") + @Test + public void testSwitchInfered() throws Exception { + var classFiles = generateClassFiles(new ByteArrayClassLoader(), "SwitchInfered.jav"); + var clazz = classFiles.get("SwitchInfered"); + var instance = clazz.getDeclaredConstructor().newInstance(); + var swtch = clazz.getDeclaredMethod("main", Object.class); + } + @Ignore("Not implemented") @Test public void testSwitch2() throws Exception { From ea217d16d5a1d635ac7fe961b98c2ad993c14ac5 Mon Sep 17 00:00:00 2001 From: Ruben Date: Thu, 6 Jun 2024 12:07:32 +0200 Subject: [PATCH 4/4] Revert "feat: changes in Grammar and Parser so typeless Recs get recognised" This reverts commit 7650813bb72f5a0f197d9ec23d2e82d083bb4827. --- resources/bytecode/javFiles/SwitchInfered.jav | 17 ----------------- .../dhbwstuttgart/parser/antlr/Java17Parser.g4 | 6 ++---- .../SyntaxTreeGenerator/StatementGenerator.java | 2 +- src/test/java/TestComplete.java | 9 --------- 4 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 resources/bytecode/javFiles/SwitchInfered.jav diff --git a/resources/bytecode/javFiles/SwitchInfered.jav b/resources/bytecode/javFiles/SwitchInfered.jav deleted file mode 100644 index d11c760d..00000000 --- a/resources/bytecode/javFiles/SwitchInfered.jav +++ /dev/null @@ -1,17 +0,0 @@ -import java.lang.Integer; -import java.lang.Object; -import java.lang.Float; - -public record Rec(Integer a, Object b) {} - -public class SwitchInfered { - public main(o) { - return switch (o) { - case Rec(a, b) -> a + b; - case Rec(Integer a, Float b) -> a + 10; - case Rec(Integer a, Rec(Integer b, Integer c)) -> a + b + c; - case Integer i -> i; - default -> 0; - }; - } -} \ No newline at end of file diff --git a/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 b/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 index 891c47b3..be87c4d7 100644 --- a/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 +++ b/src/main/antlr4/de/dhbwstuttgart/parser/antlr/Java17Parser.g4 @@ -658,12 +658,10 @@ primaryPattern recordPattern : typeType recordStructurePattern identifier? - //| recordStructurePattern identifier? ; typePattern : variableModifier* typeType identifier - | variableModifier* identifier ; recordStructurePattern @@ -719,10 +717,10 @@ switchLabeledRule ; switchLabelCase - : CASE NULL_LITERAL (ARROW | COLON) #labeledRuleNull + : CASE expressionList (ARROW | COLON) #labeledRuleExprList + | CASE NULL_LITERAL (ARROW | COLON) #labeledRuleNull | CASE pattern (ARROW | COLON) #labeledRulePattern | DEFAULT (ARROW | COLON) #labeledRuleDefault - | CASE expressionList (ARROW | COLON) #labeledRuleExprList ; // Java20 diff --git a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java index 11716318..f4587b6b 100644 --- a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java +++ b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java @@ -457,7 +457,7 @@ public class StatementGenerator { case TPatternContext tPattern: TypePatternContext typePattern = tPattern.typePattern(); var text = typePattern.identifier().getText(); - var type = typePattern.typeType() == null ? TypePlaceholder.fresh(typePattern.getStart()) : TypeGenerator.convert(typePattern.typeType(), reg, generics); + var type = TypeGenerator.convert(typePattern.typeType(), reg, generics); localVars.put(text, type); return new FormalParameter(text, type, typePattern.getStart()); case RPatternContext rPattern: diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index 9d318ba6..fd4b8b5e 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -673,15 +673,6 @@ public class TestComplete { assertEquals(swtch.invoke(instance, "Some string"), 0); } - @Ignore("Not implemented") - @Test - public void testSwitchInfered() throws Exception { - var classFiles = generateClassFiles(new ByteArrayClassLoader(), "SwitchInfered.jav"); - var clazz = classFiles.get("SwitchInfered"); - var instance = clazz.getDeclaredConstructor().newInstance(); - var swtch = clazz.getDeclaredMethod("main", Object.class); - } - @Ignore("Not implemented") @Test public void testSwitch2() throws Exception {