Javaでクラスパーサを実装する方法の例
この記事では主に、クラスファイルの分析を通じてJavaクラスパーサーの実装方法の例を紹介します。これは一定の参考価値があり、必要な友人がそれについて学ぶことができます。
私は現在、ClassAnalyzer というプライベート プロジェクトを書いています。ClassAnalyzer の目的は、Java クラス ファイルの設計と構造を深く理解することです。主要な枠組みと基本的な機能は完成していますが、今後、いくつかの詳細な機能が追加される予定です。実際、JDK は Class ファイルを逆コンパイルするためのコマンド ライン ツール javap をすでに提供していますが、この記事ではパーサーの実装に関する私の考えを明確にします。
クラス ファイル
クラスまたはインターフェイス情報のキャリアとして、各クラス ファイルはクラスを完全に定義します。 Java プログラムを「一度書けばどこでも実行できる」ようにするために、Java 仮想マシンの仕様では Class ファイルに厳しい規制が設けられています。 Class ファイルを構成する基本的なデータ単位はバイトです。このため、クラス ファイル全体に格納される内容は、1 バイトでは表現できないデータとなります。複数 連続したバイトで表されます。
Java 仮想マシンの仕様によれば、クラス ファイルは C 言語の構造に似た擬似構造を使用してデータを保存します。この擬似構造には符号なし数値とテーブルの 2 つのデータ型のみがあります。 Java 仮想マシンの仕様では、u1、u2、u4、および u8 がそれぞれ 1 バイト、2 バイト、4 バイト、および 8 バイトの符号なし数値を表すように定義されています。符号なし数値は、参照、数量、または文字列を記述するために使用できます。テーブルは、データ項目として複数の符号なし数値または他のテーブルで構成される複合データ型であり、階層複合構造でデータを記述するために使用されるため、クラス ファイル全体が本質的にテーブルになります。 ClassAnalyzer では、byte、short、int、long がそれぞれ u1、u2、u4、u8 のデータ型に対応します。Class ファイルは次の Java クラスとして記述されます。
public class ClassFile { public U4 magic; // magic public U2 minorVersion; // minor_version public U2 majorVersion; // major_version public U2 constantPoolCount; // constant_pool_count public ConstantPoolInfo[] cpInfo; // cp_info public U2 accessFlags; // access_flags public U2 thisClass; // this_class public U2 superClass; // super_class public U2 interfacesCount; // interfaces_count public U2[] interfaces; // interfaces public U2 fieldsCount; // fields_count public FieldInfo[] fields; // fields public U2 methodsCount; // methods_count public MethodInfo[] methods; // methods public U2 attributesCount; // attributes_count public BasicAttributeInfo[] attributes; // attributes }
クラスファイルを構成する各データ項目(マジックナンバー、クラスファイルのバージョン、その他のデータ項目、アクセスフラグ、クラスインデックス、親クラスインデックスなど)の解析方法
は各クラス ファイル内にあり、それぞれが固定バイト数を占有し、解析中に対応するバイト数のみを読み取る必要があります。また、柔軟に対応する必要がある部分は主に、定数プール、フィールドテーブルコレクション、メソッドテーブルコレクション、属性テーブルコレクションの4つです。フィールドとメソッドは独自の属性を持つことができ、クラス自体にも対応する属性があるため、フィールド テーブル コレクションとメソッド テーブル コレクションの解析には属性テーブルの解析も含まれます。
定数プールはクラス ファイル内のデータの大部分を占め、数値定数、文字列定数、クラス名、インターフェイス名、フィールド名、メソッド名などを含むすべての定数情報を保存するために使用されます。 Java 仮想マシンの仕様では複数の定数タイプが定義されており、それぞれが独自の構造を持っています。定数プール自体はテーブルであり、解析する際に注意すべき点がいくつかあります。
各定数型は、u1 型タグによって識別されます。
ヘッダーに指定された定数プール サイズ (constantPoolCount) は、実際の値より 1 大きくなります。たとえば、constantPoolCount が 47 に等しい場合、定数プールには 46 個の定数があります。
定数プールのインデックス範囲は 1 から始まります。たとえば、constantPoolCount が 47 の場合、定数プールのインデックス範囲は 1 ~ 46 です。項目 0 を空のままにする設計者の目的は、「定数プール項目を参照しない」ことを表現することです。
CONSTANT_Utf8_info 定数の構造には、タイプ u1 のタグ、タイプ u2 の length バイト、およびタイプ u1 の length バイトの連続データが MUTF-8 (Modified UTF-8) 文字列を使用してエンコードされます。 MUTF-8 は UTF-8 と互換性がありません。主な違いは 2 つあります。1 つ目は、NULL 文字が 2 バイト (0xC0 および 0x80) にエンコードされることです。2 つ目は、補助文字がサロゲート ペアに分割され、UTF に従って個別にエンコードされることです。 -16 、関連する詳細はここにあります (バリアント UTF-8)。
属性テーブルは、特定のシナリオに固有の情報を記述するために使用されます。クラス ファイル、フィールド テーブル、メソッド テーブルにはすべて、対応する属性テーブル セットがあります。 Java 仮想マシン仕様ではさまざまな属性が定義されており、現在 ClassAnalyzer は一般的に使用される属性の分析を実装しています。定数タイプのデータ項目とは異なり、属性には属性のタイプを識別するためのタグがありませんが、各属性にはタイプ u2 のattribute_name_indexが含まれており、Attribute_name_indexは、属性の名前を含む定数プール内のタイプCONSTANT_Utf8_infoの定数を指します。属性を解析するとき、ClassAnalyzer は、attribute_name_index が指す定数に対応する属性名を通じて属性のタイプを認識します。
フィールド テーブルは、クラスまたはインターフェイスで宣言された変数を記述するために使用されます。フィールドには、クラス レベルの変数とインスタンス レベルの変数が含まれます。フィールドテーブルの構造は、u2タイプaccess_flags、u2タイプname_index、u2タイプdescriptor_index、u2タイプattributes_count、attributes_countattributes_infoタイプの属性を含む。属性テーブルの解析についてはすでに紹介しましたが、属性の解析方法は属性テーブルの解析方法と一致しています。
Class的文件方法表采用了和字段表相同的存储格式,只是access_flags对应的含义有所不同。方法表包含着一个重要的属性:Code属性。Code属性存储了Java代码编译成的字节码指令,在ClassAnalyzer中,Code对应的Java类如下所示(仅列出了类属性)
public class Code extends BasicAttributeInfo { private short maxStack; private short maxLocals; private long codeLength; private byte[] code; private short exceptionTableLength; private ExceptionInfo[] exceptionTable; private short attributesCount; private BasicAttributeInfo[] attributes; ... private class ExceptionInfo { public short startPc; public short endPc; public short handlerPc; public short catchType; ... } }
在Code属性中,codeLength和code分别用于存储字节码长度和字节码指令,每条指令即一个字节(u1类型)。在虚拟机执行时,通过读取code中的一个个字节码,并将字节码翻译成相应的指令。另外,虽然codeLength是一个u4类型的值,但是实际上一个方法不允许超过65535条字节码指令。
代码实现
ClassAnalyzer的源码已放在了GitHub上。在ClassAnalyzer的README中,我以一个类的Class文件为例,对该Class文件的每个字节进行了分析,希望对大家的理解有所帮助。
以上がJavaでクラスパーサを実装する方法の例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Spring Bootは、Java開発に革命をもたらす堅牢でスケーラブルな、生産対応のJavaアプリケーションの作成を簡素化します。 スプリングエコシステムに固有の「構成に関する慣習」アプローチは、手動のセットアップを最小化します。
