1 {-# LANGUAGE RecordWildCards, BangPatterns #-}
2 -- | This module declares (low-level) data types for Java .class files
3 -- structures, and Binary instances to read/write them.
4 module JVM.ClassFile where
7 import Control.Applicative
9 import Data.Binary.IEEE754
10 import Data.Binary.Get
11 import Data.Binary.Put
15 import qualified Data.ByteString.Lazy as B
17 -- | Read one-byte Char
21 return $ chr (fromIntegral x)
23 -- | Generic .class file format
24 data ClassFile = ClassFile {
25 magic :: Word32, -- ^ Magic value: 0xCAFEBABE
26 minorVersion :: Word16,
27 majorVersion :: Word16,
28 constsPoolSize :: Word16, -- ^ Number of items in constants pool
29 constsPool :: [CpInfo], -- ^ Constants pool itself
30 accessFlags :: Word16, -- ^ See @JVM.Types.AccessFlag@
31 thisClass :: Word16, -- ^ Constants pool item index for this class
32 superClass :: Word16, -- ^ --/-- for super class, zero for java.lang.Object
33 interfacesCount :: Word16, -- ^ Number of implemented interfaces
34 interfaces :: [Word16], -- ^ Constants pool item indexes for implemented interfaces
35 classFieldsCount :: Word16, -- ^ Number of class fileds
36 classFields :: [FieldInfo], -- ^ Class fields
37 classMethodsCount :: Word16, -- ^ Number of class methods
38 classMethods :: [MethodInfo], -- ^ Class methods
39 classAttributesCount :: Word16, -- ^ Number of class attributes
40 classAttributes :: [AttributeInfo] -- ^ Class attributes
44 instance Binary ClassFile where
45 put (ClassFile {..}) = do
59 forM_ classMethods put
60 put classAttributesCount
61 forM_ classAttributes put
68 pool <- replicateM (fromIntegral poolsize - 1) get
72 interfacesCount <- get
73 ifaces <- replicateM (fromIntegral interfacesCount) get
74 classFieldsCount <- get
75 classFields <- replicateM (fromIntegral classFieldsCount) get
76 classMethodsCount <- get
77 classMethods <- replicateM (fromIntegral classMethodsCount) get
79 as <- replicateM (fromIntegral $ asCount - 1) get
80 return $ ClassFile magic minor major poolsize pool af this super
81 interfacesCount ifaces classFieldsCount classFields classMethodsCount classMethods asCount as
83 -- | Field signature format
93 | ObjectType String -- ^ L <class name>
94 | Array (Maybe Int) FieldType -- ^ [<type>
97 instance Show FieldType where
98 show SignedByte = "byte"
99 show CharByte = "char"
100 show DoubleType = "double"
101 show FloatType = "float"
103 show LongInt = "long"
104 show ShortInt = "short"
105 show BoolType = "bool"
106 show (ObjectType s) = "Object " ++ s
107 show (Array Nothing t) = show t ++ "[]"
108 show (Array (Just n) t) = show t ++ "[" ++ show n ++ "]"
110 -- | Class field signature
111 type FieldSignature = FieldType
113 -- | Try to read integer value from decimal representation
114 getInt :: Get (Maybe Int)
119 else return $ Just (read s)
121 getDigits :: Get [Char]
123 c <- lookAhead getChar8
131 instance Binary FieldType where
132 put SignedByte = put 'B'
133 put CharByte = put 'C'
134 put DoubleType = put 'D'
135 put FloatType = put 'F'
136 put IntType = put 'I'
137 put LongInt = put 'J'
138 put ShortInt = put 'S'
139 put BoolType = put 'Z'
140 put (ObjectType name) = put 'L' >> put name
141 put (Array Nothing sig) = put '[' >> put sig
142 put (Array (Just n) sig) = put '[' >> put (show n) >> put sig
147 'B' -> return SignedByte
148 'C' -> return CharByte
149 'D' -> return DoubleType
150 'F' -> return FloatType
151 'I' -> return IntType
152 'J' -> return LongInt
153 'S' -> return ShortInt
154 'Z' -> return BoolType
156 name <- getToSemicolon
157 return (ObjectType name)
161 return (Array mbSize sig)
162 _ -> fail $ "Unknown signature opening symbol: " ++ [b]
164 -- | Read string up to `;'
165 getToSemicolon :: Get String
171 next <- getToSemicolon
174 -- | Return value signature
175 data ReturnSignature =
180 instance Show ReturnSignature where
181 show (Returns t) = show t
182 show ReturnsVoid = "Void"
184 instance Binary ReturnSignature where
185 put (Returns sig) = put sig
186 put ReturnsVoid = put 'V'
189 x <- lookAhead getChar8
191 'V' -> skip 1 >> return ReturnsVoid
194 -- | Method argument signature
195 type ArgumentSignature = FieldType
197 -- | Class method argument signature
198 data MethodSignature =
199 MethodSignature [ArgumentSignature] ReturnSignature
202 instance Show MethodSignature where
203 show (MethodSignature args ret) = "(" ++ intercalate ", " (map show args) ++ ") returns " ++ show ret
205 instance Binary MethodSignature where
206 put (MethodSignature args ret) = do
215 fail "Cannot parse method signature: no starting `(' !"
219 fail "Internal error: method signature without `)' !?"
221 return (MethodSignature args ret)
223 -- | Read arguments signatures (up to `)')
224 getArgs :: Get [ArgumentSignature]
225 getArgs = whileJust getArg
227 getArg :: Get (Maybe ArgumentSignature)
229 x <- lookAhead getChar8
234 whileJust :: (Monad m) => m (Maybe a) -> m [a]
243 -- | Constant pool item format
245 CONSTANT_Class {nameIndex :: Word16} -- ^ 7
246 | CONSTANT_Fieldref {classIndex :: Word16, nameAndTypeIndex :: Word16} -- ^ 9
247 | CONSTANT_Methodref {classIndex :: Word16, nameAndTypeIndex :: Word16} -- ^ 10
248 | CONSTANT_InterfaceMethodref {classIndex :: Word16, nameAndTypeIndex :: Word16}-- ^ 11
249 | CONSTANT_String {stringIndex :: Word16} -- ^ 8
250 | CONSTANT_Integer {fourBytes :: Word32} -- ^ 3
251 | CONSTANT_Float Float -- ^ 4
252 | CONSTANT_Long Word64 -- ^ 5
253 | CONSTANT_Double Double -- ^ 6
254 | CONSTANT_NameAndType {nameIndex :: Word16, signatureIndex :: Word16} -- ^ 12
255 | CONSTANT_Utf8 {stringLength :: Word16, stringBytes :: B.ByteString} -- ^ 1
256 | CONSTANT_Unicode {stringLength :: Word16, stringBytes :: B.ByteString} -- ^ 2
259 instance Binary CpInfo where
260 put (CONSTANT_Class i) = putWord8 7 >> put i
261 put (CONSTANT_Fieldref i j) = putWord8 9 >> put i >> put j
262 put (CONSTANT_Methodref i j) = putWord8 10 >> put i >> put j
263 put (CONSTANT_InterfaceMethodref i j) = putWord8 11 >> put i >> put j
264 put (CONSTANT_String i) = putWord8 8 >> put i
265 put (CONSTANT_Integer x) = putWord8 3 >> put x
266 put (CONSTANT_Float x) = putWord8 4 >> putFloat32be x
267 put (CONSTANT_Long x) = putWord8 5 >> put x
268 put (CONSTANT_Double x) = putWord8 6 >> putFloat64be x
269 put (CONSTANT_NameAndType i j) = putWord8 12 >> put i >> put j
270 put (CONSTANT_Utf8 l bs) = putWord8 1 >> put l >> putLazyByteString bs
271 put (CONSTANT_Unicode l bs) = putWord8 2 >> put l >> putLazyByteString bs
279 bs <- getLazyByteString (fromIntegral l)
280 return $ CONSTANT_Utf8 l bs
283 bs <- getLazyByteString (fromIntegral l)
284 return $ CONSTANT_Unicode l bs
285 3 -> CONSTANT_Integer <$> get
286 4 -> CONSTANT_Float <$> getFloat32be
287 5 -> CONSTANT_Long <$> get
288 6 -> CONSTANT_Double <$> getFloat64be
289 7 -> CONSTANT_Class <$> get
290 8 -> CONSTANT_String <$> get
291 9 -> CONSTANT_Fieldref <$> get <*> get
292 10 -> CONSTANT_Methodref <$> get <*> get
293 11 -> CONSTANT_InterfaceMethodref <$> get <*> get
294 12 -> CONSTANT_NameAndType <$> get <*> get
295 _ -> fail $ "Unknown constants pool entry tag: " ++ show tag
297 -- | Class field format
298 data FieldInfo = FieldInfo {
299 fieldAccessFlags :: Word16,
300 fieldNameIndex :: Word16,
301 fieldSignatureIndex :: Word16,
302 fieldAttributesCount :: Word16,
303 fieldAttributes :: [AttributeInfo] }
306 instance Binary FieldInfo where
307 put (FieldInfo {..}) = do
310 put fieldSignatureIndex
311 put fieldAttributesCount
312 forM_ fieldAttributes put
319 as <- replicateM (fromIntegral n) get
320 return $ FieldInfo af ni si n as
322 -- | Class method format
323 data MethodInfo = MethodInfo {
324 methodAccessFlags :: Word16,
325 methodNameIndex :: Word16,
326 methodSignatureIndex :: Word16,
327 methodAttributesCount :: Word16,
328 methodAttributes :: [AttributeInfo] }
331 instance Binary MethodInfo where
332 put (MethodInfo {..}) = do
333 put methodAccessFlags
335 put methodSignatureIndex
336 put methodAttributesCount
337 forM_ methodAttributes put
345 as <- replicateM (fromIntegral n) get
346 return $ MethodInfo af ni si n as
348 -- | Any (class/ field/ method/ ...) attribute format.
349 -- Some formats specify special formats for @attributeValue@.
350 data AttributeInfo = AttributeInfo {
351 attributeName :: Word16,
352 attributeLength :: Word32,
353 attributeValue :: B.ByteString }
356 instance Binary AttributeInfo where
357 put (AttributeInfo {..}) = do
359 putWord32be attributeLength
360 putLazyByteString attributeValue
366 value <- getLazyByteString (fromIntegral len)
367 return $ AttributeInfo name len value