Skip to content

Commit 0ee7de2

Browse files
author
Louis Jenkins
committed
Finally got output and conditionals working, finalizing my work and prettifying it (documentation)
1 parent 323b9e5 commit 0ee7de2

File tree

5 files changed

+45
-28
lines changed

5 files changed

+45
-28
lines changed

ClassFile/Class_File.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module DataTypes.Class_File where
1919
let classFile = parseClassFile r0
2020
Prelude.putStrLn $ show classFile
2121
Prelude.putStrLn "Initializing Runtime Environment..."
22-
env <- VirtualMachine.Environment.init
22+
env <- VirtualMachine.Environment.init False
2323
Prelude.putStrLn "Loading bootstrap class..."
2424
loadClass env classFile
2525
Prelude.putStrLn "Starting Virtual Machine..."

VirtualMachine/ByteCode.hs

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,45 @@ module VirtualMachine.ByteCode where
1313
loadConstantPool env idx = (!! idx) . constant_pool <$> readIORef (current_class env) >>= toValue
1414
where
1515
toValue :: CP_Info -> IO Value
16-
toValue info = return $ case tag info of
17-
1 -> VString . show . utf8_bytes $ info
18-
3 -> VInt . fromIntegral . bytes $ info
19-
4 -> VFloat . wordToFloat . bytes $ info
20-
5 -> VLong (fromIntegral . high_bytes $ info) `shift` 32 .|. (fromIntegral . low_bytes $ info)
21-
6 -> VDouble . wordToDouble $ (fromIntegral . high_bytes $ info) `shift` 32 .|. (fromIntegral . low_bytes $ info)
16+
toValue info = case tag info of
17+
3 -> return . VInt . fromIntegral . bytes $ info
18+
4 -> return . VFloat . wordToFloat . bytes $ info
19+
5 -> return . VLong $ (fromIntegral . high_bytes $ info) `shift` 32 .|. (fromIntegral . low_bytes $ info)
20+
6 -> return . VDouble . wordToDouble $ (fromIntegral . high_bytes $ info) `shift` 32 .|. (fromIntegral . low_bytes $ info)
21+
8 -> readIORef (current_class env) >>= \c -> (return . VString . show . utf8_bytes) (constant_pool c !! (fromIntegral . string_index $ info))
2222
_ -> error $ "Bad Tag: " ++ show (tag info)
2323

