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

したがって、 a をparse受け取り、 aByteStringを返します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」は Haskell キーワードであるため、「class」を「classData」に変更しました。

于 2013-04-04T15:52:21.380 に答える