gpt4 book ai didi

android - 如何在Dalvik中生成INVOKE-POLYMORPHIC操作码

转载 作者:行者123 更新时间:2023-11-29 16:44:30 60 4
gpt4 key购买 nike

我正在为16位板开发Dalvik字节码解释器。我已经实现并测试了大多数操作码的翻译操作,但是由于无法弄清楚如何编写生成它们的Java代码,因此无法测试其他操作码。特别是,我一直无法生成“调用多态”和“调用自定义”操作码。 (以及Dalvik 35中的等效项,我正在考虑Dalvik 38的情况下进行开发)我试图在类中简单地运行一个方法,该方法将覆盖如下的超类方法:

SuperClass x = new SubClass();
x.mymethod();


这是多态性的教科书简单示例,但似乎只是生成常规的“调用直接”操作码。

另外,我对“方法句柄”或“调用站点”的含义以及为什么需要多态调用的原因一无所知。 (Dalvik文档似乎对此没有做太多的阐述)。

最佳答案

调用多态

“多态”是一个非常模糊的术语。例如,方法重载或重载通常被称为多态。在“调用多态”指令的情况下,该术语是指所谓的“签名多态”方法。

签名多态方法可以接受任何种类和数量的参数。
从Java源代码的角度来看,它们的外观和行为类似于带有vararg Object...参数的任何方法,但是它们在字节码级别上有所不同。尽管通过传递带有所有其他参数的数组来调用varargs方法,但对于“调用多态”方法而言,这不是必需的。可以使用任何描述符调用多态方法。创建参数数组或装箱原始类型的参数都不需要。

存在的唯一签名多态方法是MethodHandle#invoke(Object...)MethodHandle#invokeExact(Object...)(请参见JvmSpec §2.9.3)。

因此,这是产生“调用多态”指令的代码:

void foo(MethodHandle handle) throws Throwable {
handle.invoke(10, 20);
handle.invokeExact("foo", "bar");
}


让我们对其进行编译,运行 dx并使用 baksmali进行查看:

const/16 v0, 0xa
const/16 v1, 0x14
invoke-polymorphic {p1, v0, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (II)V

const-string v0, "foo"
const-string v1, "bar"
invoke-polymorphic {p1, v0, v1}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/String;Ljava/lang/String;)V


调用自定义

“ invoke-custom”指令与dalvik的 invokedynamic指令等效。

动态调用不调用一种静态已知的方法。它们提供了用于解析CallSite的“引导方法”。 CallSite本质上是围绕 MethodHandle的包装,它可能是可变的,也可能不是可变的。一旦“ invokedynamic”指令的CallSite被解析,它就会由JVM存储,并且从现在开始执行该指令时就被调用。 JVM Spec 10 §6.5中详细描述了整个过程。

Bootstrap方法只是返回CallSite的常规Java方法,并且必须至少具有三个参数。


第一个参数是 Lookup实例,可以访问所有类/字段/方法,这些类/字段/方法可以从包含invokedynamic指令的方法访问。
第二个参数通常称为“名称”。它是由invokedynamic指令定义的任意String。
第三个参数是MethodType。返回的CallSite必须与该类型完全匹配。这可以通过 MethodHandle#asType方法来实现。
可以传递其他参数。它们必须是存储在invokedynamic指令中的常量值。


注意:不要期望bootstrap方法与上述签名完全匹配。参数例如可以是对象类型。也可以用一个argarg参数替换一些参数。

jvm 7引入了“ invokedynamic”指令以支持动态语言,但Java语言本身并未使用该指令。该指令最初在Java 8中用于编译lambda。由于Java 9字符串连接是通过“ invokedynamic”调用实现的。

因此,下面的示例代码生成一个调用动态指令:

Supplier<String> someLambda = () -> "foo";


运行 javacdxbaksmali之后,我们将看到以下说明:

invoke-custom {}, call_site_0("get", ()Ljava/util/function/Supplier;, ()Ljava/lang/Object;, invoke-static@LTest;->lambda$foo$0()Ljava/lang/String;, ()Ljava/lang/String;)@Ljava/lang/invoke/LambdaMetafactory;->metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

.method private static synthetic lambda$foo$0()Ljava/lang/String;
const-string v0, "foo"
return-object v0
.end method


让我们仔细看一下Java 9如何编译String串联。
这是我们的示例代码:

String foo(Object a, Object b) {
return "a = " + a + ", b = " + b;
}


以及产生的指令:

invoke-custom {p1, p2}, call_site_0("makeConcatWithConstants", (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/String;, "a = \u0001, b = \u0001")@Ljava/lang/invoke/StringConcatFactory;->makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;


首先,我们有 {p1, p2}。这两个寄存器是被调用后作为参数传递给CallSite的值。

然后,我们有了 call_site_0(...)块。 call_site_0是此呼叫站点的名称,可用于区分其他相同的呼叫站点。该块存储一些信息,这些信息作为参数传递给bootstrap方法。


第一个值(“ makeConcatWithConstants”)是作为第二个参数传递给bootstrap方法的任意String。
第二个值是MethodType常量。它作为第三个参数传递给bootstrap方法,并且与返回的CallSite的类型完全匹配。在这种情况下,我们有一个方法可以接受两个对象(p1和p2)并返回一个String(串联的String)。
以下所有值都是传递给bootstrap方法的其他常量参数。在这种情况下,只有一个字符串是最终串联字符串的模式。


最后一件事(从“ @”符号开始)是对引导方法的引用。

关于android - 如何在Dalvik中生成INVOKE-POLYMORPHIC操作码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49027506/

60 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com