diff --git a/Test/JavaSources/Main.java b/Test/JavaSources/Main.java index e3b7b76..acf0eed 100644 --- a/Test/JavaSources/Main.java +++ b/Test/JavaSources/Main.java @@ -16,7 +16,8 @@ public class Main { TestRecursion recursion = new TestRecursion(10); TestMalicious malicious = new TestMalicious(); TestLoop loop = new TestLoop(); - + TestMethodOverload overload = new TestMethodOverload(); + // constructing a basic class works assert empty != null; // initializers (and default initializers to 0/null) work @@ -34,6 +35,12 @@ public class Main { // self-referencing methods work. assert recursion.fibonacci(15) == 610; assert loop.factorial(5) == 120; + // methods with the same name but different parameters work + assert overload.MethodOverload() == 42; + assert overload.MethodOverload(15) == 42 + 15; + // constructor overloading works, too. + assert (new TestConstructorOverload()).a == 42; + assert (new TestConstructorOverload(12)).a == 12; // intentionally dodgy expressions work assert malicious.assignNegativeIncrement(42) == -42; assert malicious.tripleAddition(1, 2, 3) == 6; diff --git a/Test/JavaSources/TestConstructorOverload.java b/Test/JavaSources/TestConstructorOverload.java index 44522bb..ee6422a 100644 --- a/Test/JavaSources/TestConstructorOverload.java +++ b/Test/JavaSources/TestConstructorOverload.java @@ -1,13 +1,12 @@ public class TestConstructorOverload { + public int a = 42; + TestConstructorOverload() { + // nothing here, so a will assume the default value 42. } TestConstructorOverload(int a) { - } - - public void test() { - int a = 3; - TestConstructorOverload test = new TestConstructorOverload(a); + this.a = a; } } diff --git a/Test/JavaSources/TestMethodOverload.java b/Test/JavaSources/TestMethodOverload.java index 6fe9877..5a7d976 100644 --- a/Test/JavaSources/TestMethodOverload.java +++ b/Test/JavaSources/TestMethodOverload.java @@ -1,14 +1,10 @@ public class TestMethodOverload { - public void MethodOverload() { + public int MethodOverload() { + return 42; } - public void MethodOverload(int a) { + public int MethodOverload(int a) { + return 42 + a; } - - public void test() { - int a = 3; - MethodOverload(a); - } - } diff --git a/project.cabal b/project.cabal index baec851..abe3e09 100644 --- a/project.cabal +++ b/project.cabal @@ -18,7 +18,6 @@ executable compiler other-modules: Parser.Lexer, Parser.JavaParser, Ast, - Example, Typecheck, ByteCode.Util, ByteCode.ByteUtil, diff --git a/src/ByteCode/Builder.hs b/src/ByteCode/Builder.hs index b717247..9e2e804 100644 --- a/src/ByteCode/Builder.hs +++ b/src/ByteCode/Builder.hs @@ -56,10 +56,13 @@ methodBuilder (MethodDeclaration returntype name parameters statement) input = l methods = methods input ++ [method] } +constructorBuilder :: ClassFileBuilder ConstructorDeclaration +constructorBuilder (ConstructorDeclaration name parameters statement) = methodBuilder (MethodDeclaration "void" "" parameters statement) + methodAssembler :: ClassFileBuilder MethodDeclaration methodAssembler (MethodDeclaration returntype name parameters statement) input = let - methodConstantIndex = findMethodIndex input name + methodConstantIndex = findMethodIndex input (MethodDeclaration returntype name parameters statement) in case methodConstantIndex of Nothing -> error ("Cannot find method entry in method pool for method: " ++ name) Just index -> let @@ -84,9 +87,12 @@ methodAssembler (MethodDeclaration returntype name parameters statement) input = methods = pre ++ (assembledMethod : post) } +constructorAssembler :: ClassFileBuilder ConstructorDeclaration +constructorAssembler (ConstructorDeclaration name parameters statement) = methodAssembler (MethodDeclaration "void" "" parameters statement) + classBuilder :: ClassFileBuilder Class -classBuilder (Class name methods fields) _ = let +classBuilder (Class name constructors methods fields) _ = let baseConstants = [ ClassInfo 4, MethodRefInfo 1 3, @@ -108,15 +114,15 @@ classBuilder (Class name methods fields) _ = let attributes = [] } - -- if a class has no constructor, inject an empty one. - methodsWithInjectedConstructor = injectDefaultConstructor methods -- for every constructor, prepend all initialization assignments for fields. - methodsWithInjectedInitializers = injectFieldInitializers name fields methodsWithInjectedConstructor + constructorsWithInitializers = injectFieldInitializers name fields constructors - -- add fields, then method bodies to the classfile. After all referable names are known, - -- assemble the methods into bytecode. - classFileWithFields = foldr fieldBuilder nakedClassFile fields - classFileWithMethods = foldr methodBuilder classFileWithFields methodsWithInjectedInitializers - classFileWithAssembledMethods = foldr methodAssembler classFileWithMethods methodsWithInjectedInitializers + -- add fields, then method bodies, then constructor bodies to the classfile. After all referable names are known, + -- assemble the methods and constructors into bytecode. + fieldsAdded = foldr fieldBuilder nakedClassFile fields + methodsAdded = foldr methodBuilder fieldsAdded methods + constructorsAdded = foldr constructorBuilder methodsAdded constructorsWithInitializers + methodsAssembled = foldr methodAssembler constructorsAdded methods + constructorsAssembled = foldr constructorAssembler methodsAssembled constructorsWithInitializers in - classFileWithAssembledMethods \ No newline at end of file + constructorsAssembled \ No newline at end of file diff --git a/src/ByteCode/Util.hs b/src/ByteCode/Util.hs index 7f48b17..97e3721 100644 --- a/src/ByteCode/Util.hs +++ b/src/ByteCode/Util.hs @@ -1,3 +1,5 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use lambda-case" #-} module ByteCode.Util where import Data.Int @@ -131,11 +133,12 @@ comparisonOffset anything_else = Nothing isComparisonOperation :: Operation -> Bool isComparisonOperation op = isJust (comparisonOffset op) -findMethodIndex :: ClassFile -> String -> Maybe Int -findMethodIndex classFile name = let +findMethodIndex :: ClassFile -> MethodDeclaration -> Maybe Int +findMethodIndex classFile (MethodDeclaration rtype name params stmt) = let constants = constantPool classFile + descriptor = methodDescriptor (MethodDeclaration rtype name params stmt) in - findIndex (\method -> memberInfoIsMethod constants method && memberInfoName constants method == name) (methods classFile) + findIndex (\method -> memberInfoIsMethod constants method && memberInfoName constants method == name && memberInfoDescriptor constants method == descriptor) (methods classFile) findClassIndex :: [ConstantInfo] -> String -> Maybe Int findClassIndex constants name = let @@ -211,16 +214,11 @@ findMemberIndex constants (cname, fname, ftype) = let allMembers = getKnownMembers constants desiredMember = find (\(index, (c, f, ft)) -> (c, f, ft) == (cname, fname, ftype)) allMembers in - fmap (\(index, _) -> index) desiredMember + fmap fst desiredMember -injectDefaultConstructor :: [MethodDeclaration] -> [MethodDeclaration] -injectDefaultConstructor pre - | any (\(MethodDeclaration _ name _ _) -> name == "") pre = pre - | otherwise = pre ++ [MethodDeclaration "void" "" [] (TypedStatement "void" (Block []))] - -injectFieldInitializers :: String -> [VariableDeclaration] -> [MethodDeclaration] -> [MethodDeclaration] -injectFieldInitializers classname vars pre = let - initializers = mapMaybe (\(variable) -> case variable of +injectFieldInitializers :: String -> [VariableDeclaration] -> [ConstructorDeclaration] -> [ConstructorDeclaration] +injectFieldInitializers classname vars constructors = let + initializers = mapMaybe (\variable -> case variable of VariableDeclaration dtype name (Just initializer) -> Just ( TypedStatement dtype ( StatementExpressionStatement ( @@ -235,10 +233,11 @@ injectFieldInitializers classname vars pre = let otherwise -> Nothing ) vars in - map (\method -> case method of - MethodDeclaration "void" "" params (TypedStatement "void" (Block statements)) -> MethodDeclaration "void" "" params (TypedStatement "void" (Block (initializers ++ statements))) - _ -> method - ) pre + map (\con -> let + ConstructorDeclaration classname params (TypedStatement "void" (Block statements)) = con + in + ConstructorDeclaration classname params (TypedStatement "void" (Block (initializers ++ statements))) + ) constructors -- effect of one instruction/operation on the stack operationStackCost :: [ConstantInfo] -> Operation -> Int