パッケージjava.lang.classfile
java.lang.classfile
は、JavaプラットフォームのプレビューAPIです。
クラス・ファイルの解析、生成および変換ライブラリを提供します。
java.lang.classfile
パッケージには、「Java Java Virtual Machine仕様」の第4章で指定されている、Javaクラス・ファイルの読取り、書込みおよび変更のためのクラスが含まれています。
クラス・ファイルの読取り中
クラス・ファイルを読み取るためのメイン・クラスはClassModel
PREVIEWで、ClassFile.parse(byte[])
PREVIEWを使用してバイトをClassModel
PREVIEWに変換します:
ClassModel cm = ClassFile.of().parse(bytes);
parse
には、様々な処理オプションを指定できるオーバーロードがいくつか追加されています。
ClassModel
PREVIEWは、クラス・ファイルの不変の説明です。 クラス・メタデータ(e.g., ClassModel.thisClass()
PREVIEW, ClassModel.flags()
PREVIEW)および下位クラス・ファイル・エンティティ(ClassModel.fields()
PREVIEW, AttributedElement.attributes()
PREVIEW)で取得するアクセッサ・メソッドを提供します。 ClassModel
PREVIEWは遅延して膨張します。クラス・ファイルのほとんどの部分は、実際に必要になるまで解析されません。
次の方法で、クラスのフィールドおよびメソッドの名前を列挙できます。
ClassModel cm = ClassFile.of().parse(bytes);
for (FieldModel fm : cm.fields())
System.out.printf("Field %s%n", fm.fieldName().stringValue());
for (MethodModel mm : cm.methods())
System.out.printf("Method %s%n", mm.methodName().stringValue());
メソッドを列挙すると、メソッドごとにMethodModel
PREVIEWが取得されます。ClassModel
のように、メソッド・メタデータへのアクセスと、メソッド本文のバイトコードなどの下位エンティティの子孫になる機能が提供されます。 このようにして、ClassModel
はツリーのルートであり、フィールド、メソッドおよび属性の子を持ち、MethodModel
は独自の子(attributes, CodeModel
, etc.)を持ちます。
ClassModel.methods()
PREVIEWのようなメソッドを使用すると、クラス構造を明示的に横断し、目的の部分に直接移動できます。 これは特定の種類の分析に役立ちますが、クラス・ファイル全体を処理する場合は、より整理されたものが必要な場合があります。 ClassModel
PREVIEWは、クラス・ファイルのビューを一連のクラスelementsとして提供します。これには、メソッド、フィールド、属性などが含まれ、パターン一致で区別できます。 前述の例は次のように書き換えることができます。
ClassModel cm = ClassFile.of().parse(bytes);
for (ClassElement ce : cm) {
switch (ce) {
case MethodModel mm -> System.out.printf("Method %s%n", mm.methodName().stringValue());
case FieldModel fm -> System.out.printf("Field %s%n", fm.fieldName().stringValue());
default -> { }
}
}
ClassModel
のトラバースから要素として戻されるモデルは、要素のソースになります。 クラス・ファイルを横断して、フィールドとメソッドにアクセスするすべてのクラスを列挙する場合、メソッドを記述するクラス要素を選択し、次にコード属性を記述するメソッド要素を選択し、最後にフィールド・アクセスと呼出し命令を記述するコード要素を選択します。
ClassModel cm = ClassFile.of().parse(bytes);
Set<ClassDesc> dependencies = new HashSet<>();
for (ClassElement ce : cm) {
if (ce instanceof MethodModel mm) {
for (MethodElement me : mm) {
if (me instanceof CodeModel xm) {
for (CodeElement e : xm) {
switch (e) {
case InvokeInstruction i -> dependencies.add(i.owner().asSymbol());
case FieldInstruction i -> dependencies.add(i.owner().asSymbol());
default -> { }
}
}
}
}
}
}
この同じ問合せを、クラス要素に対するストリーム・パイプラインとして処理することもできます。
ClassModel cm = ClassFile.of().parse(bytes);
Set<ClassDesc> dependencies =
cm.elementStream()
.flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty())
.flatMap(me -> me instanceof CodeModel com ? com.elementStream() : Stream.empty())
.<ClassDesc>mapMulti((xe, c) -> {
switch (xe) {
case InvokeInstruction i -> c.accept(i.owner().asSymbol());
case FieldInstruction i -> c.accept(i.owner().asSymbol());
default -> { }
}
})
.collect(toSet());
モデルと要素
このAPIによって提示されるクラス・ファイルのビューは、「モデル」およびelementsの観点からフレーム化されます。 モデルは、クラス、メソッド、フィールド、レコード要素、メソッドのコード本文などの複雑な構造を表します。 モデルは、ランダム・アクセス・ナビゲーション(ClassModel.methods()
PREVIEWアクセッサなど)を介して、またはelementsの線形シーケンスとして探索できます。 (要素はモデルでもあり、FieldModel
PREVIEWはクラスの要素でもあります。) モデル・タイプ(e.g., MethodModel
PREVIEW)ごとに、対応する要素タイプ(MethodElement
PREVIEW)があります。 モデルと要素は不変であり、遅延して膨張するため、モデルの作成では必ずしもコンテンツ全体を処理する必要はありません。
定数プール
クラス・ファイルの興味深いコンテンツの多くは、「定数プール」にあります。ClassModel
PREVIEWは、ClassModel.constantPool()
PREVIEWを介して定数プールのレイジー・インフレーションされた読取り専用ビューを提供します。 クラス・ファイル・コンテンツの説明は、多くの場合、ClassEntry
PREVIEWやUtf8Entry
PREVIEWなど、PoolEntry
PREVIEWの様々なサブタイプの形式で公開されます。
定数プール・エントリもモデルおよび要素を介して公開されます。前述のトラバース例では、InvokeInstruction
PREVIEW要素によって、定数プール内の Constant_Class_info
エントリに対応するowner
のメソッドが公開されています。
属性
クラス・ファイルの内容の多くは属性に格納され、属性はクラス、メソッド、フィールド、レコード・コンポーネントおよびCode
属性にあります。 ほとんどの属性は要素として表示されます。たとえば、SignatureAttribute
PREVIEWはClassElement
PREVIEW、MethodElement
PREVIEWおよびFieldElement
PREVIEWです。これは、これらのすべての場所に出現し、対応するモデルの要素の反復時に含まれるためです。
一部の属性は要素として表示されません。これらは密接に結合された属性です。 -- 論理的な部分 -- クラス・ファイルの他の部分。 これには、BootstrapMethods
, LineNumberTable
, StackMapTable
, LocalVariableTable
属性および LocalVariableTypeTable
属性が含まれます。 これらはライブラリによって処理され、(BootstrapMethods
属性のエントリは定数プールの一部として扱われ、行番号およびローカル変数メタデータはCodeModel
PREVIEWの要素としてモデル化されます。)と結合された構造の一部として処理されます
Code
属性は、MethodElement
PREVIEWとしてモデル化されるだけでなく、複雑な構造のため、独自の右(CodeModel
PREVIEW)のモデルでもあります。
各標準属性にはインタフェース(in java.lang.classfile.attribute
)があり、このインタフェースは属性の内容を公開し、属性を構成するためのファクトリを提供します。 たとえば、Signature
属性はSignatureAttribute
PREVIEWクラスによって定義され、SignatureAttribute.signature()
PREVIEWおよびUtf8Entry
PREVIEWまたはString
を使用するファクトリのアクセッサを提供します。
カスタム属性
属性は、クラス・ファイル形式とそれに対応するオブジェクト形式の間に、AttributeMapper
PREVIEWを介して変換されます。 AttributeMapper
は、クラス・ファイル形式から属性インスタンスへのマッピング用のAttributeMapper.readAttribute(AttributedElement, ClassReader, int)
PREVIEWメソッドと、クラス・ファイル形式へのマッピング用のAttributeMapper.writeAttribute(java.lang.classfile.BufWriter, java.lang.Object)
PREVIEWメソッドを提供します。 また、属性名、属性が適用可能なクラス・ファイル・エンティティのセット、同じ種類の複数の属性を単一のエンティティで許可するかどうかなどのメタデータも含まれます。
「Java Virtual Machine仕様」のセクション4.7で定義されている属性タイプごとに組込み属性マッパー(in Attributes
PREVIEW)と、JDKで使用される一般的な非標準属性(CharacterRangeTable
など)があります。
認識されない属性は、UnknownAttribute
PREVIEW型の要素として提供され、属性のbyte[]
コンテンツにのみアクセスできます。
非標準属性の場合、ユーザー指定の属性マッパーは、ClassFile.AttributeMapperOption.of(java.util.function.Function)
PREVIEW} クラス・ファイル・オプションを使用して指定できます。 カスタム属性の実装は、CustomAttribute
PREVIEWを拡張する必要があります。
オプション
ClassFile.of(java.lang.classfile.ClassFile.Option[])
PREVIEWは、オプションのリストを受け入れます。 ClassFile.Option
PREVIEWは、静的に列挙されたいくつかのオプションのベース・インタフェースであり、次のようなより複雑なオプションのファクトリです:
ClassFile.StackMapsOption
PREVIEW -- スタック・マップを生成 (デフォルトはSTACK_MAPS_WHEN_REQUIRED
です。)ClassFile.DebugElementsOption
PREVIEW -- ローカル変数メタデータ(デフォルトはPASS_DEBUG
です。)などのデバッグ情報の処理ClassFile.LineNumbersOption
PREVIEW -- 行番号(デフォルトはPASS_LINE_NUMBERS
です。)の処理ClassFile.AttributesProcessingOption
PREVIEW -- 認識されない、または問題のある元の属性 (デフォルトはPASS_ALL_ATTRIBUTES
です。)ClassFile.ConstantPoolSharingOption
PREVIEW} -- (デフォルトはSHARED_POOL
です。)の変換時に定数プールを共有ClassFile.ClassHierarchyResolverOption.of(java.lang.classfile.ClassHierarchyResolver)
PREVIEW -- スタック・マップ生成で使用されるカスタム・クラス階層リゾルバの指定ClassFile.AttributeMapperOption.of(java.util.function.Function)
PREVIEW -- カスタム属性の書式の指定
ほとんどのオプションでは、デバッグ情報や認識されない属性など、トラバース中にクラス・ファイルの特定の部分をスキップするようにリクエストできます。 一部のオプションでは、スタック・マップなど、クラス・ファイルの一部の生成を抑制できます。 これらのオプションの多くは、パフォーマンスのトレードオフにアクセスすることであり、デバッグ情報および行番号の処理にはコスト(書くことと読むことの両方。)があります。 この情報が必要ない場合は、オプションで抑制してパフォーマンスを向上させることができます。
クラス・ファイルの書込み
ClassFileの生成は、buildersによって行われます。 モデルを持つエンティティ・タイプごとに、対応するビルダー・タイプもあります。クラスは、ClassBuilder
PREVIEW、MethodBuilder
PREVIEWなどのメソッドを使用して構築されます。
ビルダーは、直接作成するのではなく、ユーザー指定のラムダへの引数として提供されます。 使い慣れた"こんにちは世界"プログラムを生成するには、クラス・ビルダーを要求し、そのクラス・ビルダーを使用してコンストラクタおよびmain
メソッドのメソッド・ビルダーを作成し、次にメソッド・ビルダーを使用してCode
属性を作成し、コード・ビルダーを使用して命令を生成します。
byte[] bytes = ClassFile.of().build(CD_Hello,
clb -> clb.withFlags(ClassFile.ACC_PUBLIC)
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void,
ClassFile.ACC_PUBLIC,
mb -> mb.withCode(
cob -> cob.aload(0)
.invokespecial(ConstantDescs.CD_Object,
ConstantDescs.INIT_NAME, ConstantDescs.MTD_void)
.return_()))
.withMethod("main", MTD_void_StringArray, ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC,
mb -> mb.withCode(
cob -> cob.getstatic(CD_System, "out", CD_PrintStream)
.ldc("Hello World")
.invokevirtual(CD_PrintStream, "println", MTD_void_String)
.return_())));
コンビニエンス・メソッドClassBuilder.buildMethodBody
を使用すると、メソッド・ビルダー・カスタム・ラムダをスキップして、メソッド本体を直接構築するコード・ビルダーを作成するようにClassBuilder
PREVIEWに依頼できます:
byte[] bytes = ClassFile.of().build(CD_Hello,
clb -> clb.withFlags(ClassFile.ACC_PUBLIC)
.withMethodBody(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void,
ClassFile.ACC_PUBLIC,
cob -> cob.aload(0)
.invokespecial(ConstantDescs.CD_Object,
ConstantDescs.INIT_NAME, ConstantDescs.MTD_void)
.return_())
.withMethodBody("main", MTD_void_StringArray, ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC,
cob -> cob.getstatic(CD_System, "out", CD_PrintStream)
.ldc("Hello World")
.invokevirtual(CD_PrintStream, "println", MTD_void_String)
.return_()));
ビルダーは、異なるレベルの抽象化で同じエンティティを表現する複数の方法をサポートすることがよくあります。 たとえば、println
を起動するinvokevirtual
命令は、CodeBuilder.invokevirtual
PREVIEW、CodeBuilder.invokeInstruction
PREVIEWまたはCodeBuilder.with
PREVIEWを使用して生成できます。
コンビニエンス・メソッドCodeBuilder.invokevirtual
は、コンビニエンス・メソッドCodeBuilder.invokeInstruction
をコールするかのように動作し、次にメソッドCodeBuilder.with
をコールするかのように動作します。 このビルダーでのメソッド・コールの構成により、変換(後で説明します)を作成できます。
シンボリック情報
クラスおよび型のシンボリック情報を記述するために、APIはClassDesc
やMethodTypeDesc
などのjava.lang.constant
の名目記述子抽象を使用します。これは、RAW文字列を使用するよりもエラーの発生が少なくなります。
定数プール・エントリに名目表現がある場合、対応する名目記述子型を返すメソッドを提供します。たとえば、メソッドClassEntry.asSymbol()
PREVIEWはClassDesc
を返します。
適切なビルダーは、シンボル情報を持つ要素を構築するための2つのメソッドを提供します。1つは名目記述子を受け入れ、もう1つは定数プール・エントリを受け入れます。
一貫性チェック、構文チェック、検証
クラス・ファイル(null引数チェックを除く)の構築中または変換中に整合性チェックは実行されません。 すべてのビルダーおよびクラス・ファイル要素のファクトリ・メソッドは、暗黙的な検証なしで提供された情報を受け入れます。 ただし、致命的な不整合(例:無効なコード・シーケンスまたは未解決のラベル)は内部ツールに影響し、後でクラス・ファイルの構築プロセスで例外が発生する可能性があります。
名目記述子を使用すると、実際のコンテキストに基づいて、ClassFile APIライブラリによって正しいシリアル形式が確実に適用されます。 また、これらの名目記述子は構築中に検証されるため、誤って無効な内容で作成することはできません。 次の例では、検証のためにクラス名をClassDesc.of(java.lang.String)
メソッドに渡し、クラス・エントリとして定数プールで直列化すると、ライブラリはクラス名の正しい内部形式への自動変換を実行します。
var validClassEntry = constantPoolBuilder.classEntry(ClassDesc.of("mypackage.MyClass"));
一方、定数プール・エントリを直接受け入れるビルダー・メソッドおよびファクトリを使用できます。 定数プール・エントリは、RAW値からも直接作成でき、追加の変換や検証は不要です。 次の例では、意図的に間違ったクラス名フォームを使用し、検証または変換を行わずに適用されます。
var invalidClassEntry = constantPoolBuilder.classEntry(
constantPoolBuilder.utf8Entry("mypackage.MyClass"));
クラス・ファイルのより複雑な検証は、ClassFile.verify(java.lang.classfile.ClassModel)
PREVIEWの呼出しによって実現できます。
クラス・ファイルの変換
ClassFile処理APIは、変換への読取りと書込みを組み合せるために最も頻繁に使用されます。この変換では、クラス・ファイルが読み取られ、ローカライズされた変更が行われますが、ほとんどのクラス・ファイルは変更されずに渡されます。 各種類のビルダーについて、XxxBuilder
にはメソッドwith(XxxElement)
があるため、変更せずに渡す要素をビルダーに直接戻すことができます。
名前が"debug"で始まるメソッドを削除する場合は、既存のClassModel
PREVIEWを取得し、ClassBuilder
PREVIEWを提供する新しいクラス・ファイルを作成し、元のClassModel
PREVIEWの要素を反復して、削除するメソッドを除くすべてのメソッドをビルダーに渡すことができます:
ClassModel classModel = ClassFile.of().parse(bytes);
byte[] newBytes = ClassFile.of().build(classModel.thisClass().asSymbol(),
classBuilder -> {
for (ClassElement ce : classModel) {
if (!(ce instanceof MethodModel mm
&& mm.methodName().stringValue().startsWith("debug"))) {
classBuilder.with(ce);
}
}
});
これは、名前がdebug
で始まるメソッドに対応するものを除き、すべてのクラス要素をビルダーに戻します。 もちろん、変換はより複雑で、メソッド本体や命令に潜入して変換することもできますが、すべてのエンティティに対応するモデル、ビルダーおよび要素の抽象化があるため、同じ構造がすべてのレベルで繰り返されます。
変換は、要素のシーケンスに対する"flatMap"操作とみなすことができます。すべての要素について、変更されていない要素に渡すか、削除するか、または1つ以上の要素で置換できます。 変換はクラス・ファイルに対して非常に一般的な操作であるため、各モデル型には対応する XxxTransform
型( XxxElement
のシーケンスに対する変換を記述します。)があり、各ビルダー型にはその子モデルを変換するためのtransformYyy
メソッドがあります。 変換は、ビルダーと要素を取り、実装の"flatMap"要素をビルダーに取り込む単なる機能インタフェースです。 上記のように表現できます:
ClassTransform ct = (builder, element) -> {
if (!(element instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug")))
builder.with(element);
};
var cc = ClassFile.of();
byte[] newBytes = cc.transform(cc.parse(bytes), ct);
ClassTransform.dropping
コンビニエンス・メソッドを使用すると、同じ変換構造を簡略化し、前述のように表現できます。
ClassTransform ct = ClassTransform.dropping(
element -> element instanceof MethodModel mm
&& mm.methodName().stringValue().startsWith("debug"));
持ち上がる変革
変換を使用する例はわずかに短くなりますが、この方法で変換を表現する利点は、変換操作をより簡単に結合できることです。Foo
の静的メソッドの呼出しを、かわりにBar
の対応するメソッドにリダイレクトするとします。 これは、CodeElement
PREVIEWで変換として表すことができます:
CodeTransform fooToBar = (b, e) -> {
if (e instanceof InvokeInstruction i
&& i.owner().asInternalName().equals("Foo")
&& i.opcode() == Opcode.INVOKESTATIC)
b.invokeInstruction(i.opcode(), CD_Bar, i.name().stringValue(), i.typeSymbol(), i.isInterface());
else b.with(e);
};
次に、コード要素に対するこの変換をメソッド要素の変換にliftできます。 これは、Code
属性に対応するメソッド要素をインターセプトし、そのコード要素にダイブしてコード変換を適用し、変更せずに他のメソッド要素を渡します。
MethodTransform mt = MethodTransform.transformingCode(fooToBar);
さらに、メソッド要素の変換をクラス要素の1つにリフトします。
ClassTransform ct = ClassTransform.transformingMethods(mt);
または、コード変換をクラスに直接変換します。
ClassTransform ct = ClassTransform.transformingMethodBodiess(fooToBar);
次に、クラス・ファイルを変換します。
var cc = ClassFile.of();
byte[] newBytes = cc.transform(cc.parse(bytes), ct);
これは、クラス・ファイル構造を直接トラバースすることによって表される同等のものよりもはるかに簡潔な(エラーが発生しやすい)です。
byte[] newBytes = ClassFile.of().build(classModel.thisClass().asSymbol(),
classBuilder -> {
for (ClassElement ce : classModel) {
if (ce instanceof MethodModel mm) {
classBuilder.withMethod(mm.methodName().stringValue(), mm.methodTypeSymbol(),
mm.flags().flagsMask(),
methodBuilder -> {
for (MethodElement me : mm) {
if (me instanceof CodeModel xm) {
methodBuilder.withCode(codeBuilder -> {
for (CodeElement e : xm) {
if (e instanceof InvokeInstruction i && i.owner().asInternalName().equals("Foo")
&& i.opcode() == Opcode.INVOKESTATIC)
codeBuilder.invokeInstruction(i.opcode(), CD_Bar,
i.name().stringValue(), i.typeSymbol(), i.isInterface());
else codeBuilder.with(e);
}});
}
else
methodBuilder.with(me);
}
});
}
else
classBuilder.with(ce);
}
});
変換の作成
同じタイプの要素の変換を順番に構成でき、最初の要素の出力は2番目の要素の入力に送られます。 すべてのメソッド・コールをインストゥルメントし、メソッドをコールする前にメソッドの名前を出力するとします。CodeTransform instrumentCalls = (b, e) -> {
if (e instanceof InvokeInstruction i) {
b.getstatic(CD_System, "out", CD_PrintStream)
.ldc(i.name().stringValue())
.invokevirtual(CD_PrintStream, "println", MTD_void_String);
}
b.with(e);
};
その後、fooToBar
およびinstrumentCalls
をCodeTransform.andThen(java.lang.classfile.CodeTransform)
PREVIEWで構成できます:
var cc = ClassFile.of();
byte[] newBytes = cc.transform(cc.parse(bytes),
ClassTransform.transformingMethods(
MethodTransform.transformingCode(
fooToBar.andThen(instrumentCalls))));
instrumentCalls
は、変換forToBar
によって生成されたすべてのコード要素(元のクラス・ファイルからのコード要素または置換(静的呼出しをFoo
にBar
に置換))を受け取ります。
定数プール共有
変換は、単に読取り、要素の変換および書込みのロジスティクスを処理するものではありません。 クラス・ファイルを変換するほとんどの場合、比較的小さな変更を行っています。 このようなケースを最適化するために、変換では、元のクラス・ファイルからの定数プールのコピーを使用して新しいクラス・ファイルがシードされます。これにより、(変換されないメソッドおよび属性は、バイトを解析してコンテンツを再生成するのではなく、バイトを一括コピーすることで処理できます。)が大幅に最適化されます。 定数プール共有が不要な場合は、ClassFile.ConstantPoolSharingOption
PREVIEWオプションで抑制できます。 このような抑制は、変換によって多くの要素が削除され、参照されない定数プール・エントリが多くなる場合に有益です。
不明なクラス・ファイル要素の変換処理
カスタムのクラス・ファイル変換では、将来のJDKリリースで導入されたクラス・ファイル要素が認識されない場合があります。 決定的な安定性を実現するために、すべてのクラス・ファイル要素の消費に関心のあるクラス・ファイル変換は、新しいJDKで実行されている場合、変換されたクラス・ファイルが新しいバージョンの場合、または新しいクラス・ファイル要素と不明なクラス・ファイル要素が表示される場合に、例外をスローするように厳密に実装する必要があります。 たとえば、次の厳密な互換性チェック変換スニペットのように:CodeTransform fooToBar = (b, e) -> {
if (ClassFile.latestMajorVersion() > ClassFile.JAVA_22_VERSION) {
throw new IllegalArgumentException("Cannot run on JDK > 22");
}
switch (e) {
case ArrayLoadInstruction i -> doSomething(b, i);
case ArrayStoreInstruction i -> doSomething(b, i);
default -> b.with(e);
}
};
ClassTransform fooToBar = (b, e) -> {
switch (e) {
case ClassFileVersion v when v.majorVersion() > ClassFile.JAVA_22_VERSION ->
throw new IllegalArgumentException("Cannot transform class file version " + v.majorVersion());
default -> doSomething(b, e);
}
};
CodeTransform fooToBar = (b, e) -> {
switch (e) {
case ArrayLoadInstruction i -> doSomething(b, i);
case ArrayStoreInstruction i -> doSomething(b, i);
case BranchInstruction i -> doSomething(b, i);
case ConstantInstruction i -> doSomething(b, i);
case ConvertInstruction i -> doSomething(b, i);
case DiscontinuedInstruction i -> doSomething(b, i);
case FieldInstruction i -> doSomething(b, i);
case InvokeDynamicInstruction i -> doSomething(b, i);
case InvokeInstruction i -> doSomething(b, i);
case LoadInstruction i -> doSomething(b, i);
case StoreInstruction i -> doSomething(b, i);
case IncrementInstruction i -> doSomething(b, i);
case LookupSwitchInstruction i -> doSomething(b, i);
case MonitorInstruction i -> doSomething(b, i);
case NewMultiArrayInstruction i -> doSomething(b, i);
case NewObjectInstruction i -> doSomething(b, i);
case NewPrimitiveArrayInstruction i -> doSomething(b, i);
case NewReferenceArrayInstruction i -> doSomething(b, i);
case NopInstruction i -> doSomething(b, i);
case OperatorInstruction i -> doSomething(b, i);
case ReturnInstruction i -> doSomething(b, i);
case StackInstruction i -> doSomething(b, i);
case TableSwitchInstruction i -> doSomething(b, i);
case ThrowInstruction i -> doSomething(b, i);
case TypeCheckInstruction i -> doSomething(b, i);
case PseudoInstruction i -> doSomething(b, i);
default ->
throw new IllegalArgumentException("An unknown instruction could not be handled by this transformation");
}
};
逆に、クラス・ファイル要素の一部のみの使用に関心のあるクラス・ファイル変換は、新しいクラス・ファイル要素や不明なクラス・ファイル要素を気にする必要はありません。 次に、このような将来に対応したコード変換の例を示します。
CodeTransform fooToBar = (b, e) -> {
switch (e) {
case ArrayLoadInstruction i -> doSomething(b, i);
case ArrayStoreInstruction i -> doSomething(b, i);
default -> b.with(e);
}
};
API規則
APIは、主にクラス・ファイル形式の「データ・モデル」から導出され、各要素の種類(モデルと属性を含む)とそのプロパティを定義します。 要素の種類ごとに、その要素を記述する対応するインタフェースと、その要素を作成するファクトリ・メソッドがあります。 一部の要素タイプには、対応するビルダー(e.g., CodeBuilder.invokevirtual(java.lang.constant.ClassDesc, java.lang.String, java.lang.constant.MethodTypeDesc)
PREVIEW)にもコンビニエンス・メソッドがあります。
要素内のほとんどのシンボリック情報は、定数プール・エントリ(たとえば、フィールドの所有者はClassEntry
PREVIEWで表されます。)で表されます ファクトリおよびビルダーは、java.lang.constant
(例: ClassDesc
。)の名目記述子も受け入れます。
データ・モデル
各種類の要素は、名前、オプションのアリティ・インジケータ(ゼロ以上、ゼロまたは1つのみ)およびコンポーネントのリストによって定義します。 クラスの要素は、フィールド、メソッド、およびクラスに表示される属性です。
ClassElement =
FieldModel*(UtfEntry name, Utf8Entry descriptor)
| MethodModel*(UtfEntry name, Utf8Entry descriptor)
| ModuleAttribute?(int flags, ModuleEntry moduleName, UtfEntry moduleVersion,
List<ModuleRequireInfo> requires, List<ModuleOpenInfo> opens,
List<ModuleExportInfo> exports, List<ModuleProvidesInfo> provides,
List<ClassEntry> uses)
| ModulePackagesAttribute?(List<PackageEntry> packages)
| ModuleTargetAttribute?(Utf8Entry targetPlatform)
| ModuleHashesAttribute?(Utf8Entry algorithm, List<HashInfo> hashes)
| ModuleResolutionAttribute?(int resolutionFlags)
| SourceFileAttribute?(Utf8Entry sourceFile)
| SourceDebugExtensionsAttribute?(byte[] contents)
| CompilationIDAttribute?(Utf8Entry compilationId)
| SourceIDAttribute?(Utf8Entry sourceId)
| NestHostAttribute?(ClassEntry nestHost)
| NestMembersAttribute?(List<ClassEntry> nestMembers)
| RecordAttribute?(List<RecordComponent> components)
| EnclosingMethodAttribute?(ClassEntry className, NameAndTypeEntry method)
| InnerClassesAttribute?(List<InnerClassInfo> classes)
| PermittedSubclassesAttribute?(List<ClassEntry> permittedSubclasses)
| DeclarationElement*
ここで、DeclarationElement
は、すべての宣言(クラス、メソッド、フィールド)に共通する要素であり、ファクタ・リングされます。
DeclarationElement =
SignatureAttribute?(Utf8Entry signature)
| SyntheticAttribute?()
| DeprecatedAttribute?()
| RuntimeInvisibleAnnotationsAttribute?(List<Annotation> annotations)
| RuntimeVisibleAnnotationsAttribute?(List<Annotation> annotations)
| CustomAttribute*
| UnknownAttribute*
CodeModel
PREVIEW (Code
属性をコード関連属性とともにモデル化する: スタック・マップ表、ローカル変数表、行番号表など)にあります
FieldElement =
DeclarationElement
| ConstantValueAttribute?(ConstantValueEntry constant)
MethodElement =
DeclarationElement
| CodeModel?()
| AnnotationDefaultAttribute?(ElementValue defaultValue)
| MethodParametersAttribute?(List<MethodParameterInfo> parameters)
| ExceptionsAttribute?(List<ClassEntry> exceptions)
CodeModel
PREVIEWは、その要素が「オーダー済み」であるという点で一意です。 Code
の要素には、通常のバイトコードに加え、ブランチ・ターゲット、行番号メタデータ、ローカル変数メタデータおよびcatchブロックを表す多数の擬似命令が含まれます。
CodeElement = Instruction | PseudoInstruction
Instruction =
LoadInstruction(TypeKind type, int slot)
| StoreInstruction(TypeKind type, int slot)
| IncrementInstruction(int slot, int constant)
| BranchInstruction(Opcode opcode, Label target)
| LookupSwitchInstruction(Label defaultTarget, List<SwitchCase> cases)
| TableSwitchInstruction(Label defaultTarget, int low, int high,
List<SwitchCase> cases)
| ReturnInstruction(TypeKind kind)
| ThrowInstruction()
| FieldInstruction(Opcode opcode, FieldRefEntry field)
| InvokeInstruction(Opcode opcode, MemberRefEntry method, boolean isInterface)
| InvokeDynamicInstruction(InvokeDynamicEntry invokedynamic)
| NewObjectInstruction(ClassEntry className)
| NewReferenceArrayInstruction(ClassEntry componentType)
| NewPrimitiveArrayInstruction(TypeKind typeKind)
| NewMultiArrayInstruction(ClassEntry componentType, int dims)
| ArrayLoadInstruction(Opcode opcode)
| ArrayStoreInstruction(Opcode opcode)
| TypeCheckInstruction(Opcode opcode, ClassEntry className)
| ConvertInstruction(TypeKind from, TypeKind to)
| OperatorInstruction(Opcode opcode)
| ConstantInstruction(ConstantDesc constant)
| StackInstruction(Opcode opcode)
| MonitorInstruction(Opcode opcode)
| NopInstruction()
PseudoInstruction =
| LabelTarget(Label label)
| LineNumber(int line)
| ExceptionCatch(Label tryStart, Label tryEnd, Label handler, ClassEntry exception)
| LocalVariable(int slot, UtfEntry name, Utf8Entry type, Label startScope, Label endScope)
| LocalVariableType(int slot, Utf8Entry name, Utf8Entry type, Label startScope, Label endScope)
| CharacterRange(int rangeStart, int rangeEnd, int flags, Label startScope, Label endScope)
- 導入されたバージョン:
- 22
-
クラス説明Preview.クラス、メソッドまたはフィールドのアクセス・フラグをモデル化します。Preview.宣言の注釈をモデル化します。Preview.注釈のキーと値のペアをモデル化します。Preview.注釈のキーと値のペアの値をモデル化します。Preview.annotation-valued要素をモデル化します。Preview.array-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.class-valued要素をモデル化します。Preview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.enum-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.constant-valued要素をモデル化するPreview.クラス・ファイル属性4.7をモデル化します。Preview.クラス、フィールド、メソッド、コード属性、レコード・コンポーネントなどの属性を持つエンティティを記述する
ClassFileElement
PREVIEW。Preview.属性のクラス・ファイル表現と、その属性がAPIでどのようにモデル化されるかの間の双方向マッパー。Preview.属性安定性インジケータPreview.標準クラス・ファイル属性の属性マッパー。Preview.ブートストラップ・メソッド表のエントリをモデル化します。Preview.拡張可能バッファへのクラス・ファイルの一部の書込みをサポートします。Preview.クラス・ファイルのビルダー。Preview.ClassModel
PREVIEWをトラバースするとき、またはClassBuilder
PREVIEWに提示される要素のためのマーカー・インタフェース。Preview.クラス・ファイルの解析、変換および生成のコンテキストを表します。Preview.カスタム属性の属性マッパーを記述するオプション。Preview.クラス、レコード・コンポーネント、フィールド、メソッドまたはコードが展開形式で変換されたときに、認識されない元の属性または問題のある元の属性を処理または破棄するかどうかを示すオプション。Preview.スタック・マップの生成時に使用するクラス階層リゾルバを記述するオプション。Preview.クラス・ファイルの変換時に元の定数プールを保持するかどうかを示すオプション。Preview.到達不可能なコードにパッチを適用するかどうかを示すオプション。Preview.未解決のラベルをフィルタするかどうかを説明するオプション。Preview.デバッグ要素を処理するか破棄するかを示すオプション。Preview.明細番号を処理するか破棄するかを示すオプション。Preview.クラス・ファイルの解析および書込みに影響するオプション。Preview.必要に応じて短いジャンプをlongに自動的にリライトするかどうかを示すオプション。Preview.スタック・マップを生成するかどうかを示すオプション。ClassFileBuilderPREVIEW<E extends ClassFileElementPREVIEW,B extends ClassFileBuilderPREVIEW<E, B>> Preview.クラス・ファイルまたはクラス・ファイルの一部のビルダー。Preview.クラス・ファイル(または、)の一部に対する不変モデル。ClassFileTransformPREVIEW<C extends ClassFileTransformPREVIEW<C,E, B>, E extends ClassFileElementPREVIEW, B extends ClassFileBuilderPREVIEW<E, B>> Preview.要素のストリームに対する変換。Preview.変換をビルダーにバインドした結果。Preview.クラスのクラス・ファイルのバージョン情報をモデル化します。Preview.コード構築中に正しいスタック・マップを生成するためのクラス階層情報を提供します。Preview.解決済みクラスに関する情報。Preview.クラス・ファイルをモデル化します。Preview.クラス・ファイルからの読み取りをサポートします。Preview.4.7.9で定義されているクラス・ファイルの汎用シグネチャをモデル化します。Preview.ClassElement
PREVIEWのストリームでの変換。Preview.コード属性(メソッド本体)のビルダー。Preview.コードのブロック用のビルダー。Preview.catchブロックを追加するビルダー。Preview.CodeModel
PREVIEWをトラバースするとき、またはCodeBuilder
PREVIEWに提示される要素のためのマーカー・インタフェース。Preview.メソッド(Code
属性)の本文をモデル化します。Preview.CodeElement
PREVIEWのストリームでの変換。CompoundElementPREVIEW<E extends ClassFileElementPREVIEW>Preview.メソッド、フィールド、メソッド本文、クラス全体など、他のクラス・ファイル要素に関して定義された複雑な構造を持つClassFileElement
PREVIEW。CustomAttributePREVIEW<T extends CustomAttributePREVIEW<T>>Preview.クラス・ファイルの非標準属性をモデル化します。Preview.フィールドのビルダー。Preview.FieldModel
PREVIEWをトラバースするとき、またはFieldBuilder
PREVIEWに提示される要素のためのマーカー・インタフェース。Preview.フィールドをモデル化します。Preview.FieldElement
PREVIEWのストリームでの変換。Preview.メソッド本文内の実行可能命令をモデル化します。Preview.クラスのインタフェースをモデル化します。Preview.メソッド本文の命令内の位置のマーカー。Preview.メソッドのビルダー。Preview.MethodModel
PREVIEWをトラバースするとき、またはMethodBuilder
PREVIEWに提示される要素のためのマーカー・インタフェース。Preview.メソッドをモデル化します。Preview.4.7.9で定義されているメソッドの汎用シグネチャをモデル化します。Preview.MethodElement
PREVIEWのストリームでの変換。Preview.6.5で説明されているように、JVM命令セットのopcodeについて説明します。Preview.opcodesの種類Preview.例外表のエントリ、行番号表、ローカル変数表、命令とラベルの間のマッピングなど、CodeAttribute
PREVIEWに関するメタデータをモデル化します。Preview.4.7.9.1で定義されている汎用Javaタイプ・シグネチャをモデル化します。Preview.配列型のシグネチャをモデル化します。Preview.プリミティブ型またはvoidのシグネチャをモデル化します。Preview.パラメータ化された可能性があるクラスまたはインタフェース型のシグネチャをモデル化します。Preview.参照型(クラス、インタフェース、型変数または配列型)のシグネチャをモデル化します。Preview.スロー可能タイプのシグネチャをモデル化します。Preview.型引数をモデル化します。Preview.ワイルドカードにデフォルト境界、バインドなし、上限または下限があるかどうかを示すインジケータPreview.汎用クラスまたはメソッドの型パラメータのシグネチャをモデル化します。Preview.型変数のシグネチャをモデル化します。Preview.クラスのスーパークラスをモデル化します。Preview.例外パラメータ宣言のi番目の型に注釈が表示されることを示します。Preview.フィールド宣言の型、メソッドの戻り型、新しく構築されたオブジェクトの型、またはメソッドまたはコンストラクタの受信側型に注釈が表示されることを示します。Preview.メソッド、コンストラクタまたはラムダ式の仮パラメータ宣言で、型に注釈が表示されることを示します。Preview.try-with-resources文でリソースとして宣言された変数を含む、ローカル変数宣言の型に注釈が表示されることを示します。Preview.ローカル変数が値を持つコード配列オフセットの範囲と、そのローカル変数が見つかる現在のフレームのローカル変数配列へのインデックスを示します。Preview.注釈がinstanceof式または新しい式の型、または次の前の型に表示されることを示します。 : メソッド参照式内。Preview.クラス宣言またはインタフェース宣言のextends句またはimplements句の型に注釈が表示されることを示します。Preview.宣言または式のどの型に注釈を付けるかを指定します。Preview.4.7.20.1で定義されている、注釈が表示されるターゲットの種類。Preview.メソッド宣言またはコンストラクタ宣言のthrows句のi番目の型に注釈が表示されることを示します。Preview.注釈がキャスト式のi番目の型、または次のいずれかの明示的な型引数リストのi番目の型引数に表示されることを示します: 新しい式、明示的なコンストラクタ呼出し文、メソッド呼出し式またはメソッド参照式。Preview.注釈が、汎用クラス、インタフェース、メソッドまたはコンストラクタのj番目の型パラメータ宣言のi番目の境界に表示されることを示します。Preview.注釈が、汎用クラス、汎用インタフェース、汎用メソッドまたは汎用コンストラクタのi番目の型パラメータの宣言に表示されることを示します。Preview.JVMS: Type_path構造は、4.7.20.2で定義されているように、型のどの部分に注釈が付けられているかを識別します。Preview.タイプ・パスの種類(4.7.20.2で定義)Preview.フィールドまたはメソッド記述子の一部にできる型について説明します。Preview.クラス・ファイル形式で必要なエンコーディングで、バイトのストリームとして自身をエンコードできるクラス・ファイル要素。
java.lang.classfile
を使用できます。