<commit_before>module Javelin.Runtime.Instructions where

import           Control.Monad.State.Lazy   (State, runState, state)
import           Data.Bits                  (xor, (.&.), (.|.))
import qualified Data.Map.Lazy              as Map (Map, fromList, lookup, (!))
import           Data.Word                  (Word16, Word32, Word64, Word8)
import           Javelin.Runtime.Structures
import           Javelin.Runtime.Thread
import           Javelin.Runtime.LLI.ClassPath (getClassSourcesLayout)
import           Control.Monad.Trans.Except (runExceptT)
import           Data.Array.IArray          (array)
import           Debug.Trace
import           System.Exit                (die)
import qualified Javelin.Runtime.LLI.LinkingInitializing as LI (init)
import           Javelin.ByteCode.Data      (Instruction(..))
import           System.IO                  (writeFile)
import           Flow

--stack exec javelin jvm test.App /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar:main 1
--runJVM "/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar:main" "test.App" []

-- We need to start eexuting commands in main method of main class so we have to make it look like
-- someone called 'invokestatic' on main method of main class: the class is loaded, there is a frame with main class id in it etc

---- 1. enable some sort of logging

console :: Show a => String -> a -> IO ()
console x a = putStrLn $ x ++ ": " ++ (show a)

dump :: Show a => String -> a -> IO ()
dump file a = writeFile ("./logs/" ++ file) (show a)

runJVM :: String -> String -> [String] -> IO ()
runJVM classPath mainClass args =
  let main = map (\c -> if c == '.' then '/' else c) mainClass
  in do
    putStrLn ""
    putStrLn "_______ Starting JVM ________"
    console "Main class arg" mainClass
    console "Main class" main
    maybeCPLayout <- runExceptT $ getClassSourcesLayout classPath
    case maybeCPLayout of
      Left error -> console "Failed while loading class path" error
      Right cpLayout -> console "Classpath" (_classPath cpLayout) >> dump "classpath.log" cpLayout >>
        case (Map.!) (_classes cpLayout) main of
          JarFile path -> die "Not implemented yet: running JVM from a main class inside jar file"
          ClassFile path -> do
            console "Main class found in class file" path
            let classId = ClassId BootstrapClassLoader main
            mainClassInit <- runExceptT $ LI.init classId (newRuntime cpLayout)
            case mainClassInit of
              Left err -> die $ show err
              Right rt -> case createMainFrame rt classId of
                Right frame -> runThread 0 $ Thread [frame] rt
                err -> die $ show err

-- global plan:
-- 1. create a small snippit
-- 2. do everything for a single snippet: make it work
-- 3. write an acceptance test for the working snippet
-- 4. write unit test for new code
-- 6. do all similar functionality (similar commands)
-- 7. new acceptance & unit tests for 6
-- 8. required updates of testing infrastructure
-- goto 1: next snippet

-- current snippet:
-- int a = 2;
-- int b = 3;
-- int c = a + b;
-- System.out.println(c);

-- todo:
-- 1. implement: getstatic, invokevirtual
-- 2. see what other instructions are required
-- 3. handle 'no more commands' in general and exit from main method in particular
-- 4. print state between executions
-- 5. testing: unit + acceptance

-- todo finish passing arguments -- but first finish 'currentFrame'
createMainFrame :: Runtime -> ClassId -> Either VMError Frame
createMainFrame rt classId = createFrame rt classId (PartReference "main" "(Ljava/lang/String[];)V")

-- todo finish: stack depth, local args size, arguments size
createFrame :: Runtime -> ClassId -> PartReference -> Either VMError Frame
createFrame rt classId methodReference =
  case getMethodBySignature rt classId methodReference of
    Right (index, method) -> let locals = Locals $ array (0, (fromIntegral $ localsSize method) - 1) []
                             in Right $ Frame 0 classId index locals  []
    Left err -> Left err


