From 09dd11bb344c61f36d7c6531147ba83437749459 Mon Sep 17 00:00:00 2001 From: Christian Brier Date: Sun, 23 Jun 2024 17:38:52 +0200 Subject: [PATCH 1/8] updated docs --- Something.java | 5 +++++ doc/bytecode.md | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Something.java diff --git a/Something.java b/Something.java new file mode 100644 index 0000000..a083d83 --- /dev/null +++ b/Something.java @@ -0,0 +1,5 @@ +public class Something +{ + public int a = 4134123; + //public Something something = null; +} \ No newline at end of file diff --git a/doc/bytecode.md b/doc/bytecode.md index 1002122..9bc7479 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -76,4 +76,7 @@ instance Serializable Operation where serialize (Opgetfield index) = 0xB4 : unpackWord16 index ``` -Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. \ No newline at end of file +Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. + + +Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stille des Pair Programming zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file From 9ecadc89461f3c32e1edfa14df16e23d45b2511e Mon Sep 17 00:00:00 2001 From: Christian Brier Date: Sun, 23 Jun 2024 17:39:40 +0200 Subject: [PATCH 2/8] updated docs again --- doc/bytecode.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/bytecode.md b/doc/bytecode.md index 9bc7479..bc3a6db 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -78,5 +78,6 @@ instance Serializable Operation where Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. +## Aufgabenteilung Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stille des Pair Programming zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file From 946a1f374c87fc26a29dfd3dfa8cede4cf0aadc6 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 07:44:04 +0200 Subject: [PATCH 3/8] fixed stack op depth for getfield --- src/ByteCode/Util.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ByteCode/Util.hs b/src/ByteCode/Util.hs index f177342..7f48b17 100644 --- a/src/ByteCode/Util.hs +++ b/src/ByteCode/Util.hs @@ -279,14 +279,14 @@ operationStackCost constants (Opiload _) = 1 operationStackCost constants (Opastore _) = -1 operationStackCost constants (Opistore _) = -1 operationStackCost constants (Opputfield _) = -2 -operationStackCost constants (Opgetfield _) = -1 +operationStackCost constants (Opgetfield _) = 0 -simulateStackOperation :: [ConstantInfo] -> Operation -> (Int, Int) -> (Int, Int) -simulateStackOperation constants op (cd, md) = let +simulateStackOperation :: (Int, Int) -> [ConstantInfo] -> Operation -> (Int, Int) +simulateStackOperation (cd, md) constants op = let depth = cd + operationStackCost constants op in if depth < 0 then error ("Consuming value off of empty stack: " ++ show op) else (depth, max depth md) maxStackDepth :: [ConstantInfo] -> [Operation] -> Int -maxStackDepth constants ops = snd $ foldr (simulateStackOperation constants) (0, 0) (reverse ops) +maxStackDepth constants ops = snd $ foldl (`simulateStackOperation` constants) (0, 0) ops From 74f52c3c35024201cd4ff163f3089f0dabdf1ea0 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 07:47:43 +0200 Subject: [PATCH 4/8] fixed small typo in documentation --- Something.java | 5 ----- doc/bytecode.md | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 Something.java diff --git a/Something.java b/Something.java deleted file mode 100644 index a083d83..0000000 --- a/Something.java +++ /dev/null @@ -1,5 +0,0 @@ -public class Something -{ - public int a = 4134123; - //public Something something = null; -} \ No newline at end of file diff --git a/doc/bytecode.md b/doc/bytecode.md index bc3a6db..5624b72 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -4,7 +4,7 @@ Die Bytecodegenerierung ist letztendlich eine zweistufige Transformation: `Getypter AST -> [ClassFile] -> [[Word8]]` -Vom AST, der bereits den Typcheck durchlaufen hat, wird zunächst eine Abbildung in die einzelnen ClassFiles vorgenommen. Diese ClassFiles werden anschließend in deren Byte-Repräsentation serialisiert. Dieser Teil der Aufgabenstellung wurde gemeinsam von Christian Brier und Matthias Raba umgesetzt. +Vom AST, der bereits den Typcheck durchlaufen hat, wird zunächst eine Abbildung in die einzelnen ClassFiles vorgenommen. Diese ClassFiles werden anschließend in deren Byte-Repräsentation serialisiert. ## Codegenerierung @@ -80,4 +80,4 @@ Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktio ## Aufgabenteilung -Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stille des Pair Programming zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file +Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stile des Pair Programmings zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse, alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file From 6346cb237bb4cc920b9e38f11f499379908ded4c Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 11:20:24 +0200 Subject: [PATCH 5/8] removed redundant definitions for opcodeEncodingLength --- src/ByteCode/ClassFile.hs | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/ByteCode/ClassFile.hs b/src/ByteCode/ClassFile.hs index 7a20e97..3546ded 100644 --- a/src/ByteCode/ClassFile.hs +++ b/src/ByteCode/ClassFile.hs @@ -96,40 +96,7 @@ className classFile = let opcodeEncodingLength :: Operation -> Word16 -opcodeEncodingLength Opiadd = 1 -opcodeEncodingLength Opisub = 1 -opcodeEncodingLength Opimul = 1 -opcodeEncodingLength Opidiv = 1 -opcodeEncodingLength Opirem = 1 -opcodeEncodingLength Opiand = 1 -opcodeEncodingLength Opior = 1 -opcodeEncodingLength Opixor = 1 -opcodeEncodingLength Opineg = 1 -opcodeEncodingLength Opdup = 1 -opcodeEncodingLength (Opnew _) = 3 -opcodeEncodingLength (Opif_icmplt _) = 3 -opcodeEncodingLength (Opif_icmple _) = 3 -opcodeEncodingLength (Opif_icmpgt _) = 3 -opcodeEncodingLength (Opif_icmpge _) = 3 -opcodeEncodingLength (Opif_icmpeq _) = 3 -opcodeEncodingLength (Opif_icmpne _) = 3 -opcodeEncodingLength Opaconst_null = 1 -opcodeEncodingLength Opreturn = 1 -opcodeEncodingLength Opireturn = 1 -opcodeEncodingLength Opareturn = 1 -opcodeEncodingLength Opdup_x1 = 1 -opcodeEncodingLength Oppop = 1 -opcodeEncodingLength (Opinvokespecial _) = 3 -opcodeEncodingLength (Opinvokevirtual _) = 3 -opcodeEncodingLength (Opgoto _) = 3 -opcodeEncodingLength (Opsipush _) = 3 -opcodeEncodingLength (Opldc_w _) = 3 -opcodeEncodingLength (Opaload _) = 4 -opcodeEncodingLength (Opiload _) = 4 -opcodeEncodingLength (Opastore _) = 4 -opcodeEncodingLength (Opistore _) = 4 -opcodeEncodingLength (Opputfield _) = 3 -opcodeEncodingLength (Opgetfield _) = 3 +opcodeEncodingLength op = fromIntegral . length . serialize $ op class Serializable a where serialize :: a -> [Word8] From 87f629282f815381aa3ffe2503e8b8e58bfb8e97 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 11:28:57 +0200 Subject: [PATCH 6/8] fixed order of compound AST types --- src/Ast.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ast.hs b/src/Ast.hs index fdf088b..794b030 100644 --- a/src/Ast.hs +++ b/src/Ast.hs @@ -6,8 +6,8 @@ type Identifier = String data ParameterDeclaration = ParameterDeclaration DataType Identifier deriving (Show, Eq) data VariableDeclaration = VariableDeclaration DataType Identifier (Maybe Expression) deriving (Show, Eq) -data Class = Class DataType [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq) data MethodDeclaration = MethodDeclaration DataType Identifier [ParameterDeclaration] Statement deriving (Show, Eq) +data Class = Class DataType [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq) data Statement = If Expression Statement (Maybe Statement) @@ -23,11 +23,11 @@ data StatementExpression = Assignment Expression Expression | ConstructorCall DataType [Expression] | MethodCall Expression Identifier [Expression] - | TypedStatementExpression DataType StatementExpression | PostIncrement Expression | PostDecrement Expression | PreIncrement Expression | PreDecrement Expression + | TypedStatementExpression DataType StatementExpression deriving (Show, Eq) data BinaryOperator From 1eaeffb9a46b2d667b7dd428d7b76f48e49587d9 Mon Sep 17 00:00:00 2001 From: Marvin Schlegel Date: Wed, 26 Jun 2024 09:17:21 +0200 Subject: [PATCH 7/8] parser add for statements --- Test/TestParser.hs | 42 ++++++++++++++++++++++++++++++++++++++++- src/Parser/JavaParser.y | 17 +++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Test/TestParser.hs b/Test/TestParser.hs index 3d05001..2767c15 100644 --- a/Test/TestParser.hs +++ b/Test/TestParser.hs @@ -238,6 +238,40 @@ testStatementPreIncrement = TestCase $ assertEqual "expect increment" [StatementExpressionStatement $ PostIncrement $ Reference "a"] $ parseStatement [IDENTIFIER "a",INCREMENT,SEMICOLON] +testForLoop = TestCase $ + assertEqual "expect for loop" [Block [ + LocalVariableDeclaration (VariableDeclaration "int" "i" (Just (IntegerLiteral 0))), + While (BinaryOperation CompareLessThan (Reference "i") (IntegerLiteral 3)) (Block [Block [], StatementExpressionStatement (PostIncrement (Reference "i"))]) + ]] $ + parseStatement [FOR,LBRACE,INT,IDENTIFIER "i",ASSIGN,INTEGERLITERAL 0,SEMICOLON,IDENTIFIER "i",LESS,INTEGERLITERAL 3,SEMICOLON,IDENTIFIER "i",INCREMENT,RBRACE,LBRACKET,RBRACKET] +testForLoopExpressionlistInInit = TestCase $ + assertEqual "expect expressionlist in init part of for loop" [Block [ + StatementExpressionStatement (PostIncrement (Reference "i")), + While (BinaryOperation CompareLessThan (Reference "i") (IntegerLiteral 3)) (Block [Block [], StatementExpressionStatement (PostIncrement (Reference "i"))]) + ]] $ + parseStatement [FOR,LBRACE,IDENTIFIER "i",INCREMENT,SEMICOLON,IDENTIFIER "i",LESS,INTEGERLITERAL 3,SEMICOLON,IDENTIFIER "i",INCREMENT,RBRACE,LBRACKET,RBRACKET] +testForLoopMultipleUpdateExpressions = TestCase $ + assertEqual "expect for loop with multiple update statements" [Block [ + LocalVariableDeclaration (VariableDeclaration "int" "i" (Just (IntegerLiteral 0))), + While (BinaryOperation CompareLessThan (Reference "i") (IntegerLiteral 3)) (Block [Block [], StatementExpressionStatement (PostIncrement (Reference "i")), StatementExpressionStatement (PostIncrement (Reference "k"))]) + ]] $ + parseStatement [FOR,LBRACE,INT,IDENTIFIER "i",ASSIGN,INTEGERLITERAL 0,SEMICOLON,IDENTIFIER "i",LESS,INTEGERLITERAL 3,SEMICOLON,IDENTIFIER "i",INCREMENT,COMMA,IDENTIFIER "k",INCREMENT,RBRACE,LBRACKET,RBRACKET] +testForLoopEmptyFirstPart = TestCase $ + assertEqual "expect for loop with empty init part" [Block [ + While (BinaryOperation CompareLessThan (Reference "i") (IntegerLiteral 3)) (Block [Block [], StatementExpressionStatement (PostIncrement (Reference "i"))]) + ]] $ + parseStatement [FOR,LBRACE,SEMICOLON,IDENTIFIER "i",LESS,INTEGERLITERAL 3,SEMICOLON,IDENTIFIER "i",INCREMENT,RBRACE,LBRACKET,RBRACKET] +testForLoopEmtpySecondPart = TestCase $ + assertEqual "expect for loop with empty expresion part" [Block [ + While (BooleanLiteral True) (Block [Block [], StatementExpressionStatement (PostIncrement (Reference "i"))]) + ]] $ + parseStatement [FOR,LBRACE,SEMICOLON,SEMICOLON,IDENTIFIER "i",INCREMENT,RBRACE,LBRACKET,RBRACKET] +testForLoopEmtpy = TestCase $ + assertEqual "expect empty for loop" [Block [While (BooleanLiteral True) (Block [Block []])]] $ + parseStatement [FOR,LBRACE,SEMICOLON,SEMICOLON,RBRACE,LBRACKET,RBRACKET] + + + tests = TestList [ testSingleEmptyClass, @@ -308,5 +342,11 @@ tests = TestList [ testStatementMethodCallNoParams, testStatementConstructorCall, testStatementConstructorCallWithArgs, - testStatementPreIncrement + testStatementPreIncrement, + testForLoop, + testForLoopExpressionlistInInit, + testForLoopMultipleUpdateExpressions, + testForLoopEmptyFirstPart, + testForLoopEmtpySecondPart, + testForLoopEmtpy ] \ No newline at end of file diff --git a/src/Parser/JavaParser.y b/src/Parser/JavaParser.y index d7dc862..3e011d1 100644 --- a/src/Parser/JavaParser.y +++ b/src/Parser/JavaParser.y @@ -75,6 +75,7 @@ import Parser.Lexer OREQUAL { OREQUAL } COLON { COLON } LESS { LESS } + FOR { FOR } %% compilationunit : typedeclarations { $1 } @@ -204,6 +205,7 @@ statement : statementwithouttrailingsubstatement{ $1 } -- statement retu | ifthenstatement { [$1] } | ifthenelsestatement { [$1] } | whilestatement { [$1] } + | forstatement { [$1] } expression : assignmentexpression { $1 } @@ -224,6 +226,21 @@ ifthenelsestatement : IF LBRACE expression RBRACE statementnoshortif ELSE state whilestatement : WHILE LBRACE expression RBRACE statement { While $3 (Block $5) } +forstatement : FOR LBRACE forinit optionalexpression forupdate statement { Block ($3 ++ [While ($4) (Block ($6 ++ $5))]) } + +forinit : statementexpressionlist SEMICOLON { $1 } + | localvariabledeclaration SEMICOLON { $1 } + | SEMICOLON { [] } + +optionalexpression : expression SEMICOLON { $1 } + | SEMICOLON { BooleanLiteral True } + +forupdate : statementexpressionlist RBRACE { $1 } + | RBRACE { [] } + +statementexpressionlist : statementexpression { [StatementExpressionStatement $1] } + | statementexpressionlist COMMA statementexpression { $1 ++ [StatementExpressionStatement $3] } + assignmentexpression : conditionalexpression { $1 } | assignment { StatementExpressionExpression $1 } From 6b4b9b496d33b46e6a81a7888fabcf5c66a73f64 Mon Sep 17 00:00:00 2001 From: mrab Date: Wed, 26 Jun 2024 09:42:07 +0200 Subject: [PATCH 8/8] fixed local variable declaration being ignore inside for loop --- Test/JavaSources/Main.java | 2 ++ Test/JavaSources/TestLoop.java | 12 ++++++++++++ src/ByteCode/Assembler.hs | 14 +++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 Test/JavaSources/TestLoop.java diff --git a/Test/JavaSources/Main.java b/Test/JavaSources/Main.java index b912e91..e3b7b76 100644 --- a/Test/JavaSources/Main.java +++ b/Test/JavaSources/Main.java @@ -15,6 +15,7 @@ public class Main { TestMultipleClasses multipleClasses = new TestMultipleClasses(); TestRecursion recursion = new TestRecursion(10); TestMalicious malicious = new TestMalicious(); + TestLoop loop = new TestLoop(); // constructing a basic class works assert empty != null; @@ -32,6 +33,7 @@ public class Main { assert recursion.child.child.child.child.child.value == 5; // self-referencing methods work. assert recursion.fibonacci(15) == 610; + assert loop.factorial(5) == 120; // intentionally dodgy expressions work assert malicious.assignNegativeIncrement(42) == -42; assert malicious.tripleAddition(1, 2, 3) == 6; diff --git a/Test/JavaSources/TestLoop.java b/Test/JavaSources/TestLoop.java new file mode 100644 index 0000000..40491fb --- /dev/null +++ b/Test/JavaSources/TestLoop.java @@ -0,0 +1,12 @@ +public class TestLoop { + public int factorial(int n) + { + int tally = 1; + for(int i = 1; i <= n; i++) + { + tally *= i; + } + + return tally; + } +} diff --git a/src/ByteCode/Assembler.hs b/src/ByteCode/Assembler.hs index 101d7aa..555569b 100644 --- a/src/ByteCode/Assembler.hs +++ b/src/ByteCode/Assembler.hs @@ -221,26 +221,26 @@ assembleStatement (constants, ops, lvars) (TypedStatement _ (Block statements)) assembleStatement (constants, ops, lvars) (TypedStatement dtype (If expr if_stmt else_stmt)) = let (constants_cmp, ops_cmp, _) = assembleExpression (constants, [], lvars) expr - (constants_ifa, ops_ifa, _) = assembleStatement (constants_cmp, [], lvars) if_stmt + (constants_ifa, ops_ifa, lvars_ifa) = assembleStatement (constants_cmp, [], lvars) if_stmt (constants_elsea, ops_elsea, _) = case else_stmt of - Nothing -> (constants_ifa, [], lvars) - Just stmt -> assembleStatement (constants_ifa, [], lvars) stmt + Nothing -> (constants_ifa, [], lvars_ifa) + Just stmt -> assembleStatement (constants_ifa, [], lvars_ifa) stmt -- +6 because we insert 2 gotos, one for if, one for else if_length = sum (map opcodeEncodingLength ops_ifa) -- +3 because we need to account for the goto in the if statement. else_length = sum (map opcodeEncodingLength ops_elsea) in case dtype of - "void" -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 6)] ++ ops_ifa ++ [Opgoto (else_length + 3)] ++ ops_elsea, lvars) - _ -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 3)] ++ ops_ifa ++ ops_elsea, lvars) + "void" -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 6)] ++ ops_ifa ++ [Opgoto (else_length + 3)] ++ ops_elsea, lvars_ifa) + _ -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 3)] ++ ops_ifa ++ ops_elsea, lvars_ifa) assembleStatement (constants, ops, lvars) (TypedStatement _ (While expr stmt)) = let (constants_cmp, ops_cmp, _) = assembleExpression (constants, [], lvars) expr - (constants_stmta, ops_stmta, _) = assembleStatement (constants_cmp, [], lvars) stmt + (constants_stmta, ops_stmta, lvars_stmta) = assembleStatement (constants_cmp, [], lvars) stmt -- +3 because we insert 2 gotos, one for the comparison, one for the goto back to the comparison stmt_length = sum (map opcodeEncodingLength ops_stmta) + 6 entire_length = stmt_length + sum (map opcodeEncodingLength ops_cmp) in - (constants_stmta, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq stmt_length] ++ ops_stmta ++ [Opgoto (-entire_length)], lvars) + (constants_stmta, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq stmt_length] ++ ops_stmta ++ [Opgoto (-entire_length)], lvars_stmta) assembleStatement (constants, ops, lvars) (TypedStatement _ (LocalVariableDeclaration (VariableDeclaration dtype name expr))) = let isPrimitive = elem dtype ["char", "boolean", "int"]