1

你好,我得到了在 Haskell 中运行的代码,我对这种语言有点陌生。我得到它来编译,但我不知道如何完全使用它。通过查看它,我相信它应该解析一个 java 类文件,我已经尝试解析 file.class,但它给了我一个“无法加载接口文件名”。如果有人能指出我做错了什么(我敢肯定这是小事),我将不胜感激。

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]
4

1 回答 1

1

正如hairyhum 所说,您可以从GHCi 调用它。

但是,如果您真正想做的是从程序中调用它(也许您与提出此问题的人在同一个班级:Dissecting java Class file in haskell),然后将其放在与您的相同目录中主程序,并在程序的顶部,输入:

import Parse

然后在您的程序中,读取 Java 类文件。你可能见过这样的事情:

s <- readFile "MyJava.class"

如果我们想将文件的内容读入String. 但是该parse命令需要一个ByteString,所以我们需要使用一个不同的实现readFile。因此,在程序的顶部,输入:

import qualified Data.ByteString as BS

现在您可以像这样读取文件:

s <- BS.readFile "MyJava.class"

现在你有了类数据,可以调用parse命令了。这应该足以帮助您完成该部分的作业。


更新

让我们看看parse给定代码中函数的类型签名。

parse :: B.ByteString -> JavaClass

所以parse需要 aByteString并返回 a JavaClass。现在让我们看一下 的定义JavaClass

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

如所写,所有parse将给您的是次要版本、主要版本和常量池计数。但是如果你分析代码,你应该能够看到如何修改它以获得你想要的附加信息。我建议您详细分析该代码以了解其工作原理。


更新 2

如果你只是想在 GHCI 中尝试一下,你可以这样做:

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

这是一个 GHCi 会话,我正是这样做的。

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}

但是要进行下一步,您将编写一个程序来调用parse,如我上面所描述的。假设您的程序位于名为“MyProgram.hs”的文件中。然后,您可以通过键入从命令行runghc MyProgram.hs运行它。

我推荐阅读Real World Haskell的第 1 章。

编辑:将“class”更改为“classData”,因为“class”是 Haskell 关键字。

于 2013-04-04T15:52:21.380 回答