インジェクション
導入
インジェクションを用いると、既存のメソッドの任意の場所にコードを挿入することができます。実例を見たい方は、このページ下部の 実例 を参照してください。以下はインジェクションのメソッドの基本のコードです:
@Inject(method = "メソッド記述子", at = @At("インジェクションポイント")) private void injectMethod(METHOD ARGS, CallbackInfo info) { }
Injection Point Reference に全てのインジェクションポイントがあります。以下のリストは主要なものです:
| インジェクションポイント | 説明 |
|---|---|
| HEAD | メソッドの最初 |
| RETURN | 全ての return 文の前 |
| TAIL | 最後の return 文の前 |
| INVOKE | メソッドが呼ばれたとき |
インジェクションポイントが文またはメンバを指すとき、@At 内の target に目的の値を設定することができます。その際、 JVM バイトコード記述子として設定します。
Oracle は以下の フィールド記述子 を定めています:
| 記述子 | 型 | 説明 |
|---|---|---|
| B | byte | 符号付きバイト |
| C | char | UTF-16 でエンコードされた、基本多言語面の Unicode コードポイント値 |
| D | double | 倍精度浮動小数点数 |
| F | float | 単精度浮動小数点数 |
| I | int | 符号付き整数 |
| J | long | 符号付き長整数 |
| S | short | 符号付き短整数 |
| Z | boolean | true または false |
| LClassName; | 参照 | ClassName のインスタンス |
| [ | 参照 | 配列の 1 次元 |
メソッド記述子は、メソッド名、括弧で囲まれた引数の型、返り値の型によって構成されます。例えば、Object m(int i, double[] d, Thread t) と定義されたメソッドの記述子は m(I[DLjava/lang/Thread;)Ljava/lang/Object; になります。
メソッドの返り値の型が void である場合、V (Void 記述子型)を型として用います。void foo(String bar) の記述子は foo(Ljava/lang/String;)V です。
ジェネリックは実行時に存在しないため、それらの型は省略されます。Pair<Integer, ? extends Task<? super VillagerEntity>> の記述子は Lcom/mojang/datafixers/util/Pair です。
インジェクションのメソッドの返り値の型は常に void です。一方、メソッド名やアクセス修飾子は任意であるため、そのインジェクションの内容に適するようにしてください。また、インジェクションのメソッドの引数が目的のメソッドの引数と同じになるようにして、最後に CallbackInfo 型の引数が 1 つ渡されるようにしてください。目的のメソッドの返り値の型が (T) である場合は、CallbackInfo 型の代わりに CallbackInfoReturnable<T> 型を使用してください。
インジェクションのメソッドからの return とキャンセル
インジェクションのメソッド内で目的のメソッドの処理をキャンセルしたり、早期 return したりしたい場合は、CallbackInfo#cancel または CallbackInfoReturnable<T>#setReturnValue(T) を使用してください。その際、@Inject 内で cancellable に true を設定することと、setReturnValue を呼んだあとに cancel を呼ばないようにすることに注意してください。
@Inject(method = "...", at = @At("..."), cancellable = true)
コンストラクタへのインジェクション
コンストラクタにインジェクションを行う場合は、<init>()V が目的のメソッド記述子となるようにします。その際、() 内にコンストラクタの引数の記述子を設定してください。公式にサポートされているインジェクションポイントの値は TAIL または RETURN のみです。いくつかのクラスは <init> と異なる init と名付けられたメソッドを持つため、混乱しないように注意してください。
static なコンストラクタにインジェクションを行う場合は、メソッド名が <clinit> になるようにしてください。
実例
以下のコードは TitleScreen#init メソッドの先頭で出力を行う例です。(init はメソッド名であり、コンストラクタではありません。)
@Mixin(TitleScreen.class) public class ExampleMixin { @Inject(at = @At("HEAD"), method = "init()V") private void init(CallbackInfo info) { System.out.println("This line is printed by an example mod mixin!"); // この行は example mod の mixin によって出力されます! } }
この例に関して更なる情報が欲しい方は、Fabric Example Mod リポジトリ を参照してください。