Skip to content

Latest commit

 

History

History
182 lines (162 loc) · 10.8 KB

5.5.md

File metadata and controls

182 lines (162 loc) · 10.8 KB

定位

来源: Injection Point Reference

@At注解的value属性只有以下的有限取值,这个取值也限定了argstargetordinalopcode属性的取值:

表示注入到方法的开头,对应的注入方法在目标方法最开始被调用。

  • 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: 指定注入到哪个同时被targetldc参数匹配的目标前,计数从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.arraytargetopcode匹配的目标前,计数从0开始。如果不指定,表示注入到所有匹配的目标之前
  • opcode: 指定匹配的操作符,如果不指定,则匹配所有的符合args.arraytarget的字段引用,有以下取值:
    • 178: 表示匹配GETSTATIC操作符(读取静态字段)
    • 179: 表示匹配PUTSTATIC操作符(写入静态字段)
    • 180: 表示匹配GETFIELD操作符(读取实例字段)
    • 181: 表示匹配PUTFIELD操作符(写入实例字段)

如果targetopcode都不指定,则表示匹配所有的字段引用

表示注入到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: 无

常量引用特殊情况:与0比较

在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

因为IFEQIFNE之类的操作符本身就是与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)