man123456
man123456

Reputation: 63

haskell command line

Hello I was given code to run in Haskell and I am a bit new to this language. I get it to compile but I don't know how to quite use it. By looking through it I believe it is supposed to parse a java class file and I have tried parse file.class but its giving me a "failed to load interface file name". If someone could point out what I'm doing wrong (I'm sure its something small) I would appreciate it.

module Parse where

import Data.Binary
import Data.Binary.Get
import Data.Word
import Control.Monad
import qualified Data.ByteString          as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Lazy     as L

{-
    ClassFile {
        u4 magic;
        u2 minor_version;
        u2 major_version;
        u2 constant_pool_count;
        cp_info constant_pool[constant_pool_count-1];
        u2 access_flags;
        u2 this_class;
        u2 super_class;
        u2 interfaces_count;
        u2 interfaces[interfaces_count];
        u2 fields_count;
        field_info fields[fields_count];
        u2 methods_count;
        method_info methods[methods_count];
        u2 attributes_count;
        attribute_info attributes[attributes_count];
     }
-}

getWord16 :: Get Word16
getWord16 = getWord16be

getWord32 :: Get Word32
getWord32 = getWord32be

data JavaClass = JavaClass {
    classMinorVersion :: Word16
  , classMajorVersion :: MajorVersion
  , classConstantPoolCount :: Word16
  } deriving Show

getJavaClass :: Get JavaClass
getJavaClass = do
  checkClassMagic
  minorVersion   <- getMinorVersion
  majorVersion   <- getMajorVersion
  constPoolCount <- getConstantsPoolCount
  return $ JavaClass minorVersion majorVersion constPoolCount

checkClassMagic :: Get ()
checkClassMagic = do
  magic <- getWord32
  if magic /= 0xCAFEBABE then
    fail "Invalid magic number for Java class"
    else
    return ()

getMinorVersion :: Get Word16
getMinorVersion = getWord16 >>= return

{-
J2SE 7.0 = 51 (0x33 hex),
J2SE 6.0 = 50 (0x32 hex),
J2SE 5.0 = 49 (0x31 hex),
JDK 1.4 = 48 (0x30 hex),
JDK 1.3 = 47 (0x2F hex),
JDK 1.2 = 46 (0x2E hex),
JDK 1.1 = 45 (0x2D hex).
-}

data MajorVersion
  = J2SE_7_0
  | J2SE_6_0
  | J2SE_5_0
  | JDK_1_4
  | JDK_1_3
  | JDK_1_2
  | JDK_1_1
  deriving (Eq, Show)

getMajorVersion :: Get MajorVersion
getMajorVersion = do
    version <- getWord16be
    return $ convert version
    where convert 51 = J2SE_7_0
          convert 50 = J2SE_6_0
          convert 49 = J2SE_5_0
          convert 48 = JDK_1_4
          convert 47 = JDK_1_3
          convert 46 = JDK_1_2
          convert 45 = JDK_1_1

getConstantsPoolCount :: Get Word16
getConstantsPoolCount = getWord16 >>= return

getConstantsPool = undefined

data Tag
 = TAG_STRING
 | TAG_INTEGER
 | TAG_FLOAT
 | TAG_LONG
 | TAG_DOUBLE
 | TAG_CLASS_REF
 | TAG_STRING_REF
 | TAG_FIELD_REF
 | TAG_METHOD_REF
 | TAG_INTERFACE_REF
 | TAG_NAME_AND_TYPE

getTag :: Get Tag
getTag = do
  tag <- getWord8
  return $ convert tag
  where convert 1 = TAG_STRING
        convert 3 = TAG_INTEGER
        convert 4 = TAG_FLOAT
        convert 5 = TAG_LONG
        convert 6 = TAG_DOUBLE
        convert 7 = TAG_CLASS_REF
        convert 8 = TAG_STRING_REF
        convert 9 = TAG_FIELD_REF
        convert 10 = TAG_METHOD_REF
        convert 11 = TAG_INTERFACE_REF
        convert 12 = TAG_NAME_AND_TYPE

parse :: B.ByteString -> JavaClass
parse b = runGet getJavaClass $ L.fromChunks [b]

Upvotes: 0

Views: 1042

Answers (1)

mhwombat
mhwombat

Reputation: 8126

You can invoke it from GHCi as hairyhum says.

But if what you're really trying to do is to call it from a program ( perhaps you're in the same class as the person who asked this: Dissecting java Class file in haskell), then put it in the same directory as your main program, and at the top of the program, put:

import Parse

Then in your program, read the Java class file. You've probably seen something like this:

s <- readFile "MyJava.class"

That would work if we wanted to read the contents of the file into a String. But the parse command expects a ByteString, so we need to use a different implementation of readFile. So at the top of your program, put:

import qualified Data.ByteString as BS

Now you can read the file like so:

s <- BS.readFile "MyJava.class"

Now you have the class data, and can invoke the parse command. That should be enough information to help you finish that part of the assignment.


UPDATE

Let's look at the type signature for the function parse in the code you were given.

parse :: B.ByteString -> JavaClass

So parse takes a ByteString and returns a JavaClass. Now let's look at the definition of JavaClass.

data JavaClass = JavaClass {
    classMinorVersion :: Word16
  , classMajorVersion :: MajorVersion
  , classConstantPoolCount :: Word16
  } deriving Show

So as written, all parseis going to give you is the minor version, major version, and the constant pool count. But if you analyse the code, you should be able to see how to modify it to get the additional information you want. I suggest you analyse that code in detail to understand how it works.


UPDATE 2

If you just want to try it out in GHCI, you could do something like this:

:load Parse
:m + Data.ByteString
classData <- Data.ByteString.readFile "file.class"
Parse.parse classData

Here's a GHCi session where I did exactly that.

ghci> :load Parse
[1 of 1] Compiling Parse            ( Parse.hs, interpreted )
Ok, modules loaded: Parse.
ghci> :m + Data.ByteString
ghci> classData <- Data.ByteString.readFile "file.class"
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package bytestring-0.10.0.2 ... linking ... done.
ghci> Parse.parse classData
Loading package containers-0.5.0.0 ... linking ... done.
Loading package binary-0.7.0.1 ... linking ... done.
JavaClass {classMinorVersion = 3, classMajorVersion = JDK_1_1, classConstantPoolCount = 85}

But to take it to the next step, you would write a program to invoke parse as I've described above. Suppose your program is in a file called "MyProgram.hs". Then you can run it from the command line by typing runghc MyProgram.hs.

I recommend reading Chapter 1 of Real World Haskell.

EDIT: Changed "class" to "classData" because "class" is a Haskell keyword.

Upvotes: 1

Related Questions