runThread :: Int -> Thread -> IO ()
runThread c thread
  | c > 100 = print "Exiting abnormally"
  | null $ frames thread = print "Exiting"
  | otherwise =
      case nextInstructionLine thread of
        Right instruction -> let execution = execute instruction
                                 (_, newThread) = runState execution thread
                             in runThread (c + 1) (incrementInstructionCounter newThread)
        Left error -> print error

incrementInstructionCounter :: Thread -> Thread
incrementInstructionCounter t@Thread{frames=(f@Frame{pc=pc}:fs)} = t{frames=f{pc=pc+1}:fs}

nextInstructionLine :: Thread -> Either VMError Instruction
nextInstructionLine Thread{frames=(Frame{pc=pc,
                                         currentClass=classId,
                                         currentMethod=methodIndex,
                                         locals=locals,
                                         operands=stack}):_,
                           runtime=rt} = do
  method <- getMethodByIndex rt classId methodIndex
  return $ instructions method !! pc

execute :: Instruction -> InstructionExecution
execute Nop = empty
-- push const <i> to stack
execute IConstM1 = iconst 0
execute IConst0 = iconst 0
execute IConst1 = iconst 1
execute IConst2 = iconst 2
execute IConst3 = iconst 3
execute IConst4 = iconst 4
execute IConst5 = iconst 5
-- pop int from stack, put to <i> local variable
execute IStore0 = iPopAndStoreAt 0
execute IStore1 = iPopAndStoreAt 1
execute IStore2 = iPopAndStoreAt 2
execute IStore3 = iPopAndStoreAt 3
execute (IStore localId) = iPopAndStoreAt localId
-- read int from local var <i>, push it to stack
execute (ILoad local)  = iLoadAndPushAt local
execute ILoad0 = iLoadAndPushAt 0
execute ILoad1 = iLoadAndPushAt 1<commit_msg>Cleanup<commit_after>module Javelin.Runtime.Instructions where

import           Control.Monad.State.Lazy   (State, runState, state)
import           Data.Bits                  (xor, (.&.), (.|.))
import qualified Data.Map.Lazy              as Map (Map, fromList, lookup, (!))
import           Data.Word                  (Word16, Word32, Word64, Word8)
import           Javelin.Runtime.Structures
import           Javelin.Runtime.Thread
import           Javelin.Runtime.LLI.ClassPath (getClassSourcesLayout)
import           Control.Monad.Trans.Except (runExceptT)
import           Data.Array.IArray          (array)
import           Debug.Trace
import           System.Exit                (die)
import qualified Javelin.Runtime.LLI.LinkingInitializing as LI (init)
import           Javelin.ByteCode.Data      (Instruction(..))
import           System.IO                  (writeFile)
import           Flow

--stack exec javelin jvm test.App /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar:main 1
--runJVM "/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar:main" "test.App" []

-- We need to start eexuting commands in main method of main class so we have to make it look like
-- someone called 'invokestatic' on main method of main class: the class is loaded, there is a frame with main class id in it etc

---- 1. enable some sort of logging

console :: Show a => String -> a -> IO ()
console x a = putStrLn $ x ++ ": " ++ (show a)

dump :: Show a => String -> a -> IO ()
dump file a = writeFile ("./logs/" ++ file) (show a)

runJVM :: String -> String -> [String] -> IO ()
runJVM classPath mainClass args =
  let main = map (\c -> if c == '.' then '/' else c) mainClass
  in do
    putStrLn ""
    putStrLn "_______ Starting JVM ________"
    console "Main class arg" mainClass
    console "Main class" main
    maybeCPLayout <- runExceptT $ getClassSourcesLayout classPath
    case maybeCPLayout of
      Left error -> console "Failed while loading class path" error
      Right cpLayout -> console "Classpath" (_classPath cpLayout) >> dump "classpath.log" cpLayout >>
        case (Map.!) (_classes cpLayout) main of
          JarFile path -> die "Not implemented yet: running JVM from a main class inside jar file"
          ClassFile path -> do
            console "Main class found in class file" path
            let classId = ClassId BootstrapClassLoader main
            mainClassInit <- runExceptT $ LI.init classId (newRuntime cpLayout)
            case mainClassInit of
              Left err -> die $ show err
              Right rt -> case createMainFrame rt classId of
                Right frame -> runThread 0 $ Thread [frame] rt
                err -> die $ show err

