diff --git a/resources/bytecode/javFiles/ForEach.jav b/resources/bytecode/javFiles/ForEach.jav new file mode 100644 index 00000000..32b1eb8d --- /dev/null +++ b/resources/bytecode/javFiles/ForEach.jav @@ -0,0 +1,15 @@ +import java.util.ArrayList; +import java.lang.Integer; + +public class ForEach { + m() { + var list = new ArrayList<>(); + list.add(1); list.add(2); list.add(3); + + var sum = 0; + for (var i : list) { + sum = sum + i; + } + return sum; + } +} \ 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 5ef179d3..6cd9a3bc 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -2,6 +2,7 @@ package de.dhbwstuttgart.bytecode; import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.exceptions.NotImplementedException; +import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.syntaxtree.ClassOrInterface; import de.dhbwstuttgart.syntaxtree.type.RefType; @@ -918,6 +919,9 @@ public class Codegen { state.localCounter = localCounter; break; } + case TargetForEach forEach: + generateForEach(forEach, state); + break; case TargetWhile _while: { Label start = new Label(); Label end = new Label(); @@ -1041,6 +1045,35 @@ public class Codegen { } } + private void generateForEach(TargetForEach forEach, State state) { + state.enterScope(); + TargetVarDecl vd = (TargetVarDecl) forEach.vardecl(); + var localVar = state.createVariable(vd.name(), vd.varType()); + + var expr = forEach.expression(); + // TODO Check for arrays + var mv = state.mv; + generate(state, expr); + mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Iterable", "iterator", "()Ljava/util/Iterator;", true); + var start = new Label(); + var end = new Label(); + mv.visitLabel(start); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true); + mv.visitJumpInsn(IFEQ, end); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true); + mv.visitVarInsn(ASTORE, localVar.index); + + generate(state, forEach.body()); + + mv.visitJumpInsn(GOTO, start); + mv.visitLabel(end); + mv.visitInsn(POP); + + state.exitScope(); + } + private void generateInstanceOf(State state, TargetInstanceOf instanceOf) { var mv = state.mv; diff --git a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java index 3611f003..4514a0f5 100644 --- a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java +++ b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/StatementGenerator.java @@ -490,15 +490,32 @@ public class StatementGenerator { private Statement convert(Java17Parser.ForloopContext stmt) { var control = stmt.forControl(); - var block = convert(stmt.statement()); - if (control.enhancedForControl() != null) - throw new NotImplementedException(); - else { + if (control.enhancedForControl() != null) { + var forCtrl = control.enhancedForControl(); + var name = forCtrl.variableDeclaratorId().identifier(); + + RefTypeOrTPHOrWildcardOrGeneric type; + if (Objects.isNull(forCtrl.typeType()) || !Objects.isNull(forCtrl.VAR())) { + type = TypePlaceholder.fresh(forCtrl.getStart()); + } else { + type = TypeGenerator.convert(forCtrl.typeType(), reg, generics); + } + + var vardecl = new LocalVarDecl(name.getText(), type, name.getStart()); + this.localVars.put(name.getText(), type); + + return new ForEachStmt( + stmt.getStart(), + vardecl, + convert(forCtrl.expression()), + convert(stmt.statement()) + ); + } else { return new ForStmt( stmt.getStart(), convert(control.forInit().localVariableDeclaration()), convert(control.expression()), control.forUpdate.expression().stream().map(this::convert).toList(), - block + convert(stmt.statement()) ); } } diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/AbstractASTWalker.java b/src/main/java/de/dhbwstuttgart/syntaxtree/AbstractASTWalker.java index 9b8d5286..f8bf0502 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/AbstractASTWalker.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/AbstractASTWalker.java @@ -166,6 +166,11 @@ public abstract class AbstractASTWalker implements ASTVisitor { forStmt.block.accept(this); } + @Override + public void visit(ForEachStmt forEachStmt) { + forEachStmt.block.accept(this); + } + @Override public void visit(IfStmt ifStmt) { ifStmt.then_block.accept(this); diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/StatementVisitor.java b/src/main/java/de/dhbwstuttgart/syntaxtree/StatementVisitor.java index 737e984e..3cc4e128 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/StatementVisitor.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/StatementVisitor.java @@ -25,6 +25,8 @@ public interface StatementVisitor { void visit(ForStmt forStmt); + void visit(ForEachStmt forEachStmt); + void visit(IfStmt ifStmt); void visit(InstanceOf instanceOf); diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/statement/ForEachStmt.java b/src/main/java/de/dhbwstuttgart/syntaxtree/statement/ForEachStmt.java new file mode 100644 index 00000000..3686ea27 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/statement/ForEachStmt.java @@ -0,0 +1,26 @@ +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.Void; +import org.antlr.v4.runtime.Token; + +public class ForEachStmt extends Statement { + + public final Statement statement; + public final Expression expression; + public final Statement block; + + public ForEachStmt(Token offset, Statement stmt, Expression expression, Statement block) { + super(new Void(new NullToken()), offset); + this.statement = stmt; + this.expression = expression; + this.block = block; + } + + @Override + public void accept(StatementVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java b/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java index 1afcfdea..0e0b7081 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/visual/OutputGenerator.java @@ -244,6 +244,11 @@ public class OutputGenerator implements ASTVisitor { } + @Override + public void visit(ForEachStmt forEachStmt) { + + } + @Override public void visit(IfStmt ifStmt) { out.append("if("); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java index 1e5d77d5..6eeb784b 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java @@ -144,6 +144,11 @@ public class StatementToTargetExpression implements ASTVisitor { result = new TargetFor(forStmt.initializer.stream().map(converter::convert).toList(), converter.convert(forStmt.condition), forStmt.loopExpr.stream().map(converter::convert).toList(), converter.convert(forStmt.block)); } + @Override + public void visit(ForEachStmt forEachStmt) { + result = new TargetForEach(converter.convert(forEachStmt.statement), converter.convert(forEachStmt.expression), converter.convert(forEachStmt.block)); + } + @Override public void visit(IfStmt ifStmt) { result = new TargetIf(converter.convert(ifStmt.expr), converter.convert(ifStmt.then_block), ifStmt.else_block != null ? converter.convert(ifStmt.else_block) : null); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java b/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java index f7feb9f4..a84d6322 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java @@ -66,6 +66,11 @@ public abstract class TracingStatementVisitor implements StatementVisitor { forStmt.block.accept(this); } + @Override + public void visit(ForEachStmt forEachStmt) { + forEachStmt.block.accept(this); + } + @Override public void visit(IfStmt ifStmt) { ifStmt.then_block.accept(this); diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java index ce4fa10b..e7928afc 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetForEach.java @@ -1,4 +1,4 @@ package de.dhbwstuttgart.target.tree.expression; -public record TargetForEach(TargetExpression vardecl, TargetExpression list) implements TargetExpression { +public record TargetForEach(TargetExpression vardecl, TargetExpression expression, TargetExpression body) implements TargetExpression { } diff --git a/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java b/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java index 154beca8..ac023831 100644 --- a/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java +++ b/src/main/java/de/dhbwstuttgart/typeinference/typeAlgo/TYPEStmt.java @@ -128,6 +128,15 @@ public class TYPEStmt implements StatementVisitor { forStmt.block.accept(this); } + @Override + public void visit(ForEachStmt forEachStmt) { + var iterableType = new RefType(ASTFactory.createClass(java.lang.Iterable.class).getClassName(), Arrays.asList(forEachStmt.statement.getType()), new NullToken()); + constraintsSet.addUndConstraint(new Pair(forEachStmt.expression.getType(), iterableType, PairOperator.SMALLERDOT)); + forEachStmt.statement.accept(this); + forEachStmt.expression.accept(this); + forEachStmt.block.accept(this); + } + @Override public void visit(IfStmt ifStmt) { RefType booleanType = new RefType(ASTFactory.createClass(java.lang.Boolean.class).getClassName(), new NullToken()); diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index 4e885284..1f50d362 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -737,6 +737,15 @@ public class TestComplete { var m = clazz.getDeclaredMethod("m", Integer.class); assertEquals(m.invoke(instance, 10), 60); } + + @Test + public void testForEach() throws Exception { + var classFiles = generateClassFiles(new ByteArrayClassLoader(), "ForEach.jav"); + var clazz = classFiles.get("ForEach"); + var instance = clazz.getDeclaredConstructor().newInstance(); + var m = clazz.getDeclaredMethod("m"); + assertEquals(m.invoke(instance), 6); + } @Test public void testLambdaRunnable() throws Exception {