fixed method/constructor overloading in bytecode generator
This commit is contained in:
parent
dee1fcb2df
commit
0e1f31080e
@ -16,6 +16,7 @@ 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;
|
||||
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ executable compiler
|
||||
other-modules: Parser.Lexer,
|
||||
Parser.JavaParser,
|
||||
Ast,
|
||||
Example,
|
||||
Typecheck,
|
||||
ByteCode.Util,
|
||||
ByteCode.ByteUtil,
|
||||
|
@ -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" "<init>" 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" "<init>" 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
|
||||
constructorsAssembled
|
@ -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 == "<init>") pre = pre
|
||||
| otherwise = pre ++ [MethodDeclaration "void" "<init>" [] (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" "<init>" params (TypedStatement "void" (Block statements)) -> MethodDeclaration "void" "<init>" 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
|
||||
|
Loading…
Reference in New Issue
Block a user