@At
注解的value
属性只有以下的有限取值,这个取值也限定了args
、target
、ordinal
、opcode
属性的取值:
表示注入到方法的开头,对应的注入方法在目标方法最开始被调用。
args
: 无target
: 无ordinal
: 无opcode
: 无
表示注入到RETURN
操作符之前。
args
: 无target
: 无ordinal
: 指定注入到哪个RETURN
操作符,RETURN
计数从0开始。如果不指定,表示注入到所有RETURN
操作符之前opcode
: 无
对于@Inject
而且,这是注入构造方法时唯一一个允许注入的地方。如果注入到一个带返回值的方法中,则调用CallbackInfoReturnable::getReturnType()
会得到目标方法即将返回的值,在其他地方注入时调用此方法则会返回null
表示注入到最后一个RETURN
操作符之前。
args
: 无target
: 无ordinal
: 无opcode
: 无
注意,最后一个RETURN
操作符有时并不等同于Java代码中最后一个return
关键词,你可能需要用某些查看字节码的IDE插件确认
表示注入到一个方法被调用之前。
args
: 允许下列参数:log
: 一个布尔值,填log=true
则表示会输出相关的日志信息
target
: 指定一个带完整限定名的方法签名,如果不指定,则表示匹配所有的方法调用,以下写法是合法的:org.lwjgl.opengl.Display.setTitle(Ljava/lang/String;)V
org/lwjgl/opengl/Display.setTitle(Ljava/lang/String;)V
Lorg/lwjgl/opengl/Display;setTitle(Ljava/lang/String;)V
ordinal
: 指定注入到哪个被target
匹配的目标前,计数从0开始。如果不指定,表示注入到所有匹配的目标之前opcode
: 无
表示注入到一个仅有一个String
类型的参数且无返回类型的方法被调用之前,且传入的必须是常量字符串。
args
: 允许下列参数:ldc
: 这个参数必须存在,表示匹配指定传入String
参数的值,例如ldc=Minecraft 1.12.2
log
: 同INVOKE
target
: 同INVOKE
ordinal
: 指定注入到哪个同时被target
和ldc
参数匹配的目标前,计数从0开始opcode
: 无
表示注入到一个有返回类型的方法被调用之后,如果这个方法被调用之后的值会立即传入一个局部变量,则会在传入局部变量之后注入(也就是*STORE
操作符之后)。
args
: 同INVOKE
target
: 同INVOKE
ordinal
: 指定注入到哪个被target
匹配的目标后,计数从0开始。如果不指定,表示注入到所有匹配的目标之后opcode
: 无
表示注入到一个字段被引用之前。
args
: 允许下列参数:array
: 仅当target
指向一个数组元素相关操作的目标时可用,有以下取值:array=get
: 匹配数组元素被引用的时候array=set
: 匹配数组元素被赋值的时候array=length
: 匹配数组的length
属性被引用的时候
log
: 同INVOKE
target
: 指定一个带完整限定名的字段签名,如果不指定,则匹配所有的符合opcode
的字段引用,以下写法是合法的:net.minecraft.client.Minecraft.instance:Lnet/minecraft/client/Minecraft;
net/minecraft/client/Minecraft.instance:Lnet/minecraft/client/Minecraft;
Lnet/minecraft/client/Minecraft;instance:Lnet/minecraft/client/Minecraft;
ordinal
: 指定注入到哪个同时被args.array
、target
和opcode
匹配的目标前,计数从0开始。如果不指定,表示注入到所有匹配的目标之前opcode
: 指定匹配的操作符,如果不指定,则匹配所有的符合args.array
和target
的字段引用,有以下取值:178
: 表示匹配GETSTATIC
操作符(读取静态字段)179
: 表示匹配PUTSTATIC
操作符(写入静态字段)180
: 表示匹配GETFIELD
操作符(读取实例字段)181
: 表示匹配PUTFIELD
操作符(写入实例字段)
如果target
和opcode
都不指定,则表示匹配所有的字段引用
表示注入到NEW
操作符之前。
args
: 允许下列参数:class
: 仅当target
属性未被指定的时候使用,表示匹配指定实例化的类型(例如class=net/minecraft/client/tutorial/Tutorial
)
target
: 仅当args.class
未被指定时可用,以下写法是合法的:net/minecraft/client/tutorial/Tutorial
(Lnet/minecraft/client/Minecraft;)Lnet/minecraft/client/tutorial/Tutorial;
(表示匹配使用指定构造函数实例化的目标)
ordinal
: 指定注入到哪个匹配args.class
或者target
的目标之前,计数从0开始。如果不指定,表示注入到所有匹配的目标之前opcode
: 无
以下两种写法还是有差距的:
写法A: @At(value = "NEW", target = "(Lnet/minecraft/client/Minecraft;)Lnet/minecraft/client/tutorial/Tutorial;")
写法B: @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/Tutorial;<init>(Lnet/minecraft/client/Minecraft;)V")
写法A匹配的是NEW
操作符,而写法B匹配的是INVOKESPECIAL
操作符,这意味着写法A匹配的地方构造函数的参数还没有被调用,而写法B匹配的地方参数已经调用完毕,即将执行构造函数本身。
表示注入到一个常量被引用之前(即常量引用操作符之前)。
args
: 允许以下参数:nullValue
: 匹配null
引用(填nullValue=true
)intValue
: 匹配int
类型常量引用(例如intValue=10
),对于像if (x>0)
的代码,字节码中是没有引用0的操作的,请使用expandZeroConditions
floatValue
: 匹配float
类型常量引用longValue
: 匹配long
类型常量引用doubleValue
: 匹配double
类型常量引用stringValue
: 匹配String
类型常量引用class
: 匹配类名.class
这样的引用,填这个类的完整限定名(例如class=net.minecraft.client.Minecraft
)log
: 同INVOKE
expandZeroConditions
: 参考下面一节常量引用特殊情况:与0比较
target
: 无ordinal
: 指定注入到哪个匹配args
的目标之前,计数从0开始。如果不指定,表示注入到所有匹配的目标之前opcode
: 无
如果要匹配多个expandZeroConditions
,请用逗号隔开,如expandZeroConditions=LESS_THAN_ZERO,GREATER_THAN_ZERO
表示注入到跳转操作符之前。
args
: 无target
: 无ordinal
: 指定注入到哪个匹配opcode
的目标之前,计数从0开始。如果不指定,表示注入到所有匹配的目标之前opcode
: 指定一个匹配操作符,如果不指定,则匹配所有跳转操作符。允许以下取值:153
: 表示匹配IFEQ
操作符154
: 表示匹配IFNE
操作符155
: 表示匹配IFLT
操作符156
: 表示匹配IFGE
操作符157
: 表示匹配IFGT
操作符158
: 表示匹配IFLE
操作符159
: 表示匹配IF_ICMPEQ
操作符160
: 表示匹配IF_ICMPNE
操作符161
: 表示匹配IF_ICMPLT
操作符162
: 表示匹配IF_ICMPGE
操作符163
: 表示匹配IF_ICMPGT
操作符164
: 表示匹配IF_ICMPLE
操作符165
: 表示匹配IF_ACMPEQ
操作符166
: 表示匹配IF_ACMPNE
操作符167
: 表示匹配GOTO
操作符168
: 表示匹配JSR
操作符198
: 表示匹配IFNULL
操作符199
: 表示匹配IFNONNULL
操作符
表示注入到*LOAD
操作符之前(读取一个局部变量之前),与@ModifyVariable
注解配套使用。
args
: 无target
: 无ordinal
: 指定注入到哪个*LOAD
操作符之前,计数从0开始,从@ModifyVariable
注解中的ordinal
开始数起opcode
: 无
表示注入到*STORE
操作符之后(写入一个局部变量之后),与@ModifyVariable
注解配套使用。
args
: 无target
: 无ordinal
: 指定注入到哪个*STORE
操作符之前,计数从0开始,从@ModifyVariable
注解中的ordinal
开始数起opcode
: 无
在Java中,对于像 if (x >= 0)
这样的代码,是不会出现ICONST_0
来引用0的,而是像类似于 if (x.isGreaterThanOrEqualToZero())
这样的引用方式。例如下面这一段代码:
public int foo(int num) {
if (num > 0)
return 11;
return 22;
}
它对应的一部分字节码是:
L0
LINENUMBER 6 L0
ILOAD 1 // num 入栈
IFLE L1 // 这里直接跳转,而没有引用0
L2
LINENUMBER 7 L2
BIPUSH 11
IRETURN
L1
LINENUMBER 8 L1
FRAME SAME
BIPUSH 22
IRETURN
L3
因为IFEQ
、IFNE
之类的操作符本身就是与0比较并跳转,为了解决这个问题,Mixin引入了Constant.Condition
枚举:
LESS_THAN_ZERO
: 表示<
或者>=
操作,用于匹配if(x<0)
LESS_THAN_OR_EQUAL_TO_ZERO
: 表示<=
或者>
操作,用于匹配if(x<=0)
GREATER_THAN_OR_EQUAL_TO_ZERO
: 等同于LESS_THAN_ZERO
,用于匹配if(x>=0)
GREATER_THAN_ZERO
: 等同于LESS_THAN_OR_EQUAL_TO_ZERO
,用于匹配if(x>0)