-- global plan:
-- 1. create a small snippit
-- 2. do everything for a single snippet: make it work
-- 3. write an acceptance test for the working snippet
-- 4. write unit test for new code
-- 6. do all similar functionality (similar commands)
-- 7. new acceptance & unit tests for 6
-- 8. required updates of testing infrastructure
-- goto 1: next snippet

-- current snippet:
-- int a = 2;
-- int b = 3;
-- int c = a + b;
-- System.out.println(c);

-- todo:
-- 1. implement: getstatic, invokevirtual
-- 2. see what other instructions are required
-- 3. make it work!
-- 4. print state between executions
-- 5. testing: unit + acceptance

-- todo finish passing arguments -- but first finish 'currentFrame'
createMainFrame :: Runtime -> ClassId -> Either VMError Frame
createMainFrame rt classId = createFrame rt classId (PartReference "main" "(Ljava/lang/String[];)V")

-- todo finish: stack depth, local args size, arguments size
createFrame :: Runtime -> ClassId -> PartReference -> Either VMError Frame
createFrame rt classId methodReference =
  case getMethodBySignature rt classId methodReference of
    Right (index, method) -> let locals = Locals $ array (0, (fromIntegral $ localsSize method) - 1) []
                             in Right $ Frame 0 classId index locals  []
    Left err -> Left err


runThread :: Int -> Thread -> IO ()
runThread c thread
  | c > 100 = print "Exiting abnormally"
  | null $ frames thread = print "Exiting"
  | otherwise =
      case nextInstructionLine thread of
        Right instruction -> let execution = execute instruction
                                 (_, newThread) = runState execution thread
                             in runThread (c + 1) (incrementInstructionCounter newThread)
        Left error -> print error

incrementInstructionCounter :: Thread -> Thread
incrementInstructionCounter t@Thread{frames=(f@Frame{pc=pc}:fs)} = t{frames=f{pc=pc+1}:fs}

nextInstructionLine :: Thread -> Either VMError Instruction
nextInstructionLine Thread{frames=(Frame{pc=pc,
                                         currentClass=classId,
                                         currentMethod=methodIndex}):_,
                           runtime=rt} = do
  method <- getMethodByIndex rt classId methodIndex
  return $ instructions method !! pc

execute :: Instruction -> InstructionExecution
execute Nop = empty
-- push const <i> to stack
execute IConstM1 = iconst (-1)
execute IConst0 = iconst 0
execute IConst1 = iconst 1
execute IConst2 = iconst 2
execute IConst3 = iconst 3
execute IConst4 = iconst 4
execute IConst5 = iconst 5
-- pop int from stack, put to <i> local variable
execute IStore0 = iPopAndStoreAt 0
execute IStore1 = iPopAndStoreAt 1
execute IStore2 = iPopAndStoreAt 2
execute IStore3 = iPopAndStoreAt 3
execute (IStore localId) = iPopAndStoreAt localId
-- read int from local var <i>, push it to stack
execute (ILoad local)  = iLoadAndPushAt local
execute ILoad0 = iLoadAndPushAt 0
execute ILoad1 = iLoadAndPushAt 1
{'fraction_non_alphanumeric': 0.09112008940084243, 'fraction_numerical': 0.012378578182755952, 'mean_word_length': 2.5318761384335153, 'pattern_counts': {'":': 2, '<': 15, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4314706', 'n_tokens_mistral': 3290, 'n_tokens_neox': 3158, 'n_words': 1209}