@@ -25,7 +25,7 @@ module VirtualMachine.ByteCode where
2525 execute :: Runtime_Environment -> IO ()
2626 execute env = head <$> readIORef (stack env) -- Take the head of the stack (current stack frame)
2727 >>= \ frame -> when (debug_mode env) (debugFrame frame >>= putStrLn ) -- Optional Debug
28- >> getPC' frame >>= \ pc -> maxPC frame >>= \ max_pc ->
28+ >> getPC' frame >>= \ pc -> maxPC frame >>= \ max_pc -> -- Program Counters for comparison
2929 -- While valid program_counter, execute instruction
3030 unless (pc >= max_pc) (getNextBC frame >>= execute' frame >> execute env)
3131 where
@@ -73,15 +73,15 @@ module VirtualMachine.ByteCode where
7373 -- invokevirtual: (append, println). NOTE: MUST HAVE ONLY ONE PARAMETER ELSE UNDEFINED
7474 | bc == 182 = getNextShort frame >>= \ method_idx -> (readIORef . current_class) env
7575 >>= \ c -> case methodName c method_idx of
76- " append" -> ((\ x y -> VString $ show y ++ show x) <$> popOp frame <*> popOp frame)
77- >>= pushOp frame
78- " println" -> popOp frame >>= print
79- " toString" -> return ()
76+ " append" -> ((\ x y -> VString $ show y ++ show x) <$> popOp frame <*> popOp frame) >>= pushOp frame
77+ " println" -> popOp frame >>= print -- Defer I/O to Haskell
78+ " toString" -> return () -- StringBuilder object is already a 'VString'
8079 _ -> error " Bad Method Call!"
81- -- Used to call <init>, but we don't deal with that... yet
80+ -- Invokespecial is used to call <init>, but we don't deal with that... yet
8281 | bc == 183 = void $ getNextShort frame
8382 -- Laziness: 'new' must refer to StringBuilder... otherwise it's undefined anyway
8483 | bc == 187 = getNextShort frame >> pushOp frame (VString " " )
84+ | otherwise = error $ " Bad ByteCode Instruction: " ++ show bc
8585 where
8686 methodName :: Class -> Word16 -> String
8787 methodName clazz method_idx = let
@@ -91,35 +91,30 @@ module VirtualMachine.ByteCode where
9191 utf8_name = cpool !! fromIntegral (name_index name_and_type)
9292 in show . utf8_bytes $ utf8_name
9393
94- {-
95- Loads transfer values from a slot in the Local Variable array to the Operand
96- Stack. Note as well that we can safely ignore the second index for DWORD-sized
97- variables.
98- -}
94+ {- Loads from local_variables to operand_stack -}
9995 loadOp :: StackFrame -> ByteCode -> IO ()
10096 loadOp frame bc
101- -- Loads which have the index as the next bytecode instruction
102- | bc >= 21 && bc <= 25 = getNextBC frame >>= getLocal frame >>= pushOp frame
103- -- Loads which have a constant index (I.E: ILOAD_0 to ILOAD_3)
104- | bc >= 26 && bc <= 45 = getLocal frame ((bc - 26 ) `mod` 4 ) >>= pushOp frame
97+ -- Loads with the index as the next bytecode instruction
98+ | bc >= 21 && bc <= 25 = getNextBC frame >>= getLocal' frame >>= pushOp frame
99+ -- Loads with a constant index (I.E: ILOAD_0 to ILOAD_3 have indice 0 to 3 respectively )
100+ | bc >= 26 && bc <= 45 = getLocal' frame ((bc - 26 ) `mod` 4 ) >>= pushOp frame
105101 | otherwise = error $ " Bad ByteCode Instruction: " ++ show bc
106102
107- {-
108- Stores transfer the top value of the Operand Stack to an indice in the
109- Local Variable array. The JVM specification specifies that for DWORD sized
110- types (I.E: Long and Double) take up two slots in the Local Variable
111- array (the lower index contains the higher WORD, higher index contains the
112- lower WORD), so we store a dummy value (Null reference) in the higher index.
113- -}
103+ {- Stores from operand_stack to local_variables -}
114104 storeOp :: StackFrame -> ByteCode -> IO ()
115105 storeOp frame bc
116- -- Stores which have the Index as the next bytecode instruction
106+ -- Stores with the index as the next bytecode instruction
117107 | bc >= 54 && bc <= 58 = popOp frame >>= \ op -> getNextBC frame >>= \ idx -> putLocal frame idx op
118108 >> when (bc == 55 || bc == 57 ) (putLocal frame (idx + 1 ) (VReference 0 ))
119- -- Stores which have a constant index
120- | bc >= 59 && bc <= 78 = popOp frame >>= putLocal frame ((bc - 59 ) `mod` 4 )
109+ -- Stores with a constant index (I.E: ISTORE_0 to ISTORE_3 have indice 0 to 3 respectively)
110+ | bc >= 59 && bc <= 78 = let idx = ((bc - 59 ) `mod` 4 ) in
111+ popOp frame >>= putLocal frame idx
112+ -- Special Case: Double word-sized variables, such as 'long' and 'double' must
113+ -- take up two slots. We fit both types in a single slot, but the compiler generates
114+ -- ByteCode that are sensitive to these invariants, so we insert a dummy null reference
115+ -- in the second slot to restore that balance.
121116 >> when ((bc >= 63 && bc <= 66 ) || (bc >= 71 && bc <= 74 ))
122- (putLocal frame (((bc - 59 ) `mod` 4 ) + 1 ) (VReference 0 ))
117+ (( putLocal frame $ idx + 1 ) (VReference 0 ))
123118 | otherwise = error $ " Bad ByteCode Instruction: " ++ show bc
124119
125120
@@ -162,7 +157,8 @@ module VirtualMachine.ByteCode where
162157 applyBinaryOp :: (Operand -> Operand -> Operand ) -> IO ()
163158 applyBinaryOp f = replicateM 2 (popOp frame) >>= \ (x: y: _) -> pushOp frame (f y x)
164159 increment :: IO ()
165- increment = getNextBC frame >>= \ idx -> getLocal frame idx >>= \ l -> getNextBC frame >>= \ n -> putLocal frame idx (l + fromIntegral n)
160+ increment = -- 'iinc' has local variable index as first, with value as second indice
161+ join $ modifyLocal frame <$> getNextBC frame <*> ((+) . fromIntegral <$> getNextBC frame)
166162
167163 cmpOp :: StackFrame -> ByteCode -> IO ()
168164 cmpOp frame bc
0 commit comments