24+
{- Starting point of execution of ByteCode isntructions -}
2425
execute :: Runtime_Environment -> IO ()
25-
execute env = head <$> readIORef (stack env) >>= \frame -> debugFrame frame >>= putStrLn
26+
execute env = head <$> readIORef (stack env) -- Take the head of the stack (current stack frame)
27+
>>= \frame -> when (debug_mode env) (debugFrame frame >>= putStrLn) -- Optional Debug
2628
>> readIORef frame >>= \f -> readIORef (program_counter . code_segment $ f) >>= \pc ->
27-
unless (fromIntegral pc >= length (byte_code . code_segment $ f)) $ getNextBC frame
28-
>>= execute' frame >> execute env
29+
unless (fromIntegral pc >= length (byte_code . code_segment $ f)) -- While valid program_counter
30+
(getNextBC frame >>= execute' frame >> execute env) -- Execute instruction
2931
where
32+
-- The main dispatcher logic
3033
execute' :: StackFrame -> ByteCode -> IO ()
3134
execute' frame bc
3235
-- NOP
3336
| bc == 0 = return ()
3437
-- Constants
3538
| bc >= 1 && bc <= 15 = constOp frame bc
36-
-- BIPUSH BYTE
37-
| bc == 16 = getNextBC frame >>= pushOp frame . fromIntegral
38-
-- SIPUSH BYTE1 BYTE2
39-
| bc == 17 = replicateM 2 (getNextBC frame) >>= \(b1:b2:_) -> pushOp frame (fromIntegral b1 `shift` 8 .|. fromIntegral b2)
40-
-- LDC IDX
41-
| bc == 18 = getNextBC frame >>= loadConstantPool env . fromIntegral >>= pushOp frame
42-
-- LDC2* IDX1 IDX2
43-
| bc == 19 || bc == 20 = replicateM 2 (getNextBC frame) >>= \(i1:i2:_) ->
44-
loadConstantPool env (fromIntegral i1 `shift` 8 .|. fromIntegral i2) >>= pushOp frame
39+
-- Push raw byte(s)
40+
| bc == 16 || bc == 17 =
41+
-- 0x10 pushes a single byte, but 0x11 pushes a short
42+
(if bc == 16 then fromIntegral <$> getNextBC frame else getNextShort frame)
43+
>>= pushOp frame . fromIntegral
44+
-- Load constant pool constant
45+
| bc >= 18 && bc <= 20 =
46+
-- Only 0x12 uses only one byte, so we add a special case
47+
(if bc == 18 then fromIntegral <$> getNextBC frame else getNextShort frame)
48+
>>= loadConstantPool env . fromIntegral >>= pushOp frame
4549
-- Loads
4650
| bc >= 21 && bc <= 53 = loadOp frame bc
4751
-- Stores
4852
| bc >= 54 && bc <= 86 = storeOp frame bc
53+
-- Special Case: 'dup' is used commonly but ignored, so we have to stub it
54+
| bc == 89 = return ()
4955
-- Math
5056
| bc >= 96 && bc <= 132 = mathOp frame bc
5157
-- Conditionals
@@ -62,15 +68,19 @@ module VirtualMachine.ByteCode where
6268
runtimeStub :: Runtime_Environment -> StackFrame -> ByteCode -> IO ()
6369
runtimeStub env frame bc
6470
-- getstatic: 2 bytes wide
65-
| bc == 178 = replicateM_ 2 (getNextBC frame)
71+
| bc == 178 = void $ getNextShort frame
6672
-- invokevirtual: (append, println). NOTE: MUST HAVE ONLY ONE PARAMETER ELSE UNDEFINED
6773
| bc == 182 = getNextShort frame >>= \method_idx -> (readIORef . current_class) env
6874
>>= \c -> case methodName c method_idx of
69-
"append" -> (appendValues <$> popOp frame <*> popOp frame) >>= pushOp frame
70-
where
71-
appendValues x y = VString $ show x ++ show y
75+
"append" -> ((\x y -> VString $ show y ++ show x) <$> popOp frame <*> popOp frame)
76+
>>= pushOp frame
7277
"println" -> popOp frame >>= print
78+
"toString" -> return ()
7379
_ -> error "Bad Method Call!"
80+
-- Used to call <init>, but we don't deal with that... yet
81+
| bc == 183 = void $ getNextShort frame
82+
-- Laziness: 'new' must refer to StringBuilder... otherwise it's undefined anyway
83+
| bc == 187 = getNextShort frame >> pushOp frame (VString "")
7484
where
7585
methodName :: Class -> Word16 -> String
7686
methodName clazz method_idx = let
@@ -163,7 +173,7 @@ module VirtualMachine.ByteCode where
163173
pushCmp :: IO ()
164174
pushCmp = (flip compare <$> popOp frame <*> popOp frame) >>= pushOrd
165175
pushOrd :: Ordering -> IO ()
166-
pushOrd ord = pushOp frame (VInt (ordToInt ord)) >> debugFrame frame >>= putStrLn
176+
pushOrd ord = pushOp frame (VInt (ordToInt ord))
167177
cmpJmp :: Word16 -> IO ()
168178
cmpJmp
169179
| bc == 153 || bc == 159 || bc == 165 = flip condJmp [EQ]

VirtualMachine/Debug.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module VirtualMachine.Debug where
66
import Data.Array.MArray
77
import Control.Monad
88
import Data.List
9-
import System.Console.ANSI
9+
import System.Console.ANSI
1010
import Text.Printf
1111

1212
debugFrame :: StackFrame -> IO String

VirtualMachine/Environment.hs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ module VirtualMachine.Environment where
88
import VirtualMachine.ByteCode
99
import Data.Maybe
1010

11-
init :: IO Runtime_Environment
12-
init = Environment <$> newIORef undefined <*> newIORef Map.empty <*> newIORef []
11+
{- Starting point for the runtime, invoked to instantiate the runtime environment -}
12+
init :: Bool -> IO Runtime_Environment
13+
init debug = Environment <$> newIORef undefined -- 'current_class' is not available yet
14+
<*> newIORef Map.empty -- 'class_map' is originally empty
15+
<*> newIORef [] -- The 'stack' is initially empty
16+
<*> return debug -- 'debug' is used for conditional logging
1317

18+
{- Minimal bootstrap class loader -}
1419
loadClass :: Runtime_Environment -> ClassFile -> IO ()
1520
loadClass env cf = toClass cf >>= \c -> modifyIORef' (class_map env) (Map.insert classString c)
1621
>> writeIORef (current_class env) c
@@ -22,6 +27,7 @@ module VirtualMachine.Environment where
2227
name = cp !! fromIntegral (name_index class_info)
2328
in show $ utf8_bytes name
2429

30+
{- Primer for the runtime, which sets up and executes the 'main' method -}
2531
start :: Runtime_Environment -> IO ()
2632
start env = putStrLn "Starting..." >> (fromJust . Map.lookup "main") <$> ((snd . head . Map.toList)
2733
<$> readIORef (class_map env) >>= readIORef . method_map)

VirtualMachine/Types.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ module VirtualMachine.Types where
144144
data Runtime_Environment = Environment {
145145
current_class :: IORef Class,
146146
class_map :: IORef (Map String Class),
147-
stack :: Stack
147+
stack :: Stack,
148+
debug_mode :: Bool
148149
}
149150

150151
-- newtype Runtime = Runtime { runRT :: Runtime_Environment -> IO () }

0 commit comments

Comments
 (0)