通过 Public 查找检索数组克隆方法句柄
Retrieve Array Clone Method Handle Through a Public Lookup
最近,我试图通过java.lang.invoke
库访问数组类型的clone
方法。这被证明是不成功的。示例中的克隆方法是这样的:
int[] a = new int[]{1, 2, 3, 4};
int[] b = a.clone();
我想为 a.clone()
调用创建一个 MethodHandle
。这样生成的代码类似于:
int[] a = new int[]{1, 2, 3, 4};
int[] b = findCloneMethod().invoke(a);
我为每个其他方法调用设置了这个系统。但是,系统仅使用此方法失败。
此问题与 java.lang.invoke
library, not the java.lang.reflect
图书馆有关。
问题描述
引入了以下示例代码来展示此行为。
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class Main {
void test() {
final MethodHandles.Lookup caller = MethodHandles.lookup();
final Class<?> refc = int[].class;
final String name = "clone";
final MethodType type = MethodType.methodType(Object.class);
final MethodHandle mh = findVirtual(caller, refc, name, type);
System.out.println("Lookup: " + caller);
System.out.println("mh: " + mh);
}
public static void main(String[] args) {
new Main().test();
}
private MethodHandle findVirtual(final MethodHandles.Lookup caller, final Class<?> refc, final String name, final MethodType type) {
try {
return caller.findVirtual(refc, name, type);
} catch (final Throwable t) {
t.printStackTrace();
return null;
}
}
}
示例输出:
Lookup: Main
mh: MethodHandle(Main)Object
输出有意义。来自 Main
范围的调用者只能在 Main
的 superclass: Object
中看到 clone
方法。即使使用 int[]
作为参考 class,此方法也不可见。这成为一个问题,因为如果使用此调用站点,JVM 会尝试将 int[]
转换为 Main
。
通过将引用类型引入 int[]
,人们会期望类型 int[]
中的 clone
方法可以 public 访问。因此,当文档提示:
The type of the method handle will be that of the method, with the receiver type (usually refc) prepended. 1
mh
的类型将是 (int[])Object
。
尝试的解决方案:
由于查找 caller
通过隐藏正确的 clone
方法进行干扰,因此尝试使用 public 查找:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
然而,这失败了。 Object#clone()
签名的简单检查说明了原因:
protected Object clone() throws CloneNotSupportedException { ...
方法是protected
。这对于数组类型而言并非如此,因为实现使此方法 public 可用。
这方面的一个例子是尝试访问 Object#equals
方法。这显示了三个单独的参考 classes:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
final Class<?> refc = Object.class;
final String name = "equals";
final MethodType type = MethodType.methodType(boolean.class, Object.class);
Lookup: java.lang.Object/public
mh: MethodHandle(Object,Object)boolean
然后用Main.class
作为参考 class:
final Class<?> refc = Main.class;
Lookup: java.lang.Object/public
mh: MethodHandle(Main,Object)boolean
然后用int[].class
作为参考 class:
final Class<?> refc = int[].class
Lookup: Main
mh: MethodHandle(int[],Object)boolean
这是预期的行为,显然可以流畅地运行。在第一种情况下,引用 class 是 Object
并且它返回 Object#equals
方法句柄。在第二个中,引用 class 是 Main
并且它返回 Main#equals
方法句柄。最后,引用 class 是 int[].class
并且它返回了 int[]#equals
方法句柄。
这个奇偶校验 而不是 用 int[]#clone
方法维护。由于clone
方法是受保护的,但是public对于数组,是找不到的。对我来说,这似乎是一个错误。数组 clone
方法应该 public 通过 publicLookup
可用。返回的方法句柄类型可以是:MethodHandle(Object)Object
.
检索后,MethodHandle#asType
可用于更正类型。在这种情况下,它将被转换为 MethodHandle(int[])Object
。
如果 Object#clone
方法 public 不可用,至少对于数组类型,这似乎是不可能的。
此外,这似乎是唯一可能发生这种情况的方法,因为数组类型只有 extend/implement 3 classes:Object
、Cloneable
和 Serializable
。唯一的其他方法是 Object#finalize
,它也受到保护;但是,数组类型并不使此方法 public 可用。因此 clone
是唯一失败的方法。
tl;博士:
数组 clone
方法,例如 Object[]#clone()
,public 无法通过 public 查找访问。这是因为 Object#clone
是 protected
;但是,数组类型使此方法 public 可用。由于此可见性问题,尝试通过引用 class 访问数组 clone
失败。因此,无法为该方法生成 MethodHandle
。但是,在 public 方法(例如 Object#equals
上尝试相同的技术对于数组类型来说效果很好。
我宁愿避免反射访问,因为那里的方法可以通过简单地更改查找的信任级别来检索它。
有没有办法为数组clone
方法生成MethodHandle
?
注意:我确实了解 java.lang.invoke
的正确用法,我不打算用它来替代 java.lang.reflect
。
JSR 292 的早期实现中存在错误 JDK-8001105,其中 findVirtual
不适用于数组的 clone
方法。不过这个问题早就解决了
因此,最近 JDK 8 findVirtual
如果在 publicLookup()
.
上调用,则 clone
工作正常
您可以在 JDK source code:
中找到一个特例
if (Modifier.isProtected(mods) &&
refKind == REF_invokeVirtual &&
m.getDeclaringClass() == Object.class &&
m.getName().equals("clone") &&
refc.isArray()) {
// The JVM does this hack also.
// (See ClassVerifier::verify_invoke_instructions
// and LinkResolver::check_method_accessability.)
// Because the JVM does not allow separate methods on array types,
// there is no separate method for int[].clone.
// All arrays simply inherit Object.clone.
// But for access checking logic, we make Object.clone
// (normally protected) appear to be public.
// Later on, when the DirectMethodHandle is created,
// its leading argument will be restricted to the
// requested array type.
// N.B. The return type is not adjusted, because
// that is *not* the bytecode behavior.
mods ^= Modifier.PROTECTED | Modifier.PUBLIC;
}
最近,我试图通过java.lang.invoke
库访问数组类型的clone
方法。这被证明是不成功的。示例中的克隆方法是这样的:
int[] a = new int[]{1, 2, 3, 4};
int[] b = a.clone();
我想为 a.clone()
调用创建一个 MethodHandle
。这样生成的代码类似于:
int[] a = new int[]{1, 2, 3, 4};
int[] b = findCloneMethod().invoke(a);
我为每个其他方法调用设置了这个系统。但是,系统仅使用此方法失败。
此问题与 java.lang.invoke
library, not the java.lang.reflect
图书馆有关。
问题描述
引入了以下示例代码来展示此行为。
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class Main {
void test() {
final MethodHandles.Lookup caller = MethodHandles.lookup();
final Class<?> refc = int[].class;
final String name = "clone";
final MethodType type = MethodType.methodType(Object.class);
final MethodHandle mh = findVirtual(caller, refc, name, type);
System.out.println("Lookup: " + caller);
System.out.println("mh: " + mh);
}
public static void main(String[] args) {
new Main().test();
}
private MethodHandle findVirtual(final MethodHandles.Lookup caller, final Class<?> refc, final String name, final MethodType type) {
try {
return caller.findVirtual(refc, name, type);
} catch (final Throwable t) {
t.printStackTrace();
return null;
}
}
}
示例输出:
Lookup: Main
mh: MethodHandle(Main)Object
输出有意义。来自 Main
范围的调用者只能在 Main
的 superclass: Object
中看到 clone
方法。即使使用 int[]
作为参考 class,此方法也不可见。这成为一个问题,因为如果使用此调用站点,JVM 会尝试将 int[]
转换为 Main
。
通过将引用类型引入 int[]
,人们会期望类型 int[]
中的 clone
方法可以 public 访问。因此,当文档提示:
The type of the method handle will be that of the method, with the receiver type (usually refc) prepended. 1
mh
的类型将是 (int[])Object
。
尝试的解决方案:
由于查找 caller
通过隐藏正确的 clone
方法进行干扰,因此尝试使用 public 查找:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
然而,这失败了。 Object#clone()
签名的简单检查说明了原因:
protected Object clone() throws CloneNotSupportedException { ...
方法是protected
。这对于数组类型而言并非如此,因为实现使此方法 public 可用。
这方面的一个例子是尝试访问 Object#equals
方法。这显示了三个单独的参考 classes:
final MethodHandles.Lookup caller = MethodHandles.publicLookup();
final Class<?> refc = Object.class;
final String name = "equals";
final MethodType type = MethodType.methodType(boolean.class, Object.class);
Lookup: java.lang.Object/public
mh: MethodHandle(Object,Object)boolean
然后用Main.class
作为参考 class:
final Class<?> refc = Main.class;
Lookup: java.lang.Object/public
mh: MethodHandle(Main,Object)boolean
然后用int[].class
作为参考 class:
final Class<?> refc = int[].class
Lookup: Main
mh: MethodHandle(int[],Object)boolean
这是预期的行为,显然可以流畅地运行。在第一种情况下,引用 class 是 Object
并且它返回 Object#equals
方法句柄。在第二个中,引用 class 是 Main
并且它返回 Main#equals
方法句柄。最后,引用 class 是 int[].class
并且它返回了 int[]#equals
方法句柄。
这个奇偶校验 而不是 用 int[]#clone
方法维护。由于clone
方法是受保护的,但是public对于数组,是找不到的。对我来说,这似乎是一个错误。数组 clone
方法应该 public 通过 publicLookup
可用。返回的方法句柄类型可以是:MethodHandle(Object)Object
.
检索后,MethodHandle#asType
可用于更正类型。在这种情况下,它将被转换为 MethodHandle(int[])Object
。
如果 Object#clone
方法 public 不可用,至少对于数组类型,这似乎是不可能的。
此外,这似乎是唯一可能发生这种情况的方法,因为数组类型只有 extend/implement 3 classes:Object
、Cloneable
和 Serializable
。唯一的其他方法是 Object#finalize
,它也受到保护;但是,数组类型并不使此方法 public 可用。因此 clone
是唯一失败的方法。
tl;博士:
数组 clone
方法,例如 Object[]#clone()
,public 无法通过 public 查找访问。这是因为 Object#clone
是 protected
;但是,数组类型使此方法 public 可用。由于此可见性问题,尝试通过引用 class 访问数组 clone
失败。因此,无法为该方法生成 MethodHandle
。但是,在 public 方法(例如 Object#equals
上尝试相同的技术对于数组类型来说效果很好。
我宁愿避免反射访问,因为那里的方法可以通过简单地更改查找的信任级别来检索它。
有没有办法为数组clone
方法生成MethodHandle
?
注意:我确实了解 java.lang.invoke
的正确用法,我不打算用它来替代 java.lang.reflect
。
JSR 292 的早期实现中存在错误 JDK-8001105,其中 findVirtual
不适用于数组的 clone
方法。不过这个问题早就解决了
因此,最近 JDK 8 findVirtual
如果在 publicLookup()
.
上调用,则 clone
工作正常
您可以在 JDK source code:
if (Modifier.isProtected(mods) &&
refKind == REF_invokeVirtual &&
m.getDeclaringClass() == Object.class &&
m.getName().equals("clone") &&
refc.isArray()) {
// The JVM does this hack also.
// (See ClassVerifier::verify_invoke_instructions
// and LinkResolver::check_method_accessability.)
// Because the JVM does not allow separate methods on array types,
// there is no separate method for int[].clone.
// All arrays simply inherit Object.clone.
// But for access checking logic, we make Object.clone
// (normally protected) appear to be public.
// Later on, when the DirectMethodHandle is created,
// its leading argument will be restricted to the
// requested array type.
// N.B. The return type is not adjusted, because
// that is *not* the bytecode behavior.
mods ^= Modifier.PROTECTED | Modifier.PUBLIC;
}