Java 待编译成常量池中MethodHandle的代码

Java code to be compiled into MethodHandle in Constant Pool


我正在尝试让 Java 8 Nashorn with complete source (not instrumented). As you may know, it uses Nasgen 修改 .类,并且输出在 JRE/lib/ext/nashorn.jar.


在反汇编输出时,使用 javap:

 0: aload_0
 1: ldc           #24                 // String Function
 3: ldc           #31                 // MethodHandle invokestatic jdk/nashorn/internal/objects/NativeFunction.function:(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;
 5: getstatic     #22                 // Field $nasgenmap$:Ljdk/nashorn/internal/runtime/PropertyMap;
 8: aconst_null
 9: invokespecial #34                 // Method jdk/nashorn/internal/objects/ScriptFunctionImpl."<init>":(Ljava/lang/String;Ljava/lang/invoke/MethodHandle;Ljdk/nashorn/internal/runtime/PropertyMap;[Ljdk/nashorn/internal/runtime/Specialization;)V

可能错误地写成

super("Function", NativeFunction.function, $nasgenmap$, (Specialization[]) null);

,它应该调用带有签名的超级构造函数:

ScriptFunctionImpl(String, MethodHandle, PropertyMap, Specialization[]) { }



我的问题是第二个参数 NativeFunction.function,我没有可编译的源代码,无法在常量池中生成相同的 MethodHandle

  #31 = MethodHandle       #6:#30         //  invokestatic jdk/nashorn/internal/objects/NativeFunction.function:(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;

那部分检测是由 ASM, by calling MethodVisitor.visitLdcInsn 完成的。

那么,有没有一种方法可以从 Java 源构建这样一个方法句柄,或者这是一个只能在字节码级别完成的功能?

完整的 javap 输出:

$javap -c -v NativeFunction$Constructor.class

  Last modified Apr 10, 2015; size 1161 bytes
  MD5 checksum dcae2f54643befc519a45e9ac9bc4781
final class jdk.nashorn.internal.objects.NativeFunction$Constructor extends jdk.nashorn.internal.objects.ScriptFunctionImpl
  minor version: 0
  major version: 51
  flags: ACC_FINAL
