hs-java 0.2 → 0.3.1
raw patch · 23 files changed
+1329/−275 lines, 23 filesdep +Globdep +LibZipdep +MissingHPVP ok
version bump matches the API change (PVP)
Dependencies added: Glob, LibZip, MissingH, data-default, parsec
API changes (from Hackage documentation)
- JVM.Builder.Monad: addItem :: Constant Resolved -> Generate Word16
- JVM.Builder.Monad: addNT :: Binary (Signature a) => NameType a -> Generate Word16
- JVM.Builder.Monad: addSig :: MethodSignature -> Generate Word16
- JVM.Builder.Monad: appendPool :: Constant Resolved -> Pool Resolved -> (Pool Resolved, Word16)
- JVM.Builder.Monad: endMethod :: Generate ()
- JVM.Builder.Monad: genCode :: GState -> Code
- JVM.Builder.Monad: initClass :: ByteString -> Generate Word16
- JVM.Builder.Monad: lookupPool :: Constant Resolved -> Pool Resolved -> Maybe Word16
- JVM.Builder.Monad: putInstruction :: Instruction -> Generate ()
- JVM.Builder.Monad: startMethod :: [AccessFlag] -> ByteString -> MethodSignature -> Generate ()
- JVM.Builder.Monad: type Generate a = State GState a
- JVM.ClassFile: data Pointers
- JVM.ClassFile: data Resolved
- JVM.ClassFile: instance Binary (Class Pointers)
- JVM.ClassFile: instance Binary (Constant Pointers)
- JVM.ClassFile: instance Binary (Field Pointers)
- JVM.ClassFile: instance Binary (Method Pointers)
- JVM.ClassFile: instance Binary (Signature a) => Binary (NameType a)
- JVM.ClassFile: instance Eq (Attributes Pointers)
- JVM.ClassFile: instance Eq (Attributes Resolved)
- JVM.ClassFile: instance Eq (Constant Pointers)
- JVM.ClassFile: instance Eq (Constant Resolved)
- JVM.ClassFile: instance Eq (Field Pointers)
- JVM.ClassFile: instance Eq (Field Resolved)
- JVM.ClassFile: instance Eq (Method Pointers)
- JVM.ClassFile: instance Eq (Method Resolved)
- JVM.ClassFile: instance Eq (Signature a) => Eq (NameType a)
- JVM.ClassFile: instance HasSignature Field
- JVM.ClassFile: instance HasSignature Method
- JVM.ClassFile: instance Show (Attributes Pointers)
- JVM.ClassFile: instance Show (Attributes Resolved)
- JVM.ClassFile: instance Show (Constant Pointers)
- JVM.ClassFile: instance Show (Constant Resolved)
- JVM.ClassFile: instance Show (Field Pointers)
- JVM.ClassFile: instance Show (Field Resolved)
- JVM.ClassFile: instance Show (Method Pointers)
- JVM.ClassFile: instance Show (Method Resolved)
- JVM.ClassFile: instance Show (Signature a) => Show (NameType a)
- JVM.ClassFile: nameType :: Constant stage -> Link stage (NameType Method)
- JVM.ClassFile: refClass :: Constant stage -> Link stage ByteString
- JVM.Common: instance IsString ByteString
- JVM.Converter: classFile :: Class Resolved -> Class Pointers
- JVM.Converter: convertClass :: Class Pointers -> Class Resolved
+ JVM.Assembler: ARETURN :: Instruction
+ JVM.Builder.Instructions: aaload :: Generator e g => g e ()
+ JVM.Builder.Instructions: aastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: aconst_null :: Generator e g => g e ()
+ JVM.Builder.Instructions: allocArray :: Generator e g => ByteString -> g e ()
+ JVM.Builder.Instructions: allocNewArray :: Generator e g => ByteString -> g e ()
+ JVM.Builder.Instructions: aload :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: aload_ :: Generator e g => IMM -> g e ()
+ JVM.Builder.Instructions: astore :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: astore_ :: Generator e g => Word8 -> g e ()
+ JVM.Builder.Instructions: bastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: bipush :: Generator e g => Word8 -> g e ()
+ JVM.Builder.Instructions: caload :: Generator e g => g e ()
+ JVM.Builder.Instructions: castore :: Generator e g => g e ()
+ JVM.Builder.Instructions: d2f :: Generator e g => g e ()
+ JVM.Builder.Instructions: d2i :: Generator e g => g e ()
+ JVM.Builder.Instructions: d2l :: Generator e g => g e ()
+ JVM.Builder.Instructions: dadd :: Generator e g => g e ()
+ JVM.Builder.Instructions: daload :: Generator e g => g e ()
+ JVM.Builder.Instructions: dastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: dconst_0 :: Generator e g => g e ()
+ JVM.Builder.Instructions: dconst_1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: ddiv :: Generator e g => g e ()
+ JVM.Builder.Instructions: dload :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: dload_ :: Generator e g => IMM -> g e ()
+ JVM.Builder.Instructions: dmul :: Generator e g => g e ()
+ JVM.Builder.Instructions: dneg :: Generator e g => g e ()
+ JVM.Builder.Instructions: drem :: Generator e g => g e ()
+ JVM.Builder.Instructions: dstore :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: dstore_ :: Generator e g => Word8 -> g e ()
+ JVM.Builder.Instructions: dsub :: Generator e g => g e ()
+ JVM.Builder.Instructions: dup :: Generator e g => g e ()
+ JVM.Builder.Instructions: dup2 :: Generator e g => g e ()
+ JVM.Builder.Instructions: dup2_x1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: dup2_x2 :: Generator e g => g e ()
+ JVM.Builder.Instructions: dup_x1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: dup_x2 :: Generator e g => g e ()
+ JVM.Builder.Instructions: f2d :: Generator e g => g e ()
+ JVM.Builder.Instructions: f2i :: Generator e g => g e ()
+ JVM.Builder.Instructions: f2l :: Generator e g => g e ()
+ JVM.Builder.Instructions: fadd :: Generator e g => g e ()
+ JVM.Builder.Instructions: faload :: Generator e g => g e ()
+ JVM.Builder.Instructions: fastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: fconst_0 :: Generator e g => g e ()
+ JVM.Builder.Instructions: fconst_1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: fconst_2 :: Generator e g => g e ()
+ JVM.Builder.Instructions: fdiv :: Generator e g => g e ()
+ JVM.Builder.Instructions: fload :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: fload_ :: Generator e g => IMM -> g e ()
+ JVM.Builder.Instructions: fmul :: Generator e g => g e ()
+ JVM.Builder.Instructions: fneg :: Generator e g => g e ()
+ JVM.Builder.Instructions: frem :: Generator e g => g e ()
+ JVM.Builder.Instructions: fstore :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: fstore_ :: Generator e g => Word8 -> g e ()
+ JVM.Builder.Instructions: fsub :: Generator e g => g e ()
+ JVM.Builder.Instructions: getStaticField :: Generator e g => ByteString -> NameType (Field Direct) -> g e ()
+ JVM.Builder.Instructions: i2b :: Generator e g => g e ()
+ JVM.Builder.Instructions: i2c :: Generator e g => g e ()
+ JVM.Builder.Instructions: i2d :: Generator e g => g e ()
+ JVM.Builder.Instructions: i2f :: Generator e g => g e ()
+ JVM.Builder.Instructions: i2l :: Generator e g => g e ()
+ JVM.Builder.Instructions: i2s :: Generator e g => g e ()
+ JVM.Builder.Instructions: iadd :: Generator e g => g e ()
+ JVM.Builder.Instructions: iaload :: Generator e g => g e ()
+ JVM.Builder.Instructions: iand :: Generator e g => g e ()
+ JVM.Builder.Instructions: iastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_0 :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_2 :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_3 :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_4 :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_5 :: Generator e g => g e ()
+ JVM.Builder.Instructions: iconst_m1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: idiv :: Generator e g => g e ()
+ JVM.Builder.Instructions: iinc :: Generator e g => Word8 -> Word8 -> g e ()
+ JVM.Builder.Instructions: iload :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: iload_ :: Generator e g => IMM -> g e ()
+ JVM.Builder.Instructions: imul :: Generator e g => g e ()
+ JVM.Builder.Instructions: ineg :: Generator e g => g e ()
+ JVM.Builder.Instructions: invokeSpecial :: Generator e g => ByteString -> NameType (Method Direct) -> g e ()
+ JVM.Builder.Instructions: invokeStatic :: Generator e g => ByteString -> NameType (Method Direct) -> g e ()
+ JVM.Builder.Instructions: invokeVirtual :: Generator e g => ByteString -> NameType (Method Direct) -> g e ()
+ JVM.Builder.Instructions: ior :: Generator e g => g e ()
+ JVM.Builder.Instructions: irem :: Generator e g => g e ()
+ JVM.Builder.Instructions: ishl :: Generator e g => g e ()
+ JVM.Builder.Instructions: ishr :: Generator e g => g e ()
+ JVM.Builder.Instructions: istore :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: istore_ :: Generator e g => Word8 -> g e ()
+ JVM.Builder.Instructions: isub :: Generator e g => g e ()
+ JVM.Builder.Instructions: iushr :: Generator e g => g e ()
+ JVM.Builder.Instructions: ixor :: Generator e g => g e ()
+ JVM.Builder.Instructions: l2d :: Generator e g => g e ()
+ JVM.Builder.Instructions: l2f :: Generator e g => g e ()
+ JVM.Builder.Instructions: l2i :: Generator e g => g e ()
+ JVM.Builder.Instructions: ladd :: Generator e g => g e ()
+ JVM.Builder.Instructions: laload :: Generator e g => g e ()
+ JVM.Builder.Instructions: land :: Generator e g => g e ()
+ JVM.Builder.Instructions: lastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: lcmp :: Generator e g => g e ()
+ JVM.Builder.Instructions: lconst_0 :: Generator e g => g e ()
+ JVM.Builder.Instructions: lconst_1 :: Generator e g => g e ()
+ JVM.Builder.Instructions: ldc1 :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: ldc2 :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: ldc2w :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: ldiv :: Generator e g => g e ()
+ JVM.Builder.Instructions: lload :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: lload_ :: Generator e g => IMM -> g e ()
+ JVM.Builder.Instructions: lmul :: Generator e g => g e ()
+ JVM.Builder.Instructions: lneg :: Generator e g => g e ()
+ JVM.Builder.Instructions: loadString :: Generator e g => String -> g e ()
+ JVM.Builder.Instructions: lor :: Generator e g => g e ()
+ JVM.Builder.Instructions: lrem :: Generator e g => g e ()
+ JVM.Builder.Instructions: lshl :: Generator e g => g e ()
+ JVM.Builder.Instructions: lshr :: Generator e g => g e ()
+ JVM.Builder.Instructions: lstore :: Generator e g => Constant Direct -> g e ()
+ JVM.Builder.Instructions: lstore_ :: Generator e g => Word8 -> g e ()
+ JVM.Builder.Instructions: lsub :: Generator e g => g e ()
+ JVM.Builder.Instructions: lushr :: Generator e g => g e ()
+ JVM.Builder.Instructions: lxor :: Generator e g => g e ()
+ JVM.Builder.Instructions: new :: Generator e g => ByteString -> g e ()
+ JVM.Builder.Instructions: newArray :: Generator e g => ArrayType -> g e ()
+ JVM.Builder.Instructions: nop :: Generator e g => g e ()
+ JVM.Builder.Instructions: pop :: Generator e g => g e ()
+ JVM.Builder.Instructions: pop2 :: Generator e g => g e ()
+ JVM.Builder.Instructions: saload :: Generator e g => g e ()
+ JVM.Builder.Instructions: sastore :: Generator e g => g e ()
+ JVM.Builder.Instructions: sipush :: Generator e g => Word16 -> g e ()
+ JVM.Builder.Instructions: swap :: Generator e g => g e ()
+ JVM.Builder.Instructions: wide :: Generator e g => (Word8 -> Instruction) -> Constant Direct -> g e ()
+ JVM.Builder.Monad: class (Monad (g e), MonadState GState (g e)) => Generator e g
+ JVM.Builder.Monad: classPath :: GState -> [Tree CPEntry]
+ JVM.Builder.Monad: data Generate e a
+ JVM.Builder.Monad: data GenerateIO e a
+ JVM.Builder.Monad: emptyGState :: GState
+ JVM.Builder.Monad: generateIO :: [Tree CPEntry] -> ByteString -> GenerateIO (Caught SomeException NoExceptions) () -> IO (Class Direct)
+ JVM.Builder.Monad: getClassField :: (Throws ENotFound e, Throws ENotLoaded e) => String -> ByteString -> GenerateIO e (NameType (Field Direct))
+ JVM.Builder.Monad: getClassMethod :: (Throws ENotFound e, Throws ENotLoaded e) => String -> ByteString -> GenerateIO e (NameType (Method Direct))
+ JVM.Builder.Monad: instance Generator e GenerateIO
+ JVM.Builder.Monad: instance Monad (Generate e)
+ JVM.Builder.Monad: instance Monad (GenerateIO e)
+ JVM.Builder.Monad: instance MonadIO (EMT e (StateT GState IO))
+ JVM.Builder.Monad: instance MonadIO (GenerateIO e)
+ JVM.Builder.Monad: instance MonadState GState (EMT e (State GState)) => Generator e Generate
+ JVM.Builder.Monad: instance MonadState GState (Generate e)
+ JVM.Builder.Monad: instance MonadState GState (GenerateIO e)
+ JVM.Builder.Monad: instance MonadState st (EMT e (State st))
+ JVM.Builder.Monad: instance MonadState st (EMT e (StateT st IO))
+ JVM.Builder.Monad: locals :: GState -> Word16
+ JVM.Builder.Monad: nextPoolIndex :: GState -> Word16
+ JVM.Builder.Monad: setMaxLocals :: Generator e g => Word16 -> g e ()
+ JVM.Builder.Monad: setStackSize :: Generator e g => Word16 -> g e ()
+ JVM.Builder.Monad: stackSize :: GState -> Word16
+ JVM.Builder.Monad: throwG :: (Generator e g, Exception x, Throws x e) => x -> g e a
+ JVM.Builder.Monad: withClassPath :: ClassPath () -> GenerateIO e ()
+ JVM.ClassFile: data Direct
+ JVM.ClassFile: data File
+ JVM.ClassFile: defaultClass :: (Default (AccessFlags stage), Default (Link stage ByteString), Default (Attributes stage)) => Class stage
+ JVM.ClassFile: instance Binary (Class File)
+ JVM.ClassFile: instance Binary (Field File)
+ JVM.ClassFile: instance Binary (Method File)
+ JVM.ClassFile: instance Default (Attributes Direct)
+ JVM.ClassFile: instance Default (Attributes File)
+ JVM.ClassFile: instance Eq (Attributes Direct)
+ JVM.ClassFile: instance Eq (Attributes File)
+ JVM.ClassFile: instance Eq (Class Direct)
+ JVM.ClassFile: instance Eq (Class File)
+ JVM.ClassFile: instance Eq (Constant Direct)
+ JVM.ClassFile: instance Eq (Constant File)
+ JVM.ClassFile: instance Eq (Field Direct)
+ JVM.ClassFile: instance Eq (Field File)
+ JVM.ClassFile: instance Eq (Method Direct)
+ JVM.ClassFile: instance Eq (Method File)
+ JVM.ClassFile: instance HasSignature (Field Direct)
+ JVM.ClassFile: instance HasSignature (Method Direct)
+ JVM.ClassFile: instance HasSignature a => Binary (NameType a)
+ JVM.ClassFile: instance HasSignature a => Eq (NameType a)
+ JVM.ClassFile: instance HasSignature a => Show (NameType a)
+ JVM.ClassFile: instance Show (Attributes Direct)
+ JVM.ClassFile: instance Show (Attributes File)
+ JVM.ClassFile: instance Show (Class Direct)
+ JVM.ClassFile: instance Show (Class File)
+ JVM.ClassFile: instance Show (Constant Direct)
+ JVM.ClassFile: instance Show (Constant File)
+ JVM.ClassFile: instance Show (Field Direct)
+ JVM.ClassFile: instance Show (Field File)
+ JVM.ClassFile: instance Show (Method Direct)
+ JVM.ClassFile: instance Show (Method File)
+ JVM.ClassFile: long :: Constant stage -> Bool
+ JVM.ClassFile: lookupField :: ByteString -> Class Direct -> Maybe (Field Direct)
+ JVM.ClassFile: lookupMethod :: ByteString -> Class Direct -> Maybe (Method Direct)
+ JVM.ClassFile: methodNameType :: Method Direct -> NameType (Method Direct)
+ JVM.ClassFile: toString :: ByteString -> String
+ JVM.Common: instance Default ByteString
+ JVM.Common: instance Default Word16
+ JVM.Common: mapFindIndex :: Num k => (v -> Bool) -> Map k v -> Maybe k
+ JVM.Converter: classDirect2File :: Class Direct -> Class File
+ JVM.Converter: classFile2Direct :: Class File -> Class Direct
+ JVM.Exceptions: ClassFileNotLoaded :: FilePath -> ENotLoaded
+ JVM.Exceptions: ClassNotFound :: String -> ENotFound
+ JVM.Exceptions: FieldNotFound :: String -> ByteString -> ENotFound
+ JVM.Exceptions: JARNotLoaded :: FilePath -> String -> ENotLoaded
+ JVM.Exceptions: MethodNotFound :: String -> ByteString -> ENotFound
+ JVM.Exceptions: UnexpectedEndMethod :: UnexpectedEndMethod
+ JVM.Exceptions: data ENotFound
+ JVM.Exceptions: data ENotLoaded
+ JVM.Exceptions: data UnexpectedEndMethod
+ JVM.Exceptions: instance Exception ENotFound
+ JVM.Exceptions: instance Exception ENotLoaded
+ JVM.Exceptions: instance Exception UnexpectedEndMethod
+ JVM.Exceptions: instance Show ENotFound
+ JVM.Exceptions: instance Show ENotLoaded
+ JVM.Exceptions: instance Show UnexpectedEndMethod
+ JVM.Exceptions: instance Typeable ENotFound
+ JVM.Exceptions: instance Typeable ENotLoaded
+ JVM.Exceptions: instance Typeable UnexpectedEndMethod
+ Java.ClassPath: addDirectory :: FilePath -> ClassPath ()
+ Java.ClassPath: appendPath :: FilePath -> [Tree CPEntry] -> [Tree CPEntry]
+ Java.ClassPath: execClassPath :: ClassPath () -> IO [Tree CPEntry]
+ Java.ClassPath: getEntry :: [Tree CPEntry] -> String -> IO (Maybe CPEntry)
+ Java.ClassPath: loadClass :: String -> ClassPath ()
+ Java.ClassPath: runClassPath :: ClassPath a -> IO a
+ Java.ClassPath.Common: buildTree :: [FilePath] -> [Tree FilePath]
+ Java.ClassPath.Common: mapF :: (t -> a) -> [Tree t] -> [Tree a]
+ Java.ClassPath.Common: mapFM :: (Monad m, Functor m) => (t -> m a) -> [Tree t] -> m [Tree a]
+ Java.ClassPath.Common: mapFMF :: (Monad m, Functor m) => (FilePath -> t -> m a) -> [Tree t] -> m [Tree a]
+ Java.ClassPath.Common: mapT :: (t -> a) -> Tree t -> Tree a
+ Java.ClassPath.Common: mapTM :: (Monad m, Functor m) => (t -> m a) -> Tree t -> m (Tree a)
+ Java.ClassPath.Common: mapTMF :: (Monad m, Functor m) => (FilePath -> t -> m a) -> Tree t -> m (Tree a)
+ Java.ClassPath.Common: merge :: [Tree CPEntry] -> [Tree CPEntry]
+ Java.ClassPath.Common: merge1 :: [Tree CPEntry] -> Tree CPEntry -> [Tree CPEntry]
+ Java.ClassPath.Types: Directory :: FilePath -> [Tree a] -> Tree a
+ Java.ClassPath.Types: File :: a -> Tree a
+ Java.ClassPath.Types: Loaded :: FilePath -> (Class Direct) -> CPEntry
+ Java.ClassPath.Types: LoadedJAR :: FilePath -> (Class Direct) -> CPEntry
+ Java.ClassPath.Types: NotLoaded :: FilePath -> CPEntry
+ Java.ClassPath.Types: NotLoadedJAR :: FilePath -> FilePath -> CPEntry
+ Java.ClassPath.Types: data CPEntry
+ Java.ClassPath.Types: data Tree a
+ Java.ClassPath.Types: instance Eq CPEntry
+ Java.ClassPath.Types: instance Eq a => Eq (Tree a)
+ Java.ClassPath.Types: instance Show CPEntry
+ Java.ClassPath.Types: instance Show a => Show (Tree a)
+ Java.ClassPath.Types: type ClassPath a = StateT [Tree CPEntry] IO a
+ Java.IO: printStreamClass :: FieldType
+ Java.JAR: addJAR :: FilePath -> ClassPath ()
+ Java.JAR: readJAR :: FilePath -> IO [Tree CPEntry]
+ Java.JAR: readManifest :: Archive (Maybe Manifest)
+ Java.JAR.Archive: checkClassTree :: [Tree CPEntry] -> IO [Tree (FilePath, Class Direct)]
+ Java.JAR.Archive: readAllJAR :: FilePath -> IO [Tree CPEntry]
+ Java.JAR.Archive: readFromJAR :: FilePath -> FilePath -> IO (Class Direct)
+ Java.JAR.Archive: readJAREntry :: Enum a => FilePath -> String -> IO (Maybe [a])
+ Java.JAR.Archive: zipJAR :: [Tree (FilePath, Class Direct)] -> Archive ()
+ Java.Lang: integerClass :: FieldType
+ Java.Lang: objectClass :: FieldType
+ Java.Lang: stringClass :: FieldType
+ Java.Lang: systemClass :: FieldType
+ Java.META: Manifest :: String -> String -> Bool -> Maybe String -> [String] -> Maybe String -> [ManifestEntry] -> Manifest
+ Java.META: ManifestEntry :: String -> Bool -> Maybe String -> Bool -> ManifestEntry
+ Java.META: classPath :: Manifest -> [String]
+ Java.META: createdBy :: Manifest -> String
+ Java.META: data Manifest
+ Java.META: data ManifestEntry
+ Java.META: instance Eq Manifest
+ Java.META: instance Eq ManifestEntry
+ Java.META: instance MetaSpec Manifest
+ Java.META: instance Show Manifest
+ Java.META: instance Show ManifestEntry
+ Java.META: mainClass :: Manifest -> Maybe String
+ Java.META: manifestEntries :: Manifest -> [ManifestEntry]
+ Java.META: manifestVersion :: Manifest -> String
+ Java.META: meBean :: ManifestEntry -> Bool
+ Java.META: meContentType :: ManifestEntry -> Maybe String
+ Java.META: meName :: ManifestEntry -> String
+ Java.META: meSealed :: ManifestEntry -> Bool
+ Java.META: sealed :: Manifest -> Bool
+ Java.META: signatureVersion :: Manifest -> Maybe String
+ Java.META.Parser: parseMeta :: String -> Either ParseError META
+ Java.META.Parser: parseMetaFile :: FilePath -> IO (Either ParseError META)
+ Java.META.Spec: bool2string :: Bool -> String
+ Java.META.Spec: class MetaSpec s where loadOtherSection s _ = s
+ Java.META.Spec: loadFirstSection :: MetaSpec s => Section -> s
+ Java.META.Spec: loadOtherSection :: MetaSpec s => s -> Section -> s
+ Java.META.Spec: loadSpec :: MetaSpec s => META -> s
+ Java.META.Spec: lookupList :: String -> Maybe String -> [(String, String)]
+ Java.META.Spec: storeMeta :: MetaSpec s => s -> META
+ Java.META.Spec: string2bool :: String -> Bool
+ Java.META.Types: type META = [Section]
+ Java.META.Types: type Section = Map String String
- JVM.Assembler: Code :: Word16 -> Word16 -> Word32 -> [Instruction] -> Word16 -> [CodeException] -> Word16 -> Attributes Pointers -> Code
+ JVM.Assembler: Code :: Word16 -> Word16 -> Word32 -> [Instruction] -> Word16 -> [CodeException] -> Word16 -> Attributes File -> Code
- JVM.Assembler: GOTO :: Instruction
+ JVM.Assembler: GOTO :: Word16 -> Instruction
- JVM.Assembler: IF :: CMP -> Instruction
+ JVM.Assembler: IF :: CMP -> Word16 -> Instruction
- JVM.Assembler: codeAttributes :: Code -> Attributes Pointers
+ JVM.Assembler: codeAttributes :: Code -> Attributes File
- JVM.Builder.Monad: GState :: [Instruction] -> Pool Resolved -> [Method Resolved] -> Maybe (Method Resolved) -> GState
+ JVM.Builder.Monad: GState :: [Instruction] -> Pool Direct -> Word16 -> [Method Direct] -> Maybe (Method Direct) -> Word16 -> Word16 -> [Tree CPEntry] -> GState
- JVM.Builder.Monad: addToPool :: Constant Resolved -> Generate Word16
+ JVM.Builder.Monad: addToPool :: Generator e g => Constant Direct -> g e Word16
- JVM.Builder.Monad: currentMethod :: GState -> Maybe (Method Resolved)
+ JVM.Builder.Monad: currentMethod :: GState -> Maybe (Method Direct)
- JVM.Builder.Monad: currentPool :: GState -> Pool Resolved
+ JVM.Builder.Monad: currentPool :: GState -> Pool Direct
- JVM.Builder.Monad: doneMethods :: GState -> [Method Resolved]
+ JVM.Builder.Monad: doneMethods :: GState -> [Method Direct]
- JVM.Builder.Monad: generate :: ByteString -> Generate () -> Class Resolved
+ JVM.Builder.Monad: generate :: [Tree CPEntry] -> ByteString -> Generate (Caught SomeException NoExceptions) () -> Class Direct
- JVM.Builder.Monad: i0 :: Instruction -> Generate ()
+ JVM.Builder.Monad: i0 :: Generator e g => Instruction -> g e ()
- JVM.Builder.Monad: i1 :: (Word16 -> Instruction) -> Constant Resolved -> Generate ()
+ JVM.Builder.Monad: i1 :: Generator e g => (Word16 -> Instruction) -> Constant Direct -> g e ()
- JVM.Builder.Monad: i8 :: (Word8 -> Instruction) -> Constant Resolved -> Generate ()
+ JVM.Builder.Monad: i8 :: Generator e g => (Word8 -> Instruction) -> Constant Direct -> g e ()
- JVM.Builder.Monad: newMethod :: [AccessFlag] -> ByteString -> [ArgumentSignature] -> ReturnSignature -> Generate () -> Generate (NameType Method)
+ JVM.Builder.Monad: newMethod :: (Generator e g, Throws UnexpectedEndMethod e) => [AccessFlag] -> ByteString -> [ArgumentSignature] -> ReturnSignature -> g e () -> g e (NameType (Method Direct))
- JVM.ClassFile: CField :: Link stage ByteString -> Link stage (NameType Field) -> Constant stage
+ JVM.ClassFile: CField :: (Link stage ByteString) -> (Link stage (NameType (Field stage))) -> Constant stage
- JVM.ClassFile: CIfaceMethod :: Link stage ByteString -> Link stage (NameType Method) -> Constant stage
+ JVM.ClassFile: CIfaceMethod :: (Link stage ByteString) -> (Link stage (NameType (Method stage))) -> Constant stage
- JVM.ClassFile: CLong :: Integer -> Constant stage
+ JVM.ClassFile: CLong :: Word64 -> Constant stage
- JVM.ClassFile: CMethod :: Link stage ByteString -> Link stage (NameType Method) -> Constant stage
+ JVM.ClassFile: CMethod :: (Link stage ByteString) -> (Link stage (NameType (Method stage))) -> Constant stage
- JVM.ClassFile: apsize :: Attributes Pointers -> Int
+ JVM.ClassFile: apsize :: Attributes File -> Int
- JVM.ClassFile: arlist :: Attributes Resolved -> [(ByteString, ByteString)]
+ JVM.ClassFile: arlist :: Attributes Direct -> [(ByteString, ByteString)]
- JVM.ClassFile: arsize :: Attributes Resolved -> Int
+ JVM.ClassFile: arsize :: Attributes Direct -> Int
- JVM.ClassFile: class HasSignature a where { type family Signature a; }
+ JVM.ClassFile: class (Binary (Signature a), Show (Signature a), Eq (Signature a)) => HasSignature a where type family Signature a
- JVM.ClassFile: className :: Constant Resolved -> ByteString
+ JVM.ClassFile: className :: Constant Direct -> ByteString
- JVM.ClassFile: fieldNameType :: Constant stage -> Link stage (NameType Field)
+ JVM.ClassFile: fieldNameType :: Field Direct -> NameType (Field Direct)
- JVM.Common: showListIx :: Show a => [a] -> String
+ JVM.Common: showListIx :: (Show i, Show a) => [(i, a)] -> String
- JVM.Converter: attrByName :: HasAttributes a => a Resolved -> ByteString -> Maybe ByteString
+ JVM.Converter: attrByName :: HasAttributes a => a Direct -> ByteString -> Maybe ByteString
- JVM.Converter: encodeClass :: (Class Resolved) -> ByteString
+ JVM.Converter: encodeClass :: (Class Direct) -> ByteString
- JVM.Converter: methodByName :: Class Resolved -> ByteString -> Maybe (Method Resolved)
+ JVM.Converter: methodByName :: Class Direct -> ByteString -> Maybe (Method Direct)
- JVM.Converter: methodCode :: Class Resolved -> ByteString -> Maybe ByteString
+ JVM.Converter: methodCode :: Class Direct -> ByteString -> Maybe ByteString
- JVM.Converter: parseClass :: ByteString -> Class Resolved
+ JVM.Converter: parseClass :: ByteString -> Class Direct
- JVM.Converter: parseClassFile :: FilePath -> IO (Class Resolved)
+ JVM.Converter: parseClassFile :: FilePath -> IO (Class Direct)
- JVM.Dump: dumpClass :: Class Resolved -> IO ()
+ JVM.Dump: dumpClass :: Class Direct -> IO ()
- Java.IO: out :: NameType Field
+ Java.IO: out :: NameType (Field Direct)
- Java.IO: printf :: NameType Method
+ Java.IO: printf :: NameType (Method Direct)
- Java.IO: println :: NameType Method
+ Java.IO: println :: NameType (Method Direct)
- Java.Lang: objectInit :: NameType Method
+ Java.Lang: objectInit :: NameType (Method Direct)
- Java.Lang: valueOfInteger :: NameType Method
+ Java.Lang: valueOfInteger :: NameType (Method Direct)
Files
- JVM/Assembler.hs +12/−8
- JVM/Builder/Instructions.hs +145/−2
- JVM/Builder/Monad.hs +213/−50
- JVM/ClassFile.hs +249/−111
- JVM/Common.hs +25/−10
- JVM/Converter.hs +76/−74
- JVM/Dump.hs +3/−2
- JVM/Exceptions.hs +33/−0
- Java/ClassPath.hs +100/−0
- Java/ClassPath/Common.hs +74/−0
- Java/ClassPath/Types.hs +35/−0
- Java/IO.hs +7/−3
- Java/JAR.hs +58/−0
- Java/JAR/Archive.hs +54/−0
- Java/Lang.hs +12/−2
- Java/META.hs +81/−0
- Java/META/Parser.hs +66/−0
- Java/META/Spec.hs +33/−0
- Java/META/Types.hs +8/−0
- TestGen.hs +26/−5
- dump-class.hs +1/−1
- hs-java.cabal +14/−3
- rebuild-class.hs +4/−4
JVM/Assembler.hs view
@@ -54,7 +54,7 @@ codeExceptionsN :: Word16, codeExceptions :: [CodeException], codeAttrsN :: Word16,- codeAttributes :: Attributes Pointers }+ codeAttributes :: Attributes File } deriving (Eq, Show) -- | Exception descriptor@@ -116,7 +116,7 @@ next <- readInstructions return (x: next) --- | JVM instruction set+-- | JVM instruction set. For comments, see JVM specification. data Instruction = NOP -- ^ 0 | ACONST_NULL -- ^ 1@@ -239,10 +239,10 @@ | LCMP -- ^ 148 | FCMP CMP -- ^ 149, 150 | DCMP CMP -- ^ 151, 152- | IF CMP -- ^ 153, 154, 155, 156, 157, 158+ | IF CMP Word16 -- ^ 153, 154, 155, 156, 157, 158 | IF_ICMP CMP Word16 -- ^ 159, 160, 161, 162, 163, 164 | IF_ACMP CMP Word16 -- ^ 165, 166- | GOTO -- ^ 167+ | GOTO Word16 -- ^ 167 | JSR Word16 -- ^ 168 | RET -- ^ 169 | TABLESWITCH Word32 Word32 Word32 [Word32] -- ^ 170@@ -251,6 +251,7 @@ | LRETURN -- ^ 173 | FRETURN -- ^ 174 | DRETURN -- ^ 175+ | ARETURN -- ^ 176 | RETURN -- ^ 177 | GETSTATIC Word16 -- ^ 178 | PUTSTATIC Word16 -- ^ 179@@ -475,12 +476,12 @@ put (DCMP C_LT) = putByte 151 put (DCMP C_GT) = putByte 152 put (DCMP c) = fail $ "No such instruction: DCMP " ++ show c- put (IF c) = putByte (fromIntegral $ 153 + fromEnum c)+ put (IF c x) = putByte (fromIntegral $ 153 + fromEnum c) >> put x put (IF_ACMP C_EQ x) = put1 165 x put (IF_ACMP C_NE x) = put1 166 x put (IF_ACMP c _) = fail $ "No such instruction: IF_ACMP " ++ show c put (IF_ICMP c x) = putByte (fromIntegral $ 159 + fromEnum c) >> put x- put GOTO = putByte 167+ put (GOTO x) = put1 167 x put (JSR x) = put1 168 x put RET = putByte 169 put (TABLESWITCH def low high offs) = do@@ -503,6 +504,7 @@ put LRETURN = putByte 173 put FRETURN = putByte 174 put DRETURN = putByte 175+ put ARETURN = putByte 176 put RETURN = putByte 177 put (GETSTATIC x) = put1 178 x put (PUTSTATIC x) = put1 179 x@@ -646,7 +648,7 @@ 152 -> return $ DCMP C_GT 165 -> IF_ACMP C_EQ <$> get 166 -> IF_ACMP C_NE <$> get- 167 -> return GOTO+ 167 -> GOTO <$> get 168 -> JSR <$> get 169 -> return RET 170 -> do@@ -670,6 +672,7 @@ 173 -> return LRETURN 174 -> return FRETURN 175 -> return DRETURN+ 176 -> return ARETURN 177 -> return RETURN 178 -> GETSTATIC <$> get 179 -> PUTSTATIC <$> get@@ -704,10 +707,11 @@ | inRange (34, 37) c -> imm 34 FLOAD_ c | inRange (38, 41) c -> imm 38 DLOAD_ c | inRange (42, 45) c -> imm 42 ALOAD_ c- | inRange (153, 158) c -> return $ IF (toEnum $ fromIntegral $ c-153)+ | inRange (153, 158) c -> IF (toEnum $ fromIntegral $ c-153) <$> get | inRange (159, 164) c -> IF_ICMP (toEnum $ fromIntegral $ c-159) <$> get | otherwise -> fail $ "Unknown instruction byte code: " ++ show c +-- | Encode list of instructions encodeInstructions :: [Instruction] -> B.ByteString encodeInstructions code = let p list = forM_ list put
JVM/Builder/Instructions.hs view
@@ -1,162 +1,305 @@-+-- | This module exports shortcuts for some of JVM instructions (which are defined in JVM.Assembler).+-- These functions get Constants, put them into constants pool and generate instruction using index+-- of constant in the pool. module JVM.Builder.Instructions where +import Data.Word+import qualified Data.ByteString.Lazy as B+import Codec.Binary.UTF8.String (encodeString)+import Data.String+ import JVM.ClassFile import JVM.Assembler import JVM.Builder.Monad +nop :: Generator e g => g e () nop = i0 NOP+aconst_null :: Generator e g => g e () aconst_null = i0 ACONST_NULL+iconst_m1 :: Generator e g => g e () iconst_m1 = i0 ICONST_M1+iconst_0 :: Generator e g => g e () iconst_0 = i0 ICONST_0+iconst_1 :: Generator e g => g e () iconst_1 = i0 ICONST_1+iconst_2 :: Generator e g => g e () iconst_2 = i0 ICONST_2+iconst_3 :: Generator e g => g e () iconst_3 = i0 ICONST_3+iconst_4 :: Generator e g => g e () iconst_4 = i0 ICONST_4+iconst_5 :: Generator e g => g e () iconst_5 = i0 ICONST_5+lconst_0 :: Generator e g => g e () lconst_0 = i0 LCONST_0+lconst_1 :: Generator e g => g e () lconst_1 = i0 LCONST_1+fconst_0 :: Generator e g => g e () fconst_0 = i0 FCONST_0+fconst_1 :: Generator e g => g e () fconst_1 = i0 FCONST_1+fconst_2 :: Generator e g => g e () fconst_2 = i0 FCONST_2+dconst_0 :: Generator e g => g e () dconst_0 = i0 DCONST_0+dconst_1 :: Generator e g => g e () dconst_1 = i0 DCONST_1 +bipush :: Generator e g => Word8 -> g e () bipush x = i0 (BIPUSH x)+sipush :: Generator e g => Word16 -> g e () sipush x = i0 (SIPUSH x)++ldc1 :: Generator e g => Constant Direct -> g e () ldc1 x = i8 LDC1 x+ldc2 :: Generator e g => Constant Direct -> g e () ldc2 x = i1 LDC2 x+ldc2w :: Generator e g => Constant Direct -> g e () ldc2w x = i1 LDC2W x+iload :: Generator e g => Constant Direct -> g e () iload x = i8 ILOAD x+lload :: Generator e g => Constant Direct -> g e () lload x = i8 LLOAD x+fload :: Generator e g => Constant Direct -> g e () fload x = i8 FLOAD x+dload :: Generator e g => Constant Direct -> g e () dload x = i8 DLOAD x+aload :: Generator e g => Constant Direct -> g e () aload x = i8 ALOAD x +iload_ :: Generator e g => IMM -> g e () iload_ x = i0 (ILOAD_ x)+lload_ :: Generator e g => IMM -> g e () lload_ x = i0 (LLOAD_ x)+fload_ :: Generator e g => IMM -> g e () fload_ x = i0 (FLOAD_ x)+dload_ :: Generator e g => IMM -> g e () dload_ x = i0 (DLOAD_ x)+aload_ :: Generator e g => IMM -> g e () aload_ x = i0 (ALOAD_ x) +iaload :: Generator e g => g e () iaload = i0 IALOAD+laload :: Generator e g => g e () laload = i0 LALOAD+faload :: Generator e g => g e () faload = i0 FALOAD+daload :: Generator e g => g e () daload = i0 DALOAD+aaload :: Generator e g => g e () aaload = i0 AALOAD+caload :: Generator e g => g e () caload = i0 CALOAD+saload :: Generator e g => g e () saload = i0 SALOAD +istore :: Generator e g => Constant Direct -> g e () istore x = i8 ISTORE x+lstore :: Generator e g => Constant Direct -> g e () lstore x = i8 LSTORE x+fstore :: Generator e g => Constant Direct -> g e () fstore x = i8 FSTORE x+dstore :: Generator e g => Constant Direct -> g e () dstore x = i8 DSTORE x+astore :: Generator e g => Constant Direct -> g e () astore x = i8 ASTORE x +istore_ :: Generator e g => Word8 -> g e () istore_ x = i0 (ISTORE x)+lstore_ :: Generator e g => Word8 -> g e () lstore_ x = i0 (LSTORE x)+fstore_ :: Generator e g => Word8 -> g e () fstore_ x = i0 (FSTORE x)+dstore_ :: Generator e g => Word8 -> g e () dstore_ x = i0 (DSTORE x)+astore_ :: Generator e g => Word8 -> g e () astore_ x = i0 (ASTORE x) +iastore :: Generator e g => g e () iastore = i0 IASTORE+lastore :: Generator e g => g e () lastore = i0 LASTORE+fastore :: Generator e g => g e () fastore = i0 FASTORE+dastore :: Generator e g => g e () dastore = i0 DASTORE+aastore :: Generator e g => g e () aastore = i0 AASTORE+bastore :: Generator e g => g e () bastore = i0 BASTORE+castore :: Generator e g => g e () castore = i0 CASTORE+sastore :: Generator e g => g e () sastore = i0 SASTORE +pop :: Generator e g => g e () pop = i0 POP +pop2 :: Generator e g => g e () pop2 = i0 POP2 +dup :: Generator e g => g e () dup = i0 DUP +dup_x1 :: Generator e g => g e () dup_x1 = i0 DUP_X1 +dup_x2 :: Generator e g => g e () dup_x2 = i0 DUP_X2 +dup2 :: Generator e g => g e () dup2 = i0 DUP2 +dup2_x1 :: Generator e g => g e () dup2_x1 = i0 DUP2_X1+dup2_x2 :: Generator e g => g e () dup2_x2 = i0 DUP2_X2+swap :: Generator e g => g e () swap = i0 SWAP +iadd :: Generator e g => g e () iadd = i0 IADD +ladd :: Generator e g => g e () ladd = i0 LADD +fadd :: Generator e g => g e () fadd = i0 FADD +dadd :: Generator e g => g e () dadd = i0 DADD +isub :: Generator e g => g e () isub = i0 ISUB +lsub :: Generator e g => g e () lsub = i0 LSUB +fsub :: Generator e g => g e () fsub = i0 FSUB +dsub :: Generator e g => g e () dsub = i0 DSUB +imul :: Generator e g => g e () imul = i0 IMUL +lmul :: Generator e g => g e () lmul = i0 LMUL +fmul :: Generator e g => g e () fmul = i0 FMUL +dmul :: Generator e g => g e () dmul = i0 DMUL +idiv :: Generator e g => g e () idiv = i0 IDIV +ldiv :: Generator e g => g e () ldiv = i0 LDIV +fdiv :: Generator e g => g e () fdiv = i0 FDIV +ddiv :: Generator e g => g e () ddiv = i0 DDIV +irem :: Generator e g => g e () irem = i0 IREM +lrem :: Generator e g => g e () lrem = i0 LREM +frem :: Generator e g => g e () frem = i0 FREM +drem :: Generator e g => g e () drem = i0 DREM +ineg :: Generator e g => g e () ineg = i0 INEG +lneg :: Generator e g => g e () lneg = i0 LNEG +fneg :: Generator e g => g e () fneg = i0 FNEG +dneg :: Generator e g => g e () dneg = i0 DNEG +ishl :: Generator e g => g e () ishl = i0 ISHL +lshl :: Generator e g => g e () lshl = i0 LSHL +ishr :: Generator e g => g e () ishr = i0 ISHR +lshr :: Generator e g => g e () lshr = i0 LSHR +iushr :: Generator e g => g e () iushr = i0 IUSHR +lushr :: Generator e g => g e () lushr = i0 LUSHR +iand :: Generator e g => g e () iand = i0 IAND +land :: Generator e g => g e () land = i0 LAND +ior :: Generator e g => g e () ior = i0 IOR +lor :: Generator e g => g e () lor = i0 LOR +ixor :: Generator e g => g e () ixor = i0 IXOR +lxor :: Generator e g => g e () lxor = i0 LXOR +iinc :: Generator e g => Word8 -> Word8 -> g e () iinc x y = i0 (IINC x y) +i2l :: Generator e g => g e () i2l = i0 I2L +i2f :: Generator e g => g e () i2f = i0 I2F +i2d :: Generator e g => g e () i2d = i0 I2D +l2i :: Generator e g => g e () l2i = i0 L2I +l2f :: Generator e g => g e () l2f = i0 L2F +l2d :: Generator e g => g e () l2d = i0 L2D +f2i :: Generator e g => g e () f2i = i0 F2I +f2l :: Generator e g => g e () f2l = i0 F2L +f2d :: Generator e g => g e () f2d = i0 F2D +d2i :: Generator e g => g e () d2i = i0 D2I +d2l :: Generator e g => g e () d2l = i0 D2L +d2f :: Generator e g => g e () d2f = i0 D2F +i2b :: Generator e g => g e () i2b = i0 I2B +i2c :: Generator e g => g e () i2c = i0 I2C +i2s :: Generator e g => g e () i2s = i0 I2S +lcmp :: Generator e g => g e () lcmp = i0 LCMP +-- | Wide instruction+wide :: Generator e g => (Word8 -> Instruction) -> Constant Direct -> g e ()+wide fn c = do+ ix <- addToPool c+ let ix0 = fromIntegral (ix `div` 0x100) :: Word8+ ix1 = fromIntegral (ix `mod` 0x100) :: Word8+ i0 (WIDE ix0 $ fn ix1)++new :: Generator e g => B.ByteString -> g e () new cls = i1 NEW (CClass cls) +newArray :: Generator e g => ArrayType -> g e () newArray t = i0 (NEWARRAY $ atype2byte t) +allocNewArray :: Generator e g => B.ByteString -> g e () allocNewArray cls = i1 ANEWARRAY (CClass cls) +invokeVirtual :: Generator e g => B.ByteString -> NameType (Method Direct) -> g e () invokeVirtual cls sig = i1 INVOKEVIRTUAL (CMethod cls sig) +invokeStatic :: Generator e g => B.ByteString -> NameType (Method Direct) -> g e () invokeStatic cls sig = i1 INVOKESTATIC (CMethod cls sig) +invokeSpecial :: Generator e g => B.ByteString -> NameType (Method Direct) -> g e () invokeSpecial cls sig = i1 INVOKESPECIAL (CMethod cls sig) +getStaticField :: Generator e g => B.ByteString -> NameType (Field Direct) -> g e () getStaticField cls sig = i1 GETSTATIC (CField cls sig) +loadString :: Generator e g => String -> g e () loadString str =- i8 LDC1 (CString str)+ i8 LDC1 (CString $ fromString $ encodeString $ str) +allocArray :: Generator e g => B.ByteString -> g e () allocArray cls = i1 ANEWARRAY (CClass cls)
JVM/Builder/Monad.hs view
@@ -1,55 +1,141 @@-{-# LANGUAGE FlexibleContexts, TypeFamilies, OverloadedStrings #-}-module JVM.Builder.Monad where+{-# LANGUAGE FlexibleContexts, TypeFamilies, OverloadedStrings, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, GeneralizedNewtypeDeriving, ScopedTypeVariables #-}+-- | This module defines Generate[IO] monad, which helps generating JVM code and+-- creating Java class constants pool.+--+-- Code generation could be done using one of two monads: Generate and GenerateIO.+-- Generate monad is pure (simply State monad), while GenerateIO is IO-related.+-- In GenerateIO additional actions are available, such as setting up ClassPath+-- and loading classes (from .class files or JAR archives).+--+module JVM.Builder.Monad+ (GState (..),+ emptyGState,+ Generator (..),+ Generate, GenerateIO,+ addToPool,+ i0, i1, i8,+ newMethod,+ setStackSize, setMaxLocals,+ withClassPath,+ getClassField, getClassMethod,+ generate, generateIO+ ) where +import Prelude hiding (catch) import Control.Monad.State as St+import Control.Monad.Exception+import Control.Monad.Exception.Base import Data.Word-import Data.List import Data.Binary import qualified Data.Map as M import qualified Data.Set as S import qualified Data.ByteString.Lazy as B -import JVM.Common () -- import instances only+import JVM.Common import JVM.ClassFile import JVM.Assembler+import JVM.Exceptions+import Java.ClassPath +-- | Generator state data GState = GState {- generated :: [Instruction],- currentPool :: Pool Resolved,- doneMethods :: [Method Resolved],- currentMethod :: Maybe (Method Resolved)}+ generated :: [Instruction], -- ^ Already generated code (in current method)+ currentPool :: Pool Direct, -- ^ Already generated constants pool+ nextPoolIndex :: Word16, -- ^ Next index to be used in constants pool+ doneMethods :: [Method Direct], -- ^ Already generated class methods+ currentMethod :: Maybe (Method Direct), -- ^ Current method+ stackSize :: Word16, -- ^ Maximum stack size for current method+ locals :: Word16, -- ^ Maximum number of local variables for current method+ classPath :: [Tree CPEntry]+ } deriving (Eq,Show) +-- | Empty generator state+emptyGState :: GState emptyGState = GState { generated = [], currentPool = M.empty,+ nextPoolIndex = 1, doneMethods = [],- currentMethod = Nothing }+ currentMethod = Nothing,+ stackSize = 496,+ locals = 0,+ classPath = []} -type Generate a = State GState a+class (Monad (g e), MonadState GState (g e)) => Generator e g where+ throwG :: (Exception x, Throws x e) => x -> g e a -appendPool :: Constant Resolved -> Pool Resolved -> (Pool Resolved, Word16)-appendPool c pool =- let size = fromIntegral (M.size pool)- pool' = M.insert size c pool- in (pool', size)+-- | Generate monad+newtype Generate e a = Generate {+ runGenerate :: EMT e (State GState) a }+ deriving (Monad, MonadState GState) -addItem :: Constant Resolved -> Generate Word16+instance MonadState st (EMT e (StateT st IO)) where+ get = lift St.get+ put x = lift (St.put x)++instance MonadState st (EMT e (State st)) where+ get = lift St.get+ put x = lift (St.put x)++-- | IO version of Generate monad+newtype GenerateIO e a = GenerateIO {+ runGenerateIO :: EMT e (StateT GState IO) a }+ deriving (Monad, MonadState GState, MonadIO)++instance MonadIO (EMT e (StateT GState IO)) where+ liftIO action = lift $ liftIO action++instance Generator e GenerateIO where+ throwG e = GenerateIO (throw e)++instance (MonadState GState (EMT e (State GState))) => Generator e Generate where+ throwG e = Generate (throw e)++execGenerateIO :: [Tree CPEntry]+ -> GenerateIO (Caught SomeException NoExceptions) a+ -> IO GState+execGenerateIO cp (GenerateIO emt) = do+ let caught = emt `catch` (\(e :: SomeException) -> fail $ show e)+ execStateT (runEMT caught) (emptyGState {classPath = cp})++execGenerate :: [Tree CPEntry]+ -> Generate (Caught SomeException NoExceptions) a+ -> GState+execGenerate cp (Generate emt) = do+ let caught = emt `catch` (\(e :: SomeException) -> fail $ show e)+ execState (runEMT caught) (emptyGState {classPath = cp})++-- | Update ClassPath+withClassPath :: ClassPath () -> GenerateIO e ()+withClassPath cp = do+ res <- liftIO $ execClassPath cp+ st <- St.get+ St.put $ st {classPath = res}++-- | Add a constant to pool+addItem :: (Generator e g) => Constant Direct -> g e Word16 addItem c = do pool <- St.gets currentPool case lookupPool c pool of- Just i -> return (i+1)+ Just i -> return i Nothing -> do- let (pool', i) = appendPool c pool+ i <- St.gets nextPoolIndex+ let pool' = M.insert i c pool+ i' = if long c+ then i+2+ else i+1 st <- St.get- St.put $ st {currentPool = pool'}- return (i+1)+ St.put $ st {currentPool = pool',+ nextPoolIndex = i'}+ return i -lookupPool :: Constant Resolved -> Pool Resolved -> Maybe Word16+-- | Lookup in a pool+lookupPool :: Constant Direct -> Pool Direct -> Maybe Word16 lookupPool c pool =- fromIntegral `fmap` findIndex (== c) (M.elems pool)+ fromIntegral `fmap` mapFindIndex (== c) pool -addNT :: Binary (Signature a) => NameType a -> Generate Word16+addNT :: (Generator e g, HasSignature a) => NameType a -> g e Word16 addNT (NameType name sig) = do let bsig = encode sig x <- addItem (CNameType name bsig)@@ -57,12 +143,13 @@ addItem (CUTF8 bsig) return x -addSig :: MethodSignature -> Generate Word16+addSig :: (Generator e g) => MethodSignature -> g e Word16 addSig c@(MethodSignature args ret) = do let bsig = encode c addItem (CUTF8 bsig) -addToPool :: Constant Resolved -> Generate Word16+-- | Add a constant into pool+addToPool :: (Generator e g) => Constant Direct -> g e Word16 addToPool c@(CClass str) = do addItem (CUTF8 str) addItem c@@ -87,29 +174,47 @@ addItem c addToPool c = addItem c -putInstruction :: Instruction -> Generate ()+putInstruction :: (Generator e g) => Instruction -> g e () putInstruction instr = do st <- St.get let code = generated st St.put $ st {generated = code ++ [instr]} -i0 :: Instruction -> Generate ()+-- | Generate one (zero-arguments) instruction+i0 :: (Generator e g) => Instruction -> g e () i0 = putInstruction -i1 :: (Word16 -> Instruction) -> Constant Resolved -> Generate ()+-- | Generate one one-argument instruction+i1 :: (Generator e g) => (Word16 -> Instruction) -> Constant Direct -> g e () i1 fn c = do ix <- addToPool c i0 (fn ix) -i8 :: (Word8 -> Instruction) -> Constant Resolved -> Generate ()+-- | Generate one one-argument instruction+i8 :: (Generator e g) => (Word8 -> Instruction) -> Constant Direct -> g e () i8 fn c = do ix <- addToPool c i0 (fn $ fromIntegral ix) -startMethod :: [AccessFlag] -> B.ByteString -> MethodSignature -> Generate ()+-- | Set maximum stack size for current method+setStackSize :: (Generator e g) => Word16 -> g e ()+setStackSize n = do+ st <- St.get+ St.put $ st {stackSize = n}++-- | Set maximum number of local variables for current method+setMaxLocals :: (Generator e g) => Word16 -> g e ()+setMaxLocals n = do+ st <- St.get+ St.put $ st {locals = n}++-- | Start generating new method+startMethod :: (Generator e g) => [AccessFlag] -> B.ByteString -> MethodSignature -> g e () startMethod flags name sig = do addToPool (CString name) addSig sig+ setStackSize 4096+ setMaxLocals 100 st <- St.get let method = Method { methodAccessFlags = S.fromList flags,@@ -120,12 +225,13 @@ St.put $ st {generated = [], currentMethod = Just method } -endMethod :: Generate ()+-- | End of method generation+endMethod :: (Generator e g, Throws UnexpectedEndMethod e) => g e () endMethod = do m <- St.gets currentMethod code <- St.gets genCode case m of- Nothing -> fail "endMethod without startMethod!"+ Nothing -> throwG UnexpectedEndMethod Just method -> do let method' = method {methodAttributes = AR $ M.fromList [("Code", encodeMethod code)], methodAttributesCount = 1}@@ -134,7 +240,14 @@ currentMethod = Nothing, doneMethods = doneMethods st ++ [method']} -newMethod :: [AccessFlag] -> B.ByteString -> [ArgumentSignature] -> ReturnSignature -> Generate () -> Generate (NameType Method)+-- | Generate new method+newMethod :: (Generator e g, Throws UnexpectedEndMethod e)+ => [AccessFlag] -- ^ Access flags for method (public, static etc)+ -> B.ByteString -- ^ Method name+ -> [ArgumentSignature] -- ^ Signatures of method arguments+ -> ReturnSignature -- ^ Method return signature+ -> g e () -- ^ Generator for method code+ -> g e (NameType (Method Direct)) newMethod flags name args ret gen = do let sig = MethodSignature args ret startMethod flags name sig@@ -142,10 +255,42 @@ endMethod return (NameType name sig) +-- | Get a class from current ClassPath+getClass :: (Throws ENotLoaded e, Throws ENotFound e)+ => String -> GenerateIO e (Class Direct)+getClass name = do+ cp <- St.gets classPath+ res <- liftIO $ getEntry cp name+ case res of+ Just (NotLoaded p) -> throwG (ClassFileNotLoaded p)+ Just (Loaded _ c) -> return c+ Just (NotLoadedJAR p c) -> throwG (JARNotLoaded p c)+ Just (LoadedJAR _ c) -> return c+ Nothing -> throwG (ClassNotFound name)++-- | Get class field signature from current ClassPath+getClassField :: (Throws ENotFound e, Throws ENotLoaded e)+ => String -> B.ByteString -> GenerateIO e (NameType (Field Direct))+getClassField clsName fldName = do+ cls <- getClass clsName+ case lookupField fldName cls of+ Just fld -> return (fieldNameType fld)+ Nothing -> throwG (FieldNotFound clsName fldName)++-- | Get class method signature from current ClassPath+getClassMethod :: (Throws ENotFound e, Throws ENotLoaded e)+ => String -> B.ByteString -> GenerateIO e (NameType (Method Direct))+getClassMethod clsName mName = do+ cls <- getClass clsName+ case lookupMethod mName cls of+ Just m -> return (methodNameType m)+ Nothing -> throwG (MethodNotFound clsName mName)++-- | Convert Generator state to method Code. genCode :: GState -> Code genCode st = Code {- codeStackSize = 4096,- codeMaxLocals = 100,+ codeStackSize = stackSize st,+ codeMaxLocals = locals st, codeLength = len, codeInstructions = generated st, codeExceptionsN = 0,@@ -155,34 +300,52 @@ where len = fromIntegral $ B.length $ encodeInstructions (generated st) -initClass :: B.ByteString -> Generate Word16+-- | Start class generation.+initClass :: (Generator e g) => B.ByteString -> g e Word16 initClass name = do addToPool (CClass "java/lang/Object") addToPool (CClass name) addToPool (CString "Code") -generate :: B.ByteString -> Generate () -> Class Resolved-generate name gen =+-- | Generate a class+generateIO :: [Tree CPEntry]+ -> B.ByteString+ -> GenerateIO (Caught SomeException NoExceptions) ()+ -> IO (Class Direct)+generateIO cp name gen = do let generator = do initClass name gen- res = execState generator emptyGState+ res <- execGenerateIO cp generator+ let code = genCode res+ d = defaultClass :: Class Direct+ return $ d {+ constsPoolSize = fromIntegral $ M.size (currentPool res),+ constsPool = currentPool res,+ accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC],+ thisClass = name,+ superClass = "java/lang/Object",+ classMethodsCount = fromIntegral $ length (doneMethods res),+ classMethods = doneMethods res }++-- | Generate a class+generate :: [Tree CPEntry]+ -> B.ByteString+ -> Generate (Caught SomeException NoExceptions) ()+ -> Class Direct+generate cp name gen =+ let generator = do+ initClass name+ gen+ res = execGenerate cp generator code = genCode res- in Class {- magic = 0xCAFEBABE,- minorVersion = 0,- majorVersion = 50,+ d = defaultClass :: Class Direct+ in d { constsPoolSize = fromIntegral $ M.size (currentPool res), constsPool = currentPool res, accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC], thisClass = name, superClass = "java/lang/Object",- interfacesCount = 0,- interfaces = [],- classFieldsCount = 0,- classFields = [], classMethodsCount = fromIntegral $ length (doneMethods res),- classMethods = doneMethods res,- classAttributesCount = 0,- classAttributes = AR M.empty }+ classMethods = doneMethods res }
JVM/ClassFile.hs view
@@ -2,36 +2,69 @@ -- | This module declares (low-level) data types for Java .class files -- structures, and Binary instances to read/write them. module JVM.ClassFile- (Attribute (..),+ (-- * About+ -- $about+ --+ --+ -- * Internal class file structures+ Attribute (..), FieldType (..),+ -- * Signatures FieldSignature, MethodSignature (..), ReturnSignature (..), ArgumentSignature (..),+ -- * Stage types+ File, Direct,+ -- * Staged structures Pool, Link, Method (..), Field (..), Class (..), Constant (..),- Pointers, Resolved,- NameType (..),- HasSignature (..), HasAttributes (..), AccessFlag (..), AccessFlags, Attributes (..),+ defaultClass,+ -- * Misc+ HasSignature (..), HasAttributes (..),+ NameType (..),+ fieldNameType, methodNameType,+ lookupField, lookupMethod,+ long,+ toString, className, apsize, arsize, arlist ) where import Control.Monad+import Control.Monad.Trans (lift) import Control.Applicative+import qualified Control.Monad.State as St import Data.Binary import Data.Binary.IEEE754 import Data.Binary.Get import Data.Binary.Put import Data.Char import Data.List+import Data.Default import qualified Data.Set as S import qualified Data.Map as M import qualified Data.ByteString.Lazy as B import Codec.Binary.UTF8.String hiding (encode, decode) +-- $about+--+-- Java .class file uses constants pool, which stores almost all source-code-level+-- constants (strings, integer literals etc), and also all identifiers (class,+-- method, field names etc). All other structures contain indexes of constants in+-- the pool instead of constants theirself.+--+-- It's not convient to use that indexes programmatically. So, .class file is represented+-- at two stages: File and Direct. At File stage, all data structures contain only indexes,+-- not constants theirself. When we read a class from a file, we get structure at File stage.+-- We only can write File stage structure to file.+--+-- At Direct stage, structures conain constants, not indexes. Convertion functions (File <-> Direct)+-- are located in the JVM.Converter module.+--+ -- | Read one-byte Char getChar8 :: Get Char getChar8 = do@@ -41,36 +74,57 @@ toString :: B.ByteString -> String toString bstr = decodeString $ map (chr . fromIntegral) $ B.unpack bstr -type family Link s a+-- | File stage+data File = File -data Pointers = Pointers+-- | Direct representation stage+data Direct = Direct -data Resolved = Resolved+-- | Link to some object+type family Link stage a -type instance Link Pointers a = Word16+-- | At File stage, Link contain index of object in the constants pool.+type instance Link File a = Word16 -type instance Link Resolved a = a+-- | At Direct stage, Link contain object itself.+type instance Link Direct a = a +-- | Object (class, method, field …) access flags type family AccessFlags stage -type instance AccessFlags Pointers = Word16+-- | At File stage, access flags are represented as Word16+type instance AccessFlags File = Word16 -type instance AccessFlags Resolved = S.Set AccessFlag+-- | At Direct stage, access flags are represented as set of flags.+type instance AccessFlags Direct = S.Set AccessFlag +-- | Object (class, method, field) attributes data family Attributes stage -data instance Attributes Pointers = AP {attributesList :: [Attribute]}+-- | At File stage, attributes are represented as list of Attribute structures.+data instance Attributes File = AP {attributesList :: [Attribute]} deriving (Eq, Show)-data instance Attributes Resolved = AR (M.Map B.ByteString B.ByteString)++instance Default (Attributes File) where+ def = AP []++-- | At Direct stage, attributes are represented as a Map.+data instance Attributes Direct = AR (M.Map B.ByteString B.ByteString) deriving (Eq, Show) -arsize :: Attributes Resolved -> Int+instance Default (Attributes Direct) where+ def = AR M.empty++-- | Size of attributes set at Direct stage+arsize :: Attributes Direct -> Int arsize (AR m) = M.size m -arlist :: Attributes Resolved -> [(B.ByteString, B.ByteString)]+-- | Associative list of attributes at Direct stage+arlist :: Attributes Direct -> [(B.ByteString, B.ByteString)] arlist (AR m) = M.assocs m -apsize :: Attributes Pointers -> Int+-- | Size of attributes set at File stage+apsize :: Attributes File -> Int apsize (AP list) = length list -- | Access flags. Used for classess, methods, variables.@@ -88,26 +142,28 @@ | ACC_ABSTRACT -- ^ 0x0400 deriving (Eq, Show, Ord, Enum) -class HasSignature a where+-- | Fields and methods have signatures.+class (Binary (Signature a), Show (Signature a), Eq (Signature a))+ => HasSignature a where type Signature a -instance HasSignature Field where- type Signature Field = FieldSignature+instance HasSignature (Field Direct) where+ type Signature (Field Direct) = FieldSignature -instance HasSignature Method where- type Signature Method = MethodSignature+instance HasSignature (Method Direct) where+ type Signature (Method Direct) = MethodSignature -- | Name and signature pair. Used for methods and fields. data NameType a = NameType { ntName :: B.ByteString, ntSignature :: Signature a } -instance Show (Signature a) => Show (NameType a) where+instance (HasSignature a) => Show (NameType a) where show (NameType n t) = toString n ++ ": " ++ show t -deriving instance Eq (Signature a) => Eq (NameType a)+deriving instance HasSignature a => Eq (NameType a) -instance (Binary (Signature a)) => Binary (NameType a) where+instance HasSignature a => Binary (NameType a) where put (NameType n t) = putLazyByteString n >> put t get = NameType <$> get <*> get@@ -115,23 +171,24 @@ -- | Constant pool item data Constant stage = CClass (Link stage B.ByteString)- | CField {refClass :: Link stage B.ByteString, fieldNameType :: Link stage (NameType Field)}- | CMethod {refClass :: Link stage B.ByteString, nameType :: Link stage (NameType Method)}- | CIfaceMethod {refClass :: Link stage B.ByteString, nameType :: Link stage (NameType Method)}+ | CField (Link stage B.ByteString) (Link stage (NameType (Field stage)))+ | CMethod (Link stage B.ByteString) (Link stage (NameType (Method stage)))+ | CIfaceMethod (Link stage B.ByteString) (Link stage (NameType (Method stage))) | CString (Link stage B.ByteString) | CInteger Word32 | CFloat Float- | CLong Integer+ | CLong Word64 | CDouble Double | CNameType (Link stage B.ByteString) (Link stage B.ByteString) | CUTF8 {getString :: B.ByteString} | CUnicode {getString :: B.ByteString} -className :: Constant Resolved -> B.ByteString+-- | Name of the CClass. Error on any other constant.+className :: Constant Direct -> B.ByteString className (CClass s) = s className x = error $ "Not a class: " ++ show x -instance Show (Constant Resolved) where+instance Show (Constant Direct) where show (CClass name) = "class " ++ toString name show (CField cls nt) = "field " ++ toString cls ++ "." ++ show nt show (CMethod cls nt) = "method " ++ toString cls ++ "." ++ show nt@@ -150,35 +207,60 @@ -- | Generic .class file format data Class stage = Class {- magic :: Word32, -- ^ Magic value: 0xCAFEBABE+ magic :: Word32, -- ^ Magic value: 0xCAFEBABE minorVersion :: Word16, majorVersion :: Word16,- constsPoolSize :: Word16, -- ^ Number of items in constants pool- constsPool :: Pool stage, -- ^ Constants pool itself- accessFlags :: AccessFlags stage, -- ^ See @JVM.Types.AccessFlag@- thisClass :: Link stage B.ByteString, -- ^ Constants pool item index for this class- superClass :: Link stage B.ByteString, -- ^ --/-- for super class, zero for java.lang.Object- interfacesCount :: Word16, -- ^ Number of implemented interfaces- interfaces :: [Link stage B.ByteString], -- ^ Constants pool item indexes for implemented interfaces- classFieldsCount :: Word16, -- ^ Number of class fileds- classFields :: [Field stage], -- ^ Class fields- classMethodsCount :: Word16, -- ^ Number of class methods- classMethods :: [Method stage], -- ^ Class methods- classAttributesCount :: Word16, -- ^ Number of class attributes- classAttributes :: Attributes stage -- ^ Class attributes+ constsPoolSize :: Word16, -- ^ Number of items in constants pool+ constsPool :: Pool stage, -- ^ Constants pool itself+ accessFlags :: AccessFlags stage, -- ^ See @JVM.Types.AccessFlag@+ thisClass :: Link stage B.ByteString, -- ^ Constants pool item index for this class+ superClass :: Link stage B.ByteString, -- ^ --/-- for super class, zero for java.lang.Object+ interfacesCount :: Word16, -- ^ Number of implemented interfaces+ interfaces :: [Link stage B.ByteString], -- ^ Constants pool item indexes for implemented interfaces+ classFieldsCount :: Word16, -- ^ Number of class fileds+ classFields :: [Field stage], -- ^ Class fields+ classMethodsCount :: Word16, -- ^ Number of class methods+ classMethods :: [Method stage], -- ^ Class methods+ classAttributesCount :: Word16, -- ^ Number of class attributes+ classAttributes :: Attributes stage -- ^ Class attributes } -deriving instance Eq (Constant Pointers)-deriving instance Eq (Constant Resolved)-deriving instance Show (Constant Pointers)+deriving instance Eq (Class File)+deriving instance Eq (Class Direct)+deriving instance Show (Class File)+deriving instance Show (Class Direct) -instance Binary (Class Pointers) where+deriving instance Eq (Constant File)+deriving instance Eq (Constant Direct)+deriving instance Show (Constant File)++-- | Default (empty) class file definition.+defaultClass :: (Default (AccessFlags stage), Default (Link stage B.ByteString), Default (Attributes stage))+ => Class stage+defaultClass = Class {+ magic = 0xCAFEBABE,+ minorVersion = 0,+ majorVersion = 50,+ constsPoolSize = 0,+ constsPool = def,+ accessFlags = def,+ thisClass = def,+ superClass = def,+ interfacesCount = 0,+ interfaces = [],+ classFieldsCount = 0,+ classFields = [],+ classMethodsCount = 0,+ classMethods = [],+ classAttributesCount = 0,+ classAttributes = def }++instance Binary (Class File) where put (Class {..}) = do put magic put minorVersion put majorVersion- put constsPoolSize- forM_ (M.elems constsPool) put+ putPool constsPool put accessFlags put thisClass put superClass@@ -193,23 +275,26 @@ get = do magic <- get+ when (magic /= 0xCAFEBABE) $+ fail $ "Invalid .class file MAGIC value: " ++ show magic minor <- get major <- get- poolsize <- get- pool <- replicateM (fromIntegral poolsize - 1) get- af <- get+ when (major > 50) $+ fail $ "Too new .class file format: " ++ show major+ poolsize <- getWord16be+ pool <- getPool (poolsize - 1)+ af <- get this <- get super <- get interfacesCount <- get ifaces <- replicateM (fromIntegral interfacesCount) get- classFieldsCount <- get+ classFieldsCount <- getWord16be classFields <- replicateM (fromIntegral classFieldsCount) get classMethodsCount <- get classMethods <- replicateM (fromIntegral classMethodsCount) get asCount <- get as <- replicateM (fromIntegral $ asCount) get- let pool' = M.fromList $ zip [1..] pool- return $ Class magic minor major poolsize pool' af this super+ return $ Class magic minor major poolsize pool af this super interfacesCount ifaces classFieldsCount classFields classMethodsCount classMethods asCount (AP as) @@ -376,50 +461,81 @@ return (x: next) Nothing -> return [] -instance Binary (Constant Pointers) where- put (CClass i) = putWord8 7 >> put i- put (CField i j) = putWord8 9 >> put i >> put j- put (CMethod i j) = putWord8 10 >> put i >> put j- put (CIfaceMethod i j) = putWord8 11 >> put i >> put j- put (CString i) = putWord8 8 >> put i- put (CInteger x) = putWord8 3 >> put x- put (CFloat x) = putWord8 4 >> putFloat32be x- put (CLong x) = putWord8 5 >> put x- put (CDouble x) = putWord8 6 >> putFloat64be x- put (CNameType i j) = putWord8 12 >> put i >> put j- put (CUTF8 bs) = do- putWord8 1- put (fromIntegral (B.length bs) :: Word16)- putLazyByteString bs- put (CUnicode bs) = do- putWord8 2- put (fromIntegral (B.length bs) :: Word16)- putLazyByteString bs+long :: Constant stage -> Bool+long (CLong _) = True+long (CDouble _) = True+long _ = False - get = do- !offset <- bytesRead- tag <- getWord8- case tag of- 1 -> do- l <- get- bs <- getLazyByteString (fromIntegral (l :: Word16))- return $ CUTF8 bs- 2 -> do- l <- get- bs <- getLazyByteString (fromIntegral (l :: Word16))- return $ CUnicode bs- 3 -> CInteger <$> get- 4 -> CFloat <$> getFloat32be- 5 -> CLong <$> get- 6 -> CDouble <$> getFloat64be- 7 -> CClass <$> get- 8 -> CString <$> get- 9 -> CField <$> get <*> get- 10 -> CMethod <$> get <*> get- 11 -> CIfaceMethod <$> get <*> get- 12 -> CNameType <$> get <*> get- _ -> fail $ "Unknown constants pool entry tag: " ++ show tag+putPool :: Pool File -> Put+putPool pool = do+ let list = M.elems pool+ d = length $ filter long list+ putWord16be $ fromIntegral (M.size pool + d + 1)+ forM_ list putC+ where+ putC (CClass i) = putWord8 7 >> put i+ putC (CField i j) = putWord8 9 >> put i >> put j+ putC (CMethod i j) = putWord8 10 >> put i >> put j+ putC (CIfaceMethod i j) = putWord8 11 >> put i >> put j+ putC (CString i) = putWord8 8 >> put i+ putC (CInteger x) = putWord8 3 >> put x+ putC (CFloat x) = putWord8 4 >> putFloat32be x+ putC (CLong x) = putWord8 5 >> put x+ putC (CDouble x) = putWord8 6 >> putFloat64be x+ putC (CNameType i j) = putWord8 12 >> put i >> put j+ putC (CUTF8 bs) = do+ putWord8 1+ put (fromIntegral (B.length bs) :: Word16)+ putLazyByteString bs+ putC (CUnicode bs) = do+ putWord8 2+ put (fromIntegral (B.length bs) :: Word16)+ putLazyByteString bs +getPool :: Word16 -> Get (Pool File)+getPool n = do+ items <- St.evalStateT go 1+ return $ M.fromList items+ where+ go :: St.StateT Word16 Get [(Word16, Constant File)]+ go = do+ i <- St.get+ if i > n+ then return []+ else do+ c <- lift getC+ let i' = if long c+ then i+2+ else i+1+ St.put i'+ next <- go+ return $ (i,c): next++ getC = do+ !offset <- bytesRead+ tag <- getWord8+ case tag of+ 1 -> do+ l <- get+ bs <- getLazyByteString (fromIntegral (l :: Word16))+ return $ CUTF8 bs+ 2 -> do+ l <- get+ bs <- getLazyByteString (fromIntegral (l :: Word16))+ return $ CUnicode bs+ 3 -> CInteger <$> get+ 4 -> CFloat <$> getFloat32be+ 5 -> CLong <$> get+ 6 -> CDouble <$> getFloat64be+ 7 -> CClass <$> get+ 8 -> CString <$> get+ 9 -> CField <$> get <*> get+ 10 -> CMethod <$> get <*> get+ 11 -> CIfaceMethod <$> get <*> get+ 12 -> CNameType <$> get <*> get+ _ -> fail $ "Unknown constants pool entry tag: " ++ show tag+-- _ -> return $ CInteger 0+ -- | Class field format data Field stage = Field { fieldAccessFlags :: AccessFlags stage,@@ -428,12 +544,23 @@ fieldAttributesCount :: Word16, fieldAttributes :: Attributes stage } -deriving instance Eq (Field Pointers)-deriving instance Eq (Field Resolved)-deriving instance Show (Field Pointers)-deriving instance Show (Field Resolved)+deriving instance Eq (Field File)+deriving instance Eq (Field Direct)+deriving instance Show (Field File)+deriving instance Show (Field Direct) -instance Binary (Field Pointers) where+lookupField :: B.ByteString -> Class Direct -> Maybe (Field Direct)+lookupField name cls = look (classFields cls)+ where+ look [] = Nothing+ look (f:fs)+ | fieldName f == name = Just f+ | otherwise = look fs++fieldNameType :: Field Direct -> NameType (Field Direct)+fieldNameType f = NameType (fieldName f) (fieldSignature f)++instance Binary (Field File) where put (Field {..}) = do put fieldAccessFlags put fieldName@@ -443,9 +570,9 @@ get = do af <- get- ni <- get+ ni <- getWord16be si <- get- n <- get+ n <- getWord16be as <- replicateM (fromIntegral n) get return $ Field af ni si n (AP as) @@ -457,12 +584,23 @@ methodAttributesCount :: Word16, methodAttributes :: Attributes stage } -deriving instance Eq (Method Pointers)-deriving instance Eq (Method Resolved)-deriving instance Show (Method Pointers)-deriving instance Show (Method Resolved)+deriving instance Eq (Method File)+deriving instance Eq (Method Direct)+deriving instance Show (Method File)+deriving instance Show (Method Direct) -instance Binary (Method Pointers) where+methodNameType :: Method Direct -> NameType (Method Direct)+methodNameType m = NameType (methodName m) (methodSignature m)++lookupMethod :: B.ByteString -> Class Direct -> Maybe (Method Direct)+lookupMethod name cls = look (classMethods cls)+ where+ look [] = Nothing+ look (f:fs)+ | methodName f == name = Just f+ | otherwise = look fs++instance Binary (Method File) where put (Method {..}) = do put methodAccessFlags put methodName@@ -500,7 +638,7 @@ get = do offset <- bytesRead- name <- get+ name <- getWord16be len <- getWord32be value <- getLazyByteString (fromIntegral len) return $ Attribute name len value
JVM/Common.hs view
@@ -1,20 +1,29 @@ {-# LANGUAGE TypeFamilies, StandaloneDeriving, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}--- | This module declares `high-level' data types for Java classes, methods etc.-module JVM.Common where+-- | This module declares some commonly used functions and instances.+module JVM.Common+ (toCharList,+ poolSize,+ (!),+ showListIx,+ mapFindIndex,+ byteString+ ) where -import Codec.Binary.UTF8.String hiding (encode, decode) import Data.Binary import Data.Binary.Put import qualified Data.ByteString.Lazy as B-import Data.Char-import Data.String import qualified Data.Map as M+import Data.Default+import Data.List import JVM.ClassFile -instance IsString B.ByteString where- fromString s = B.pack $ map (fromIntegral . ord) $ encodeString s+instance Default B.ByteString where+ def = B.empty +instance Default Word16 where+ def = 0+ toCharList :: B.ByteString -> [Int] toCharList bstr = map fromIntegral $ B.unpack bstr @@ -24,10 +33,16 @@ (!) :: (Ord k) => M.Map k a -> k -> a (!) = (M.!) -showListIx :: (Show a) => [a] -> String-showListIx list = unlines $ zipWith s [1..] list- where s i x = show i ++ ":\t" ++ show x+showListIx :: (Show i, Show a) => [(i,a)] -> String+showListIx list = unlines $ map s list+ where s (i, x) = show i ++ ":\t" ++ show x byteString :: (Binary t) => t -> B.ByteString byteString x = runPut (put x)++mapFindIndex :: (Num k) => (v -> Bool) -> M.Map k v -> Maybe k+mapFindIndex check m =+ case find (check . snd) (M.assocs m) of+ Nothing -> Nothing+ Just (k,_) -> Just k
JVM/Converter.hs view
@@ -3,7 +3,7 @@ -- high-level Java classes, methods etc representation module JVM.Converter (parseClass, parseClassFile,- convertClass, classFile,+ classFile2Direct, classDirect2File, encodeClass, methodByName, attrByName,@@ -16,7 +16,9 @@ import Data.Word import Data.Bits import Data.Binary+import Data.Default () -- import instances only import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString.Lazy.Char8 () import qualified Data.Set as S import qualified Data.Map as M @@ -25,67 +27,64 @@ import JVM.Exceptions -- | Parse .class file data-parseClass :: B.ByteString -> Class Resolved-parseClass bstr = convertClass $ decode bstr+parseClass :: B.ByteString -> Class Direct+parseClass bstr = classFile2Direct $ decode bstr -- | Parse class data from file-parseClassFile :: FilePath -> IO (Class Resolved)-parseClassFile path = convertClass `fmap` decodeFile path+parseClassFile :: FilePath -> IO (Class Direct)+parseClassFile path = classFile2Direct `fmap` decodeFile path -encodeClass :: (Class Resolved) -> B.ByteString-encodeClass cls = encode $ classFile cls+encodeClass :: (Class Direct) -> B.ByteString+encodeClass cls = encode $ classDirect2File cls -convertClass :: Class Pointers -> Class Resolved-convertClass (Class {..}) =- let pool = constantPoolArray constsPool+classFile2Direct :: Class File -> Class Direct+classFile2Direct (Class {..}) =+ let pool = poolFile2Direct constsPool superName = className $ pool ! superClass- in Class {- magic = 0xCAFEBABE,- minorVersion = 0,- majorVersion = 50,+ d = defaultClass :: Class Direct+ in d { constsPoolSize = fromIntegral (M.size pool), constsPool = pool,- accessFlags = convertAccess accessFlags,+ accessFlags = accessFile2Direct accessFlags, thisClass = className $ pool ! thisClass, superClass = if superClass == 0 then "" else superName, interfacesCount = interfacesCount, interfaces = map (\i -> className $ pool ! i) interfaces, classFieldsCount = classFieldsCount,- classFields = map (convertField pool) classFields,+ classFields = map (fieldFile2Direct pool) classFields, classMethodsCount = classMethodsCount,- classMethods = map (convertMethod pool) classMethods,+ classMethods = map (methodFile2Direct pool) classMethods, classAttributesCount = classAttributesCount,- classAttributes = convertAttrs pool classAttributes }+ classAttributes = attributesFile2Direct pool classAttributes } -classFile :: Class Resolved -> Class Pointers-classFile (Class {..}) = Class {- magic = 0xCAFEBABE,- minorVersion = 0,- majorVersion = 50,+classDirect2File :: Class Direct -> Class File+classDirect2File (Class {..}) =+ let d = defaultClass :: Class File+ in d { constsPoolSize = fromIntegral (M.size poolInfo + 1), constsPool = poolInfo,- accessFlags = access2word16 accessFlags,+ accessFlags = accessDirect2File accessFlags, thisClass = force "this" $ poolClassIndex poolInfo thisClass, superClass = force "super" $ poolClassIndex poolInfo superClass, interfacesCount = fromIntegral (length interfaces), interfaces = map (force "ifaces" . poolIndex poolInfo) interfaces, classFieldsCount = fromIntegral (length classFields),- classFields = map (fieldInfo poolInfo) classFields,+ classFields = map (fieldDirect2File poolInfo) classFields, classMethodsCount = fromIntegral (length classMethods),- classMethods = map (methodInfo poolInfo) classMethods,+ classMethods = map (methodDirect2File poolInfo) classMethods, classAttributesCount = fromIntegral $ arsize classAttributes, classAttributes = to (arlist classAttributes) } where- poolInfo = toCPInfo constsPool- to :: [(B.ByteString, B.ByteString)] -> Attributes Pointers+ poolInfo = poolDirect2File constsPool+ to :: [(B.ByteString, B.ByteString)] -> Attributes File to pairs = AP (map (attrInfo poolInfo) pairs) -toCPInfo :: Pool Resolved -> Pool Pointers-toCPInfo pool = result+poolDirect2File :: Pool Direct -> Pool File+poolDirect2File pool = result where result = M.map cpInfo pool - cpInfo :: Constant Resolved -> Constant Pointers+ cpInfo :: Constant Direct -> Constant File cpInfo (CClass name) = CClass (force "class" $ poolIndex result name) cpInfo (CField cls name) = CField (force "field a" $ poolClassIndex result cls) (force "field b" $ poolNTIndex result name)@@ -104,22 +103,22 @@ cpInfo (CUnicode s) = CUnicode s -- | Find index of given string in the list of constants-poolIndex :: (Throws NoItemInPool e) => Pool Pointers -> B.ByteString -> EM e Word16-poolIndex list name = case findIndex test (M.elems list) of+poolIndex :: (Throws NoItemInPool e) => Pool File -> B.ByteString -> EM e Word16+poolIndex list name = case mapFindIndex test list of Nothing -> throw (NoItemInPool name)- Just i -> return $ fromIntegral $ i+1+ Just i -> return $ fromIntegral i where test (CUTF8 s) | s == name = True test (CUnicode s) | s == name = True test _ = False -- | Find index of given string in the list of constants-poolClassIndex :: (Throws NoItemInPool e) => Pool Pointers -> B.ByteString -> EM e Word16-poolClassIndex list name = case findIndex checkString (M.elems list) of+poolClassIndex :: (Throws NoItemInPool e) => Pool File -> B.ByteString -> EM e Word16+poolClassIndex list name = case mapFindIndex checkString list of Nothing -> throw (NoItemInPool name)- Just i -> case findIndex (checkClass $ fromIntegral $ i+1) (M.elems list) of- Nothing -> throw (NoItemInPool $ i+1)- Just j -> return $ fromIntegral $ j+1+ Just i -> case mapFindIndex (checkClass $ fromIntegral i) list of+ Nothing -> throw (NoItemInPool i)+ Just j -> return $ fromIntegral j where checkString (CUTF8 s) | s == name = True checkString (CUnicode s) | s == name = True@@ -131,56 +130,59 @@ poolNTIndex list x@(NameType n t) = do ni <- poolIndex list n ti <- poolIndex list (byteString t)- case findIndex (check ni ti) (M.elems list) of+ case mapFindIndex (check ni ti) list of Nothing -> throw (NoItemInPool x)- Just i -> return $ fromIntegral (i+1)+ Just i -> return $ fromIntegral i where check ni ti (CNameType n' t') | (ni == n') && (ti == t') = True check _ _ _ = False -fieldInfo :: Pool Pointers -> Field Resolved -> Field Pointers-fieldInfo pool (Field {..}) = Field {- fieldAccessFlags = access2word16 fieldAccessFlags,+fieldDirect2File :: Pool File -> Field Direct -> Field File+fieldDirect2File pool (Field {..}) = Field {+ fieldAccessFlags = accessDirect2File fieldAccessFlags, fieldName = force "field name" $ poolIndex pool fieldName, fieldSignature = force "signature" $ poolIndex pool (encode fieldSignature), fieldAttributesCount = fromIntegral (arsize fieldAttributes), fieldAttributes = to (arlist fieldAttributes) } where- to :: [(B.ByteString, B.ByteString)] -> Attributes Pointers+ to :: [(B.ByteString, B.ByteString)] -> Attributes File to pairs = AP (map (attrInfo pool) pairs) -methodInfo :: Pool Pointers -> Method Resolved -> Method Pointers-methodInfo pool (Method {..}) = Method {- methodAccessFlags = access2word16 methodAccessFlags,+methodDirect2File :: Pool File -> Method Direct -> Method File+methodDirect2File pool (Method {..}) = Method {+ methodAccessFlags = accessDirect2File methodAccessFlags, methodName = force "method name" $ poolIndex pool methodName, methodSignature = force "method sig" $ poolIndex pool (encode methodSignature), methodAttributesCount = fromIntegral (arsize methodAttributes), methodAttributes = to (arlist methodAttributes) } where- to :: [(B.ByteString, B.ByteString)] -> Attributes Pointers+ to :: [(B.ByteString, B.ByteString)] -> Attributes File to pairs = AP (map (attrInfo pool) pairs) -attrInfo :: Pool Pointers -> (B.ByteString, B.ByteString) -> Attribute+attrInfo :: Pool File -> (B.ByteString, B.ByteString) -> Attribute attrInfo pool (name, value) = Attribute { attributeName = force "attr name" $ poolIndex pool name, attributeLength = fromIntegral (B.length value), attributeValue = value } -constantPoolArray :: Pool Pointers -> Pool Resolved-constantPoolArray ps = pool+poolFile2Direct :: Pool File -> Pool Direct+poolFile2Direct ps = pool where- pool :: Pool Resolved+ pool :: Pool Direct pool = M.map convert ps n = fromIntegral $ M.size ps - convertNameType :: (HasSignature a, Binary (Signature a)) => Word16 -> NameType a+ convertNameType :: (HasSignature a) => Word16 -> NameType a convertNameType i =- let (CNameType n s) = pool ! i- in NameType n (decode s)+ case pool ! i of+ CNameType n s -> NameType n (decode s)+ x -> error $ "Unexpected: " ++ show i - convert (CClass i) = CClass $ getString $ pool ! i+ convert (CClass i) = case pool ! i of+ CUTF8 name -> CClass name+ x -> error $ "Unexpected class name: " ++ show x ++ " at " ++ show i convert (CField i j) = CField (className $ pool ! i) (convertNameType j) convert (CMethod i j) = CMethod (className $ pool ! i) (convertNameType j) convert (CIfaceMethod i j) = CIfaceMethod (className $ pool ! i) (convertNameType j)@@ -193,8 +195,8 @@ convert (CUTF8 bs) = CUTF8 bs convert (CUnicode bs) = CUnicode bs -convertAccess :: AccessFlags Pointers -> AccessFlags Resolved-convertAccess w = S.fromList $ concat $ zipWith (\i f -> if testBit w i then [f] else []) [0..] $ [+accessFile2Direct :: AccessFlags File -> AccessFlags Direct+accessFile2Direct w = S.fromList $ concat $ zipWith (\i f -> if testBit w i then [f] else []) [0..] $ [ ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED,@@ -207,48 +209,48 @@ ACC_INTERFACE, ACC_ABSTRACT ] -access2word16 :: AccessFlags Resolved -> AccessFlags Pointers-access2word16 fs = bitsOr $ map toBit $ S.toList fs+accessDirect2File :: AccessFlags Direct -> AccessFlags File+accessDirect2File fs = bitsOr $ map toBit $ S.toList fs where bitsOr = foldl (.|.) 0 toBit f = 1 `shiftL` (fromIntegral $ fromEnum f) -convertField :: Pool Resolved -> Field Pointers -> Field Resolved-convertField pool (Field {..}) = Field {- fieldAccessFlags = convertAccess fieldAccessFlags,+fieldFile2Direct :: Pool Direct -> Field File -> Field Direct+fieldFile2Direct pool (Field {..}) = Field {+ fieldAccessFlags = accessFile2Direct fieldAccessFlags, fieldName = getString $ pool ! fieldName, fieldSignature = decode $ getString $ pool ! fieldSignature, fieldAttributesCount = fromIntegral (apsize fieldAttributes),- fieldAttributes = convertAttrs pool fieldAttributes }+ fieldAttributes = attributesFile2Direct pool fieldAttributes } -convertMethod :: Pool Resolved -> Method Pointers -> Method Resolved-convertMethod pool (Method {..}) = Method {- methodAccessFlags = convertAccess methodAccessFlags,+methodFile2Direct :: Pool Direct -> Method File -> Method Direct+methodFile2Direct pool (Method {..}) = Method {+ methodAccessFlags = accessFile2Direct methodAccessFlags, methodName = getString $ pool ! methodName, methodSignature = decode $ getString $ pool ! methodSignature, methodAttributesCount = fromIntegral (apsize methodAttributes),- methodAttributes = convertAttrs pool methodAttributes }+ methodAttributes = attributesFile2Direct pool methodAttributes } -convertAttrs :: Pool Resolved -> Attributes Pointers -> Attributes Resolved-convertAttrs pool (AP attrs) = AR (M.fromList $ map go attrs)+attributesFile2Direct :: Pool Direct -> Attributes File -> Attributes Direct+attributesFile2Direct pool (AP attrs) = AR (M.fromList $ map go attrs) where go :: Attribute -> (B.ByteString, B.ByteString) go (Attribute {..}) = (getString $ pool ! attributeName, attributeValue) -- | Try to get class method by name-methodByName :: Class Resolved -> B.ByteString -> Maybe (Method Resolved)+methodByName :: Class Direct -> B.ByteString -> Maybe (Method Direct) methodByName cls name = find (\m -> methodName m == name) (classMethods cls) -- | Try to get object attribute by name-attrByName :: (HasAttributes a) => a Resolved -> B.ByteString -> Maybe B.ByteString+attrByName :: (HasAttributes a) => a Direct -> B.ByteString -> Maybe B.ByteString attrByName x name = let (AR m) = attributes x in M.lookup name m -- | Try to get Code for class method (no Code for interface methods)-methodCode :: Class Resolved+methodCode :: Class Direct -> B.ByteString -- ^ Method name -> Maybe B.ByteString methodCode cls name = do
JVM/Dump.hs view
@@ -3,7 +3,7 @@ import Control.Monad import qualified Data.Map as M-import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString.Lazy.Char8 as B import Text.Printf import JVM.Common ()@@ -11,7 +11,8 @@ import JVM.Converter import JVM.Assembler -dumpClass :: Class Resolved -> IO ()+-- | Dump a class to console.+dumpClass :: Class Direct -> IO () dumpClass cls = do putStr "Class: " B.putStrLn (thisClass cls)
JVM/Exceptions.hs view
@@ -2,7 +2,10 @@ module JVM.Exceptions where import Control.Monad.Exception+import qualified Data.ByteString.Lazy as B +import JVM.ClassFile+ data NoItemInPool = forall a. Show a => NoItemInPool a deriving (Typeable) @@ -10,6 +13,36 @@ instance Show NoItemInPool where show (NoItemInPool s) = "Internal error: no such item in pool: <" ++ show s ++ ">"++data UnexpectedEndMethod = UnexpectedEndMethod+ deriving (Typeable)++instance Show UnexpectedEndMethod where+ show UnexpectedEndMethod = "endMethod without startMethod!"++instance Exception UnexpectedEndMethod++data ENotLoaded = ClassFileNotLoaded FilePath+ | JARNotLoaded FilePath String+ deriving (Typeable)++instance Show ENotLoaded where+ show (ClassFileNotLoaded p) = "Class file was not loaded: " ++ p+ show (JARNotLoaded p c) = "Class was not loaded from JAR: " ++ p ++ ": " ++ c++instance Exception ENotLoaded++data ENotFound = ClassNotFound String+ | FieldNotFound String B.ByteString+ | MethodNotFound String B.ByteString+ deriving (Typeable)++instance Show ENotFound where+ show (ClassNotFound p) = "No such class in ClassPath: " ++ p+ show (FieldNotFound c f) = "No such field in class " ++ c ++ ": " ++ toString f+ show (MethodNotFound c m) = "No such method in class " ++ c ++ ": " ++ toString m++instance Exception ENotFound force :: String -> EM AnyException a -> a force s x =
+ Java/ClassPath.hs view
@@ -0,0 +1,100 @@++module Java.ClassPath+ (module Java.ClassPath.Types,+ module Java.ClassPath.Common,+ appendPath, addDirectory, loadClass,+ runClassPath, execClassPath,+ getEntry+ ) where++import qualified Control.Monad.State as St+import Control.Monad.Trans (liftIO)+import System.FilePath.Glob hiding (glob)+import Data.String.Utils (split)++import JVM.ClassFile+import JVM.Converter+import Java.ClassPath.Types+import Java.ClassPath.Common+import Java.JAR.Archive++-- | For given list of glob masks, return list of matching files+glob :: FilePath -> [FilePath] -> IO [FilePath]+glob dir patterns = do+ (matches, _) <- globDir (map compile patterns) dir+ return $ concat matches++-- | Append one file to ClassPath forest+appendPath :: FilePath -> [Tree CPEntry] -> [Tree CPEntry]+appendPath path forest = merge $ forest ++ (mapF NotLoaded $ buildTree [path])++-- | Add one directory to current ClassPath+addDirectory :: FilePath -> ClassPath ()+addDirectory dir = do+ files <- liftIO $ glob dir ["*.class"]+ cp <- St.get+ let cp' = foldr appendPath cp files+ St.put cp'++-- | Run ClassPath monad+runClassPath :: ClassPath a -> IO a+runClassPath m = St.evalStateT m []++-- | Run ClassPath monad and return resulting ClassPath+execClassPath :: ClassPath () -> IO [Tree CPEntry]+execClassPath m = St.execStateT m []++-- | Load one class in current ClassPath+loadClass :: String -> ClassPath ()+loadClass path = do+ cp <- St.get+ cp' <- liftIO $ mapM (load xs) cp+ St.put cp'+ where+ xs = split "/" path++ load :: [String] -> Tree CPEntry -> IO (Tree CPEntry)+ load [] t = return t+ load (p:ps) t@(Directory dir forest)+ | p == dir = Directory dir `fmap` mapM (load ps) forest+ | otherwise = return t+ load [p] t@(File (NotLoaded f))+ | (p ++ ".class") == f = do+ cls <- parseClassFile (path ++ ".class")+ return (File $ Loaded path cls)+ | otherwise = return t+ load [p] t@(File (NotLoadedJAR jarfile f))+ | (p ++ ".class") == f = do+ cls <- readFromJAR jarfile (path ++ ".class")+ return (File $ LoadedJAR jarfile cls)+ | otherwise = return t+ load ps (File _) = fail $ "Found file when expecting directory! " ++ show ps++-- | Get one ClassPath entry+getEntry :: [Tree CPEntry] -> String -> IO (Maybe CPEntry)+getEntry cp path = get cp (split "/" path)+ where+ get :: [Tree CPEntry] -> [String] -> IO (Maybe CPEntry)+ get _ [] = fail "Empty path for ClassPath.getEntry.get!"+ get [] _ = return Nothing+ get (Directory dir forest: es) (p:ps)+ | dir == p = get forest ps+ | otherwise = get es (p:ps)+ get (File i@(NotLoaded f): es) [p]+ | (p ++ ".class" == f) = do+ cls <- parseClassFile (path ++ ".class")+ return $ Just (Loaded path cls)+ | otherwise = get es [p]+ get (File i@(NotLoadedJAR jarfile r): es) [p]+ | (p ++ ".class" == r) = do+ cls <- readFromJAR jarfile (path ++ ".class")+ return $ Just (LoadedJAR jarfile cls)+ | otherwise = get es [p]+ get (File i@(Loaded f c):es) [p]+ | f == p = return (Just i)+ | otherwise = get es [p]+ get (File i@(LoadedJAR f c):es) [p]+ | toString (thisClass c) == path = return (Just i)+ | otherwise = get es [p]+ get x y = fail $ "Unexpected arguments for ClassPath.getEntry.get: " ++ show x ++ ", " ++ show y+
+ Java/ClassPath/Common.hs view
@@ -0,0 +1,74 @@++module Java.ClassPath.Common where++import Data.List+import Data.String.Utils (split)+import System.FilePath++import Java.ClassPath.Types++-- | map on forest+mapF :: (t -> a) -> [Tree t] -> [Tree a]+mapF fn forest = map (mapT fn) forest++-- | mapM on forest+mapFM :: (Monad m, Functor m) => (t -> m a) -> [Tree t] -> m [Tree a]+mapFM fn forest = mapM (mapTM fn) forest++-- | mapM on tree+mapTM :: (Monad m, Functor m) => (t -> m a) -> Tree t -> m (Tree a)+mapTM fn (Directory dir forest) = Directory dir `fmap` mapFM fn forest+mapTM fn (File a) = File `fmap` fn a++mapFMF :: (Monad m, Functor m) => (FilePath -> t -> m a) -> [Tree t] -> m [Tree a]+mapFMF fn forest = mapM (mapTMF fn) forest++mapTMF :: (Monad m, Functor m) => (FilePath -> t -> m a) -> Tree t -> m (Tree a)+mapTMF fn t = go "" t+ where+ go path (Directory dir forest) = Directory dir `fmap` mapM (go $ path </> dir) forest+ go path (File a) = File `fmap` fn path a++-- | map on tree+mapT :: (t -> a) -> Tree t -> Tree a+mapT fn (Directory dir forest) = Directory dir (mapF fn forest)+mapT fn (File a) = File (fn a)++-- | Build tree from list of filenames.+-- For example, ["org/haskell", "org/java"] --> [org/{haskell, java}]+buildTree :: [FilePath] -> [Tree FilePath]+buildTree strs =+ let build :: [[String]] -> [Tree FilePath]+ build [[name]] = [File name]+ build ss = map node $ groupBy eq (sort ss)++ node [] = error "Impossible: groupBy give an empty group!"+ node ([]:l) = node l+ node l | all (null . tail) l = File (head $ head l)+ | otherwise = Directory (head $ head l) (build $ map tail l)++ ls = map (split "/") strs++ eq [] [] = True+ eq (x:_) (y:_) = x == y++ in build ls++-- | Merge ClassPath forest.+-- For example, [org/haskell, org/java] --> [org/{haskell, java}].+merge :: [Tree CPEntry] -> [Tree CPEntry]+merge [] = []+merge [t1,t2] = merge1 [t1] t2+merge (t:ts) = foldl merge1 [t] ts+ +-- | Add one ClassPath tree to forest.+merge1 :: [Tree CPEntry] -> Tree CPEntry -> [Tree CPEntry]+merge1 [] x = [x]+merge1 (x@(File e): es) y@(File e') | e == e' = x: es+ | otherwise = x: merge1 es y+merge1 (d@(Directory _ _):es) f@(File _) = d: merge1 es f+merge1 (f@(File _):es) d@(Directory _ _) = f: merge1 es d+merge1 (x@(Directory dir f):es) y@(Directory dir' f')+ | dir == dir' = Directory dir (merge $ f ++ f'): es + | otherwise = x: merge1 es y+
+ Java/ClassPath/Types.hs view
@@ -0,0 +1,35 @@++module Java.ClassPath.Types where++import Control.Monad.State+import Data.List++import JVM.ClassFile++-- | Directories tree+data Tree a =+ Directory FilePath [Tree a]+ | File a+ deriving (Eq)++instance Show a => Show (Tree a) where+ show (Directory dir forest) = dir ++ "/{" ++ intercalate ", " (map show forest) ++ "}"+ show (File a) = show a++-- | ClassPath entry+data CPEntry =+ NotLoaded FilePath -- ^ Not loaded .class file+ | Loaded FilePath (Class Direct) -- ^ Class loaded from .class file+ | NotLoadedJAR FilePath FilePath -- ^ Not loaded .jar file+ | LoadedJAR FilePath (Class Direct) -- ^ Class loaded from .jar file+ deriving (Eq)++instance Show CPEntry where+ show (NotLoaded path) = "<Not loaded file: " ++ path ++ ">"+ show (Loaded path cls) = "<Loaded from " ++ path ++ ": " ++ toString (thisClass cls) ++ ">"+ show (NotLoadedJAR jar path) = "<Not loaded JAR: " ++ jar ++ ": " ++ path ++ ">"+ show (LoadedJAR path cls) = "<Read JAR: " ++ path ++ ": " ++ toString (thisClass cls) ++ ">"++-- | ClassPath monad+type ClassPath a = StateT [Tree CPEntry] IO a+
Java/IO.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+-- | This module exports definitions for some most used classes and methods from standard Java java.io package. module Java.IO where import Data.String@@ -8,18 +9,21 @@ import qualified Java.Lang +-- | java.io.PrintStream class name printStream :: IsString s => s printStream = "java/io/PrintStream" +-- | java.io.PrintStream class as field type+printStreamClass :: FieldType printStreamClass = ObjectType printStream -println :: NameType Method+println :: NameType (Method Direct) println = NameType "println" $ MethodSignature [Java.Lang.stringClass] ReturnsVoid -out :: NameType Field+out :: NameType (Field Direct) out = NameType "out" printStreamClass -printf :: NameType Method+printf :: NameType (Method Direct) printf = NameType "printf" $ MethodSignature [Java.Lang.stringClass, Array Nothing Java.Lang.objectClass] (Returns printStreamClass)
+ Java/JAR.hs view
@@ -0,0 +1,58 @@++module Java.JAR + (readManifest,+ readJAR,+ addJAR+ ) where++import Control.Monad.Trans (liftIO)+import qualified Control.Monad.State as St+import Data.List+import qualified Codec.Archive.LibZip as Zip++import Java.ClassPath+import Java.JAR.Archive+import Java.META++readManifest :: Zip.Archive (Maybe Manifest)+readManifest = do+ let manifestPath = "META-INF/MANIFEST.MF"+ files <- Zip.fileNames []+ if manifestPath `elem` files+ then do+ content <- Zip.fileContents [] manifestPath+ case parseMeta content of+ Left e -> fail $ show e+ Right meta -> return $ Just (loadSpec meta)+ else return Nothing++readOne :: FilePath -> String -> Zip.Archive [Tree CPEntry]+readOne jarfile str = do+ files <- Zip.fileNames []+ return $ mapF (NotLoadedJAR jarfile) (buildTree $ filter good files)+ where+ good name = (str `isPrefixOf` name) && (".class" `isSuffixOf` name)++-- | Read entries from JAR file, using MANIFEST.MF if it exists.+readJAR :: FilePath -> IO [Tree CPEntry]+readJAR jarfile = do+ r <- Zip.withArchive [] jarfile $ do+ m <- readManifest+ case m of+ Nothing -> return Nothing+ Just mf -> do+ trees <- mapM (readOne jarfile) (map meName $ manifestEntries mf)+ let forest = merge (concat trees)+ return (Just forest)+ case r of+ Nothing -> readAllJAR jarfile+ Just f -> return f++-- | Add given JAR file to ClassPath+addJAR :: FilePath -> ClassPath ()+addJAR jarfile = do+ classes <- liftIO $ readJAR jarfile+ cp <- St.get+ let cp' = merge $ cp ++ classes+ St.put cp'+
+ Java/JAR/Archive.hs view
@@ -0,0 +1,54 @@+-- | This module defines functions to read Java JAR files.+module Java.JAR.Archive where++import qualified Codec.Archive.LibZip as Zip+import Data.Binary+import Data.List+import qualified Data.ByteString.Lazy as B+import System.FilePath++import Java.ClassPath.Types+import Java.ClassPath.Common+import JVM.ClassFile+import JVM.Converter++readJAREntry :: (Enum a) => FilePath -> String -> IO (Maybe [a])+readJAREntry jarfile path = do+ Zip.catchZipError (Just `fmap` (Zip.withArchive [] jarfile $ Zip.fileContents [] path))+ (\_ -> return Nothing)++-- | Read all entires from JAR file+readAllJAR :: FilePath -> IO [Tree CPEntry]+readAllJAR jarfile = do+ files <- Zip.withArchive [] jarfile $ Zip.fileNames []+ return $ mapF (NotLoadedJAR jarfile) (buildTree $ filter good files)+ where+ good file = ".class" `isSuffixOf` file++-- | Read one class from JAR file+readFromJAR :: FilePath -> FilePath -> IO (Class Direct)+readFromJAR jarfile path = do+ content <- Zip.withArchive [] jarfile $ Zip.fileContents [] path+ let bstr = B.pack content+ return $ classFile2Direct (decode bstr)++checkClassTree :: [Tree CPEntry] -> IO [Tree (FilePath, Class Direct)]+checkClassTree forest = mapFMF check forest+ where+ check _ (NotLoaded path) = do+ cls <- parseClassFile path+ return (path, cls)+ check a (Loaded path cls) = return (a </> path, cls)+ check a (NotLoadedJAR jar path) = do+ cls <- readFromJAR jar (a </> path)+ return (a </> path, cls)+ check a (LoadedJAR _ cls) =+ return (a </> show (thisClass cls), cls)++zipJAR :: [Tree (FilePath, Class Direct)] -> Zip.Archive ()+zipJAR forest = do+ mapFM go forest+ return ()+ where+ go (path, cls) = Zip.addFile path =<< Zip.sourceBuffer (B.unpack $ encodeClass cls)+
Java/Lang.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+-- | This module exports some definitions from standard Java java.lang package. module Java.Lang where import Data.String@@ -6,9 +7,16 @@ import JVM.Common () -- import instances only import JVM.ClassFile +objectClass :: FieldType objectClass = ObjectType object++stringClass :: FieldType stringClass = ObjectType string++integerClass :: FieldType integerClass = ObjectType integer++systemClass :: FieldType systemClass = ObjectType system object :: IsString s => s@@ -23,9 +31,11 @@ system :: IsString s => s system = "java/lang/System" -objectInit :: NameType Method+-- | java.lang.Object.<init>() method+objectInit :: NameType (Method Direct) objectInit = NameType "<init>" $ MethodSignature [] ReturnsVoid -valueOfInteger :: NameType Method+-- | java.lang.Integer.valueOf() method+valueOfInteger :: NameType (Method Direct) valueOfInteger = NameType "valueOf" $ MethodSignature [IntType] (Returns Java.Lang.integerClass)
+ Java/META.hs view
@@ -0,0 +1,81 @@+-- | This module declares functions and data types for+-- JAR meta-information classes, such as MANIFEST.MF etc.+module Java.META+ (module Java.META.Types,+ module Java.META.Parser,+ module Java.META.Spec,+ Manifest (..),+ ManifestEntry (..))+ where++import qualified Data.Map as M+import Data.Map ((!))++import Java.META.Types+import Java.META.Parser+import Java.META.Spec++-- | JAR MANIFEST.MF+data Manifest = Manifest {+ manifestVersion :: String,+ createdBy :: String,+ sealed :: Bool,+ signatureVersion :: Maybe String,+ classPath :: [String],+ mainClass :: Maybe String,+ manifestEntries :: [ManifestEntry]}+ deriving (Eq, Show)++-- | Manifest entry+data ManifestEntry = ManifestEntry {+ meName :: String,+ meSealed :: Bool,+ meContentType :: Maybe String,+ meBean :: Bool }+ deriving (Eq, Show)++instance MetaSpec Manifest where+ loadFirstSection s = Manifest {+ manifestVersion = s ! "Manifest-Version",+ createdBy = s ! "Created-By",+ sealed = case M.lookup "Sealed" s of+ Nothing -> False+ Just str -> string2bool str,+ signatureVersion = M.lookup "Signature-Version" s,+ classPath = case M.lookup "Class-Path" s of+ Nothing -> []+ Just str -> words str,+ mainClass = M.lookup "Main-Class" s,+ manifestEntries = []}++ loadOtherSection m s = m {manifestEntries = manifestEntries m ++ [entry]}+ where+ entry = ManifestEntry {+ meName = s ! "Name",+ meSealed = case M.lookup "Sealed" s of+ Nothing -> sealed m+ Just str -> string2bool str,+ meContentType = M.lookup "Content-Type" s,+ meBean = case M.lookup "Java-Bean" s of+ Nothing -> False+ Just str -> string2bool str }++ storeMeta m = first: map store (manifestEntries m)+ where+ first = M.fromList $ [+ ("Manifest-Version", manifestVersion m),+ ("Created-By", createdBy m)] +++ lookupList "Signature-Version" (signatureVersion m) +++ lookupList "Main-Class" (mainClass m) +++ case classPath m of+ [] -> []+ list -> [("Class-Path", unwords list)]++ store e = M.fromList $ [+ ("Name", meName e),+ ("Sealed", bool2string $ meSealed e)] +++ lookupList "Content-Type" (meContentType e) +++ if meBean e+ then [("Java-Bean", "true")]+ else []+
+ Java/META/Parser.hs view
@@ -0,0 +1,66 @@++module Java.META.Parser+ (parseMeta,+ parseMetaFile) where++import qualified Data.Map as M+import Text.Parsec+import Text.Parsec.String++import Java.META.Types++pNewLine :: Parser ()+pNewLine = choice $ map try $ [+ string "\r\n" >> return (),+ char '\r' >> return (),+ char '\n' >> return () ]++blankline :: Parser ()+blankline = do+ pNewLine <?> "first newline in blankline"+ return ()++pSection :: Parser Section+pSection = do+ list <- many1 pHeader+ blankline <?> "blank line"+ return $ M.fromList list++pHeader :: Parser (String, String) +pHeader = do+ name <- many1 headerChar <?> "header name"+ char ':'+ value <- pValue+ return (name, value)+ where+ headerChar = oneOf "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"++pValue :: Parser String+pValue = do+ list <- manyLines+ return (concat list)++manyLines :: Parser [String]+manyLines = do+ char ' '+ many space+ s <- many1 (noneOf "\n\r\0")+ pNewLine <?> "new line at end of value line"+ c <- lookAhead anyChar+ if c == ' '+ then do+ next <- manyLines+ return (s: next)+ else return [s]++pMETA :: Parser META+pMETA = many1 pSection++parseMetaFile :: FilePath -> IO (Either ParseError META)+parseMetaFile path = do+ str <- readFile path+ return $ parse pMETA path (str ++ "\n\n")++parseMeta :: String -> Either ParseError META+parseMeta str = parse pMETA "<META>" (str ++ "\n\n")+
+ Java/META/Spec.hs view
@@ -0,0 +1,33 @@+module Java.META.Spec where++import Data.Char (toLower)++import Java.META.Types++class MetaSpec s where+ loadFirstSection :: Section -> s++ loadOtherSection :: s -> Section -> s+ loadOtherSection s _ = s++ storeMeta :: s -> META++loadSpec :: (MetaSpec s) => META -> s+loadSpec [] = error "Cannot load empty metadata"+loadSpec (s:ss) =+ let x = loadFirstSection s+ in foldl loadOtherSection x ss++lookupList :: String -> Maybe String -> [(String, String)]+lookupList _ Nothing = []+lookupList name (Just val) = [(name, val)]++bool2string :: Bool -> String+bool2string True = "true"+bool2string False = "false"++string2bool :: String -> Bool+string2bool s+ | map toLower s == "true" = True+ | otherwise = False+
+ Java/META/Types.hs view
@@ -0,0 +1,8 @@++module Java.META.Types where++import qualified Data.Map as M++type Section = M.Map String String+type META = [Section]+
TestGen.hs view
@@ -1,23 +1,39 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, FlexibleContexts #-} +import Control.Monad.Exception import qualified Data.ByteString.Lazy as B import JVM.ClassFile import JVM.Converter import JVM.Assembler import JVM.Builder+import JVM.Exceptions+import Java.ClassPath import qualified Java.Lang import qualified Java.IO -test :: Generate ()+test :: (Throws ENotFound e, Throws ENotLoaded e, Throws UnexpectedEndMethod e) => GenerateIO e () test = do+ withClassPath $ do+ -- Add current directory (with Hello.class) to ClassPath+ addDirectory "."++ -- Load method signature: Hello.hello() from Hello.class+ helloJava <- getClassMethod "./Hello" "hello"++ -- Initializer method. Just calls java.lang.Object.<init> newMethod [ACC_PUBLIC] "<init>" [] ReturnsVoid $ do+ setStackSize 1+ aload_ I0 invokeSpecial Java.Lang.object Java.Lang.objectInit i0 RETURN + -- Declare hello() method and bind it's signature to hello. hello <- newMethod [ACC_PUBLIC, ACC_STATIC] "hello" [IntType] ReturnsVoid $ do+ setStackSize 8+ getStaticField Java.Lang.system Java.IO.out loadString "Здравствуй, мир!" invokeVirtual Java.IO.printStream Java.IO.println@@ -31,19 +47,24 @@ invokeStatic Java.Lang.integer Java.Lang.valueOfInteger aastore invokeVirtual Java.IO.printStream Java.IO.printf+ -- Call Hello.hello()+ invokeStatic "Hello" helloJava pop i0 RETURN + -- Main class method. newMethod [ACC_PUBLIC, ACC_STATIC] "main" [arrayOf Java.Lang.stringClass] ReturnsVoid $ do+ setStackSize 1+ iconst_5+ -- Call previously declared method invokeStatic "Test" hello i0 RETURN return () -testClass :: Class Resolved-testClass = generate "Test" test-+main :: IO () main = do+ testClass <- generateIO [] "Test" test B.writeFile "Test.class" (encodeClass testClass)
dump-class.hs view
@@ -15,7 +15,7 @@ case args of [clspath] -> do clsFile <- decodeFile clspath- putStrLn $ showListIx $ M.elems $ constsPool (clsFile :: Class Pointers)+ putStrLn $ showListIx $ M.assocs $ constsPool (clsFile :: Class File) cls <- parseClassFile clspath dumpClass cls _ -> error "Synopsis: dump-class File.class"
hs-java.cabal view
@@ -1,5 +1,5 @@ Name: hs-java-Version: 0.2+Version: 0.3.1 Cabal-Version: >= 1.6 License: BSD3 License-File: LICENSE@@ -11,6 +11,7 @@ Description: This package declares data types for Java .class files format and functions to assemble/disassemble Java bytecode. See dump-class.hs, rebuild-class.hs, TestGen.hs for examples of usage.+Bug-reports: http://redmine.iportnov.ru/projects/hs-java/ Extra-source-files: dump-class.hs rebuild-class.hs@@ -28,14 +29,24 @@ JVM.Exceptions Java.Lang Java.IO+ Java.ClassPath+ Java.ClassPath.Types+ Java.ClassPath.Common+ Java.JAR+ Java.JAR.Archive+ Java.META+ Java.META.Types+ Java.META.Spec+ Java.META.Parser Build-Depends: base >= 3 && <= 5, containers, binary, mtl, directory, filepath, utf8-string, array, bytestring, data-binary-ieee754, binary-state,- control-monad-exception+ control-monad-exception, data-default, MissingH,+ LibZip, Glob, parsec >= 3 && <4 ghc-options: -fwarn-unused-imports Source-repository head type: git- location: git remote add origin git@gitorious.org:hs-java/hs-java.git+ location: git@gitorious.org:hs-java/hs-java.git
rebuild-class.hs view
@@ -15,11 +15,11 @@ case args of [clspath,outpath] -> do cls <- parseClassFile clspath- clsfile <- decodeFile clspath :: IO (Class Pointers)+ clsfile <- decodeFile clspath :: IO (Class File) dumpClass cls- putStrLn $ "Source pool:\n" ++ showListIx (M.elems $ constsPool clsfile)- let result = classFile cls- putStrLn $ "Result pool:\n" ++ showListIx (M.elems $ constsPool result)+ putStrLn $ "Source pool:\n" ++ showListIx (M.assocs $ constsPool clsfile)+ let result = classDirect2File cls+ putStrLn $ "Result pool:\n" ++ showListIx (M.assocs $ constsPool result) B.writeFile outpath (encodeClass cls) _ -> error "Synopsis: rebuild-class File.class Output.class"