Initial commit
authorIlya Portnov <portnov84@rambler.ru>
Fri, 10 Jun 2011 13:04:55 +0000 (19:04 +0600)
committerIlya Portnov <portnov84@rambler.ru>
Fri, 10 Jun 2011 13:04:55 +0000 (19:04 +0600)
.gitignore [new file with mode: 0644]
JVM/.Converter.hs.swp [new file with mode: 0644]
JVM/ClassFile.hs [new file with mode: 0644]
JVM/Converter.hs [new file with mode: 0644]
JVM/Types.hs [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a9781ac
--- /dev/null
@@ -0,0 +1,4 @@
+*.hi
+*.o
+*.class
+*.bytecode
diff --git a/JVM/.Converter.hs.swp b/JVM/.Converter.hs.swp
new file mode 100644 (file)
index 0000000..048d029
Binary files /dev/null and b/JVM/.Converter.hs.swp differ
diff --git a/JVM/ClassFile.hs b/JVM/ClassFile.hs
new file mode 100644 (file)
index 0000000..0f8a182
--- /dev/null
@@ -0,0 +1,345 @@
+{-# LANGUAGE RecordWildCards, BangPatterns #-}
+module JVM.ClassFile where
+
+import Control.Monad
+import Control.Applicative
+import Data.Binary
+import Data.Binary.IEEE754
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.Word
+import Data.Char
+import qualified Data.ByteString.Lazy as B
+
+import Debug.Trace
+
+traceS :: (Show a) => String -> a -> a
+traceS msg x = trace (msg ++ ": " ++ show x) x
+
+char :: Word8 -> Char
+char n = chr (fromIntegral n)
+
+getChar8 :: Get Char
+getChar8 = do
+  x <- getWord8
+  return (char x)
+
+data ClassFile = ClassFile {
+  magic :: Word32,
+  minorVersion :: Word16,
+  majorVersion :: Word16,
+  constsPoolSize :: Word16,
+  constsPool :: [CpInfo],
+  accessFlags :: Word16,
+  thisClass :: Word16,
+  superClass :: Word16,
+  interfacesCount :: Word16,
+  interfaces :: [Word16],
+  classFieldsCount :: Word16,
+  classFields :: [FieldInfo],
+  classMethodsCount :: Word16,
+  classMethods :: [MethodInfo],
+  classAttributesCount :: Word16,
+  classAttributes :: [AttributeInfo]
+  }
+  deriving (Eq, Show)
+
+traceM msg x = do
+  r <- x
+  return $ traceS msg r
+
+replicateMT n m = replicateM n (traceM ">" m)
+
+instance Binary ClassFile where
+  put (ClassFile {..}) = do
+    put magic
+    put minorVersion
+    put majorVersion
+    put constsPoolSize
+    forM_ constsPool put
+    put accessFlags
+    put thisClass
+    put superClass
+    put interfacesCount
+    forM_ interfaces put
+    put classFieldsCount
+    forM_ classFields put
+    put classMethodsCount
+    forM_ classMethods put
+    put classAttributesCount
+    forM_ classAttributes put
+
+  get = do
+    magic <- get
+    minor <- get
+    major <- get
+    poolsize <- get
+    pool <- replicateM (fromIntegral poolsize - 1) get
+    af <- get
+    this <- get
+    super <- get
+    interfacesCount <- get
+    ifaces <- replicateM (fromIntegral interfacesCount) get
+    classFieldsCount <- get
+    classFields <- replicateM (fromIntegral classFieldsCount) get
+    classMethodsCount <- get
+    classMethods <- replicateM (fromIntegral classMethodsCount) get
+    asCount <- get
+    as <- replicateM (fromIntegral $ asCount - 1) get
+    return $ ClassFile magic minor major poolsize pool af this super
+               interfacesCount ifaces classFieldsCount classFields classMethodsCount classMethods asCount as
+
+data FieldType =
+    SignedByte -- B
+  | CharByte   -- C
+  | DoubleType -- D
+  | FloatType  -- F
+  | IntType    -- I
+  | LongInt    -- J
+  | ShortInt   -- S
+  | BoolType   -- Z
+  | ObjectType String -- L <class name>
+  | Array (Maybe Int) FieldType
+  deriving (Eq, Show)
+
+type FieldSignature = FieldType
+
+getInt :: Get (Maybe Int)
+getInt = do
+    s <- getDigits
+    if null s
+      then return Nothing
+      else return $ Just (read s)
+  where
+    getDigits :: Get [Char]
+    getDigits = do
+      c <- lookAhead getChar8
+      if isDigit c
+        then do
+             skip 1
+             next <- getDigits
+             return (c: next)
+        else return []
+
+instance Binary FieldType where
+  put SignedByte = put 'B'
+  put CharByte   = put 'C'
+  put DoubleType = put 'D'
+  put FloatType  = put 'F'
+  put IntType    = put 'I'
+  put LongInt    = put 'J'
+  put ShortInt   = put 'S'
+  put BoolType   = put 'Z'
+  put (ObjectType name) = put 'L' >> put name
+  put (Array Nothing sig) = put '[' >> put sig
+  put (Array (Just n) sig) = put '[' >> put (show n) >> put sig
+
+  get = do
+    b <- getChar8
+    case b of
+      'B' -> return SignedByte
+      'C' -> return CharByte
+      'D' -> return DoubleType
+      'F' -> return FloatType
+      'I' -> return IntType
+      'J' -> return LongInt
+      'S' -> return ShortInt
+      'Z' -> return BoolType
+      'L' -> do
+             name <- getToSemicolon
+             return (ObjectType name)
+      '[' -> do
+             mbSize <- getInt
+             sig <- get
+             return (Array mbSize sig)
+      _   -> fail $ "Unknown signature opening symbol: " ++ [b]
+
+getToSemicolon :: Get String
+getToSemicolon = do
+  x <- get
+  if x == ';'
+    then return []
+    else do
+         next <- getToSemicolon
+         return (x: next)
+
+data ReturnSignature =
+    Returns FieldType
+  | ReturnsVoid
+  deriving (Eq, Show)
+
+instance Binary ReturnSignature where
+  put (Returns sig) = put sig
+  put ReturnsVoid   = put 'V'
+
+  get = do
+    x <- lookAhead getChar8
+    case x of
+      'V' -> skip 1 >> return ReturnsVoid
+      _   -> Returns <$> get
+
+type ArgumentSignature = FieldType
+
+data MethodSignature =
+    MethodSignature [ArgumentSignature] ReturnSignature
+  deriving (Eq, Show)
+
+instance Binary MethodSignature where
+  put (MethodSignature args ret) = do
+    put '('
+    forM_ args put
+    put ')'
+    put ret
+
+  get =  do
+    x <- getChar8
+    when (x /= '(') $
+      fail "Cannot parse method signature: no starting `(' !"
+    args <- getArgs
+    y <- getChar8
+    when (y /= ')') $
+      fail "Internal error: method signature without `)' !?"
+    ret <- get
+    return (MethodSignature args ret)
+
+getArgs :: Get [ArgumentSignature]
+getArgs = whileJust getArg
+  where
+    getArg :: Get (Maybe ArgumentSignature)
+    getArg = do
+      x <- lookAhead getChar8
+      if x == ')'
+        then return Nothing
+        else Just <$> get
+
+whileJust :: (Monad m) => m (Maybe a) -> m [a]
+whileJust m = do
+  r <- m
+  case r of
+    Just x -> do
+              next <- whileJust m
+              return (x: next)
+    Nothing -> return []
+
+data CpInfo =
+    CONSTANT_Class {nameIndex :: Word16}                                          -- 7
+  | CONSTANT_Fieldref {classIndex :: Word16, nameAndTypeIndex :: Word16}               -- 9
+  | CONSTANT_Methodref         {classIndex :: Word16, nameAndTypeIndex :: Word16}        -- 10
+  | CONSTANT_InterfaceMethodref {classIndex :: Word16, nameAndTypeIndex :: Word16}-- 11
+  | CONSTANT_String {stringIndex :: Word16}                                       -- 8
+  | CONSTANT_Integer {fourBytes :: Word32}                                           -- 3
+  | CONSTANT_Float Float                                                          -- 4
+  | CONSTANT_Long Word64                                                          -- 5
+  | CONSTANT_Double Double                                                        -- 6
+  | CONSTANT_NameAndType {nameIndex :: Word16, signatureIndex :: Word16}               -- 12
+  | CONSTANT_Utf8 {stringLength :: Word16, stringBytes :: B.ByteString}              -- 1
+  | CONSTANT_Unicode {stringLength :: Word16, stringBytes :: B.ByteString}         -- 2
+  deriving (Eq, Show)
+
+instance Binary CpInfo where
+  put (CONSTANT_Class i) = putWord8 7 >> put i
+  put (CONSTANT_Fieldref i j) = putWord8 9 >> put i >> put j
+  put (CONSTANT_Methodref i j) = putWord8 10 >> put i >> put j
+  put (CONSTANT_InterfaceMethodref i j) = putWord8 11 >> put i >> put j
+  put (CONSTANT_String i) = putWord8 8 >> put i
+  put (CONSTANT_Integer x) = putWord8 3 >> put x
+  put (CONSTANT_Float x)   = putWord8 4 >> putFloat32be x
+  put (CONSTANT_Long x)    = putWord8 5 >> put x
+  put (CONSTANT_Double x)  = putWord8 6 >> putFloat64be x
+  put (CONSTANT_NameAndType i j) = putWord8 12 >> put i >> put j
+  put (CONSTANT_Utf8 l bs) = putWord8 1 >> put l >> putLazyByteString bs
+  put (CONSTANT_Unicode l bs) = putWord8 2 >> put l >> putLazyByteString bs
+
+  get = do
+    !offset <- bytesRead
+    tag <- getWord8
+    case tag of
+      1 -> do
+        l <- get
+        bs <- getLazyByteString (fromIntegral l)
+        return $ CONSTANT_Utf8 l bs
+      2 -> do
+        l <- get
+        bs <- getLazyByteString (fromIntegral l)
+        return $ CONSTANT_Unicode l bs
+      3  -> CONSTANT_Integer   <$> get
+      4  -> CONSTANT_Float     <$> getFloat32be
+      5  -> CONSTANT_Long      <$> get
+      6  -> CONSTANT_Double    <$> getFloat64be
+      7  -> CONSTANT_Class     <$> get
+      8  -> CONSTANT_String    <$> get
+      9  -> CONSTANT_Fieldref  <$> get <*> get
+      10 -> CONSTANT_Methodref <$> get <*> get
+      11 -> CONSTANT_InterfaceMethodref <$> get <*> get
+      12 -> CONSTANT_NameAndType <$> get <*> get
+      _  -> fail $ "Unknown constants pool entry tag: " ++ show tag
+
+data FieldInfo = FieldInfo {
+  fieldAccessFlags :: Word16,
+  fieldNameIndex :: Word16,
+  fieldSignatureIndex :: Word16,
+  fieldAttributesCount :: Word16,
+  fieldAttributes :: [AttributeInfo] }
+  deriving (Eq, Show)
+
+instance Binary FieldInfo where
+  put (FieldInfo {..}) = do
+    put fieldAccessFlags 
+    put fieldNameIndex
+    put fieldSignatureIndex
+    put fieldAttributesCount
+    forM_ fieldAttributes put
+
+  get = do
+    af <- get
+    ni <- get
+    si <- get
+    n <- get
+    as <- replicateM (fromIntegral n) get
+    return $ FieldInfo af ni si n as
+
+data MethodInfo = MethodInfo {
+  methodAccessFlags :: Word16,
+  methodNameIndex :: Word16,
+  methodSignatureIndex :: Word16,
+  methodAttributesCount :: Word16,
+  methodAttributes :: [AttributeInfo] }
+  deriving (Eq, Show)
+
+instance Binary MethodInfo where
+  put (MethodInfo {..}) = do
+    put methodAccessFlags
+    put methodNameIndex 
+    put methodSignatureIndex
+    put methodAttributesCount 
+    forM_ methodAttributes put
+
+  get = do
+    offset <- bytesRead
+    af <- get
+    ni <- get
+    si <- get
+    n <- get
+    as <- replicateM (fromIntegral n) get
+    return $ MethodInfo af ni si n as
+
+data AttributeInfo = AttributeInfo {
+  attributeName :: Word16,
+  attributeLength :: Word32,
+  attributeValue :: B.ByteString }
+  deriving (Eq, Show)
+
+instance Binary AttributeInfo where
+  put (AttributeInfo {..}) = do
+    put attributeName
+    putWord32be attributeLength
+    putLazyByteString attributeValue
+
+  get = do
+    offset <- bytesRead
+    name <- get
+    len <- getWord32be
+    value <- getLazyByteString (fromIntegral len)
+    return $ AttributeInfo name len value
+
+
diff --git a/JVM/Converter.hs b/JVM/Converter.hs
new file mode 100644 (file)
index 0000000..82c88e9
--- /dev/null
@@ -0,0 +1,116 @@
+{-# LANGUAGE TypeFamilies, StandaloneDeriving, FlexibleInstances, FlexibleContexts, UndecidableInstances, RecordWildCards, OverloadedStrings #-}
+module JVM.Converter where
+
+import Codec.Binary.UTF8.String hiding (encode, decode)
+import Data.List
+import Data.Word
+import Data.Bits
+import Data.Binary
+import Data.Char
+import Data.String
+import qualified Data.ByteString.Lazy as B
+import Data.Array
+import qualified Data.Set as S
+import qualified Data.Map as M
+
+import Debug.Trace
+
+import JVM.ClassFile
+import JVM.Types
+
+instance IsString B.ByteString where
+  fromString s = B.pack $ map (fromIntegral . ord) $ encodeString s
+
+decompile :: B.ByteString -> Class
+decompile bstr = convertClass $ decode bstr
+
+decompileFile :: FilePath -> IO Class
+decompileFile path = convertClass `fmap` decodeFile path
+
+convertClass :: ClassFile -> Class
+convertClass (ClassFile {..}) =
+  let pool = constantPoolArray constsPool
+      superName = className $ pool ! superClass
+  in Class {
+      constantPool = pool,
+      classAccess = convertAccess accessFlags,
+      this = className $ pool ! thisClass,
+      super = if superClass == 0 then Nothing else Just superName,
+      implements = map (\i -> className $ pool ! i) interfaces,
+      fields = map (convertField pool) classFields,
+      methods = map (convertMethod pool) classMethods,
+      classAttrs = convertAttrs pool classAttributes }
+
+constantPoolArray :: [CpInfo] -> Pool
+constantPoolArray list = pool
+  where
+    pool :: Pool
+    pool = listArray (1,n) $ map convert list
+    n = fromIntegral $ length list
+
+    convertNameType :: (HasSignature a, Binary (Signature a)) => Word16 -> NameType a
+    convertNameType i =
+      let (CNameType n s) = pool ! i
+      in  NameType n (decode s)
+
+    convert (CONSTANT_Class i) = CClass $ getString $ pool ! i
+    convert (CONSTANT_Fieldref i j) = CField (className $ pool ! i) (convertNameType j)
+    convert (CONSTANT_Methodref i j) = CMethod (className $ pool ! i) (convertNameType j)
+    convert (CONSTANT_InterfaceMethodref i j) = CIfaceMethod (className $ pool ! i) (convertNameType j)
+    convert (CONSTANT_String i) = CString $ getString $ pool ! i
+    convert (CONSTANT_Integer x) = CInteger x
+    convert (CONSTANT_Float x)   = CFloat x
+    convert (CONSTANT_Long x)    = CLong (fromIntegral x)
+    convert (CONSTANT_Double x)  = CDouble x
+    convert (CONSTANT_NameAndType i j) = CNameType (getString $ pool ! i) (getString $ pool ! j)
+    convert (CONSTANT_Utf8 _ bs) = CUTF8 bs
+    convert (CONSTANT_Unicode _ bs) = CUnicode bs
+
+className' x = trace ("Class name: " ++ show x) B.empty
+
+convertAccess :: Word16 -> Access
+convertAccess w = S.fromList $ concat $ zipWith (\i f -> if testBit w i then [f] else []) [0..] $ [
+   ACC_PUBLIC,
+   ACC_PRIVATE,
+   ACC_PROTECTED,
+   ACC_STATIC,
+   ACC_FINAL,
+   ACC_SYNCHRONIZED,
+   ACC_VOLATILE,
+   ACC_TRANSIENT,
+   ACC_NATIVE,
+   ACC_INTERFACE,
+   ACC_ABSTRACT ]
+
+convertField :: Pool -> FieldInfo -> Field
+convertField pool (FieldInfo {..}) = Field {
+  fieldAccess = convertAccess fieldAccessFlags,
+  fieldName = getString $ pool ! fieldNameIndex,
+  fieldSignature = decode $ getString $ pool ! fieldSignatureIndex,
+  fieldAttrs = convertAttrs pool fieldAttributes }
+
+convertMethod :: Pool -> MethodInfo -> Method
+convertMethod pool (MethodInfo {..}) = Method {
+  methodAccess = convertAccess methodAccessFlags,
+  methodName = getString $ pool ! methodNameIndex,
+  methodSignature = decode $ getString $ pool ! methodSignatureIndex,
+  methodAttrs = convertAttrs pool methodAttributes }
+
+convertAttrs :: Pool -> [AttributeInfo] -> Attributes
+convertAttrs pool attrs = M.fromList $ map go attrs
+  where
+    go (AttributeInfo {..}) = (getString $ pool ! attributeName,
+                               attributeValue)
+
+methodByName :: Class -> B.ByteString -> Maybe Method
+methodByName cls name =
+  find (\m -> methodName m == name) (methods cls)
+
+attrByName :: (HasAttributes a) => a -> B.ByteString -> Maybe B.ByteString
+attrByName x name = M.lookup name (attributes x)
+
+methodCode :: Class -> B.ByteString -> Maybe B.ByteString
+methodCode cls name = do
+  method <- methodByName cls name
+  attrByName method "Code"
+
diff --git a/JVM/Types.hs b/JVM/Types.hs
new file mode 100644 (file)
index 0000000..08ceeae
--- /dev/null
@@ -0,0 +1,109 @@
+{-# LANGUAGE TypeFamilies, StandaloneDeriving, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
+module JVM.Types where
+
+import Data.Array
+import qualified Data.ByteString.Lazy as B
+import Data.Word
+import qualified Data.Set as S
+import qualified Data.Map as M
+
+import JVM.ClassFile
+
+type Pool = Array Word16 Constant
+
+class HasAttributes a where
+  attributes :: a -> Attributes
+
+data Class = Class {
+  constantPool :: Pool,
+  classAccess :: Access,
+  this :: B.ByteString,        -- ^ this class name
+  super :: Maybe B.ByteString, -- ^ super class name
+  implements :: [B.ByteString], -- ^ implemented interfaces
+  fields :: [Field],
+  methods :: [Method],
+  classAttrs :: Attributes
+  }
+  deriving (Eq, Show)
+
+instance HasAttributes Class where
+  attributes = classAttrs
+
+class HasSignature a where
+  type Signature a
+
+data NameType a = NameType {
+  ntName :: B.ByteString,
+  ntSignature :: Signature a }
+
+deriving instance Show (Signature a) => Show (NameType a)
+deriving instance Eq (Signature a) => Eq (NameType a)
+
+data Constant =
+    CClass {className :: B.ByteString}
+  | CField {refClass :: B.ByteString, fieldNameType :: NameType Field}
+  | CMethod {refClass :: B.ByteString, nameType :: NameType Method}
+  | CIfaceMethod {refClass :: B.ByteString, nameType :: NameType Method}
+  | CString B.ByteString
+  | CInteger Word32
+  | CFloat Float
+  | CLong Integer
+  | CDouble Double
+  | CNameType B.ByteString B.ByteString
+  | CUTF8 {getString :: B.ByteString}
+  | CUnicode {getString :: B.ByteString}
+  deriving (Eq, Show)
+
+data Field = Field {
+  fieldAccess :: Access,
+  fieldName :: B.ByteString,
+  fieldSignature :: FieldSignature,
+  fieldAttrs :: Attributes }
+  deriving (Eq, Show)
+
+instance HasSignature Field where
+  type Signature Field = FieldSignature
+
+instance HasAttributes Field where
+  attributes = fieldAttrs
+
+data Method = Method {
+  methodAccess :: Access,
+  methodName :: B.ByteString,
+  methodSignature :: MethodSignature,
+  methodAttrs :: Attributes }
+  deriving (Eq, Show)
+
+instance HasSignature Method where
+  type Signature Method = MethodSignature
+
+instance HasAttributes Method where
+  attributes = methodAttrs
+
+type Access = S.Set AccessFlag
+
+data AccessFlag =
+    ACC_PUBLIC              -- 0x0001 Видимый для всех   Класс, Метод, Переменная
+  | ACC_PRIVATE           -- 0x0002 Видимый только для определяемого класса         Метод, Переменная
+  | ACC_PROTECTED       -- 0x0004 Видимый для подклассов   Метод, Переменная
+  | ACC_STATIC              -- 0x0008 Переменная или метод статические    Метод, Переменная
+  | ACC_FINAL       -- 0x0010 Нет дальнейшей подкласификации, обхода или присваивания после инициализации   Класс, Метод, Переменная
+  | ACC_SYNCHRONIZED -- 0x0020 Использует возврат в блокировке монитора    Метод
+  | ACC_VOLATILE          -- 0x0040 Не может помещать в кеш         Переменная
+  | ACC_TRANSIENT       -- 0x0080 Не может боть написан или прочитан постоянным объектом управления   Перемення
+  | ACC_NATIVE              -- 0x0100 Реализован в других языках        Метод
+  | ACC_INTERFACE       -- 0x0200 интерфейс   Класс
+  | ACC_ABSTRACT          -- 0x0400 Ничего не предусматривает   Класс, Метод
+  deriving (Eq, Show, Ord)
+
+data Attribute = Attribute {
+  attrName :: B.ByteString,
+  attrValue :: B.ByteString }
+  deriving (Eq, Show)
+
+class AttributeValue a where
+  decodeAttribute :: B.ByteString -> a
+  encodeAttribute :: a -> B.ByteString
+
+type Attributes = M.Map B.ByteString B.ByteString
+