@@ -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 ]
0 commit comments