Constant pool:
   #1 = Utf8               jdk/nashorn/internal/objects/NativeFunction$Constructor
   #2 = Class              #1             //  jdk/nashorn/internal/objects/NativeFunction$Constructor
   #3 = Utf8               jdk/nashorn/internal/objects/ScriptFunctionImpl
   #4 = Class              #3             //  jdk/nashorn/internal/objects/ScriptFunctionImpl
   #5 = Utf8               $nasgenmap$
   #6 = Utf8               Ljdk/nashorn/internal/runtime/PropertyMap;
   #7 = Utf8               <clinit>
   #8 = Utf8               ()V
   #9 = Utf8               java/util/ArrayList
  #10 = Class              #9             //  java/util/ArrayList
  #11 = Utf8               <init>
  #12 = Utf8               (I)V
  #13 = NameAndType        #11:#12        //  "<init>":(I)V
  #14 = Methodref          #10.#13        //  java/util/ArrayList."<init>":(I)V
  #15 = Utf8               jdk/nashorn/internal/runtime/PropertyMap
  #16 = Class              #15            //  jdk/nashorn/internal/runtime/PropertyMap
  #17 = Utf8               newMap
  #18 = Utf8               (Ljava/util/Collection;)Ljdk/nashorn/internal/runtime/PropertyMap;
  #19 = NameAndType        #17:#18        //  newMap:(Ljava/util/Collection;)Ljdk/nashorn/internal/runtime/PropertyMap;
  #20 = Methodref          #16.#19        //  jdk/nashorn/internal/runtime/PropertyMap.newMap:(Ljava/util/Collection;)Ljdk/nashorn/internal/runtime/PropertyMap;
  #21 = NameAndType        #5:#6          //  $nasgenmap$:Ljdk/nashorn/internal/runtime/PropertyMap;
  #22 = Fieldref           #2.#21         //  jdk/nashorn/internal/objects/NativeFunction$Constructor.$nasgenmap$:Ljdk/nashorn/internal/runtime/PropertyMap;
  #23 = Utf8               Function
  #24 = String             #23            //  Function
  #25 = Utf8               jdk/nashorn/internal/objects/NativeFunction
  #26 = Class              #25            //  jdk/nashorn/internal/objects/NativeFunction
  #27 = Utf8               function
  #28 = Utf8               (ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;
  #29 = NameAndType        #27:#28        //  function:(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;
  #30 = Methodref          #26.#29        //  jdk/nashorn/internal/objects/NativeFunction.function:(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;
  #31 = MethodHandle       #6:#30         //  invokestatic jdk/nashorn/internal/objects/NativeFunction.function:(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;
  #32 = Utf8               (Ljava/lang/String;Ljava/lang/invoke/MethodHandle;Ljdk/nashorn/internal/runtime/PropertyMap;[Ljdk/nashorn/internal/runtime/Specialization;)V
  #33 = NameAndType        #11:#32        //  "<init>":(Ljava/lang/String;Ljava/lang/invoke/MethodHandle;Ljdk/nashorn/internal/runtime/PropertyMap;[Ljdk/nashorn/internal/runtime/Specialization;)V
  #34 = Methodref          #4.#33         //  jdk/nashorn/internal/objects/ScriptFunctionImpl."<init>":(Ljava/lang/String;Ljava/lang/invoke/MethodHandle;Ljdk/nashorn/internal/runtime/PropertyMap;[Ljdk/nashorn/internal/runtime/Specialization;)V
  #35 = Utf8               jdk/nashorn/internal/objects/NativeFunction$Prototype
  #36 = Class              #35            //  jdk/nashorn/internal/objects/NativeFunction$Prototype
  #37 = NameAndType        #11:#8         //  "<init>":()V
  #38 = Methodref          #36.#37        //  jdk/nashorn/internal/objects/NativeFunction$Prototype."<init>":()V
  #39 = Utf8               jdk/nashorn/internal/objects/PrototypeObject
  #40 = Class              #39            //  jdk/nashorn/internal/objects/PrototypeObject
  #41 = Utf8               setConstructor
  #42 = Utf8               (Ljava/lang/Object;Ljava/lang/Object;)V
  #43 = NameAndType        #41:#42        //  setConstructor:(Ljava/lang/Object;Ljava/lang/Object;)V
  #44 = Methodref          #40.#43        //  jdk/nashorn/internal/objects/PrototypeObject.setConstructor:(Ljava/lang/Object;Ljava/lang/Object;)V
  #45 = Utf8               jdk/nashorn/internal/runtime/ScriptFunction
  #46 = Class              #45            //  jdk/nashorn/internal/runtime/ScriptFunction
  #47 = Utf8               setPrototype
  #48 = Utf8               (Ljava/lang/Object;)V
  #49 = NameAndType        #47:#48        //  setPrototype:(Ljava/lang/Object;)V
  #50 = Methodref          #46.#49        //  jdk/nashorn/internal/runtime/ScriptFunction.setPrototype:(Ljava/lang/Object;)V
  #51 = Utf8               setArity
  #52 = NameAndType        #51:#12        //  setArity:(I)V
  #53 = Methodref          #46.#52        //  jdk/nashorn/internal/runtime/ScriptFunction.setArity:(I)V
  #54 = Utf8               Code
{
  public static {};
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: new           #10                 // class java/util/ArrayList
         3: dup
         4: iconst_1
         5: invokespecial #14                 // Method java/util/ArrayList."<init>":(I)V
         8: invokestatic  #20                 // Method jdk/nashorn/internal/runtime/PropertyMap.newMap:(Ljava/util/Collection;)Ljdk/nashorn
/internal/runtime/PropertyMap;
        11: putstatic     #22                 // Field $nasgenmap$:Ljdk/nashorn/internal/runtime/PropertyMap;
        14: return

  jdk.nashorn.internal.objects.NativeFunction$Constructor();
    flags:
    Code:
      stack=5, locals=1, args_size=1
         0: aload_0
         1: ldc           #24                 // String Function
         3: ldc           #31                 // MethodHandle invokestatic jdk/nashorn/internal/objects/NativeFunction.function:(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;
         5: getstatic     #22                 // Field $nasgenmap$:Ljdk/nashorn/internal/runtime/PropertyMap;
         8: aconst_null
         9: invokespecial #34                 // Method jdk/nashorn/internal/objects/ScriptFunctionImpl."<init>":(Ljava/lang/String;Ljava/lang/invoke/MethodHandle;Ljdk/nashorn/internal/runtime/PropertyMap;[Ljdk/nashorn/internal/runtime/Specialization;)V
        12: aload_0
        13: new           #36                 // class jdk/nashorn/internal/objects/NativeFunction$Prototype
        16: dup
        17: invokespecial #38                 // Method jdk/nashorn/internal/objects/NativeFunction$Prototype."<init>":()V
        20: dup
        21: aload_0
        22: invokestatic  #44                 // Method jdk/nashorn/internal/objects/PrototypeObject.setConstructor:(Ljava/lang/Object;Ljava/lang/Object;)V
        25: invokevirtual #50                 // Method jdk/nashorn/internal/runtime/ScriptFunction.setPrototype:(Ljava/lang/Object;)V
        28: aload_0
        29: iconst_1
        30: invokevirtual #53                 // Method jdk/nashorn/internal/runtime/ScriptFunction.setArity:(I)V
        33: return
}

没有 Java 语言构造来生成加载 MethodHandleldc 指令。不过,您可以创建一个 等效 具有更复杂构造的句柄:

MethodHandles.lookup().findStatic(
  jdk.nashorn.internal.objects.NativeFunction.class, "function",
  MethodType.fromMethodDescriptorString(
   "(ZLjava/lang/Object;[Ljava/lang/Object;)Ljdk/nashorn/internal/runtime/ScriptFunction;",
   null))

这不仅比单个 ldc 字节码指令更复杂,您还被迫处理已检查的异常 NoSuchMethodExceptionIllegalAccessException(或它们的共同祖先 ReflectiveOperationException).

你可以把操作封装成一个方法,比如

private static MethodHandle MH_NativeFunction_function() {
  try {
    return MethodHandles.lookup().findStatic(
      jdk.nashorn.internal.objects.NativeFunction.class, "function",
      MethodType.fromMethodDescriptorString("(ZLjava/lang/Object;[Ljava/lang/Object;)"
        + "Ljdk/nashorn/internal/runtime/ScriptFunction;", null));
  } catch(ReflectiveOperationException ex) {
      throw new AssertionError(ex);
  }
}

并在您的构造函数中将其称为

super("Function", MH_NativeFunction_function(), $nasgenmap$, (Specialization[]) null);

这种方法的优点是您可以使用 Indify 将调用 MH_NativeFunction_function() 转换回从常量池加载 MethodHandleldc 指令。