Android 如何在Dalvik中生成调用多态操作码
我正在为16位板开发Dalvik字节码解释器。我已经实现并测试了大多数操作码的翻译操作,但无法测试其他一些操作码,因为我无法理解如何编写生成它们的Java代码。特别是,我无法生成调用多态性和调用自定义操作码。与Dalvik 35中的等价物一样,我正在开发Dalvik 38,我已经尝试在一个类中运行一个方法,该方法覆盖一个超类方法,如下所示:Android 如何在Dalvik中生成调用多态操作码,android,dalvik,dex,Android,Dalvik,Dex,我正在为16位板开发Dalvik字节码解释器。我已经实现并测试了大多数操作码的翻译操作,但无法测试其他一些操作码,因为我无法理解如何编写生成它们的Java代码。特别是,我无法生成调用多态性和调用自定义操作码。与Dalvik 35中的等价物一样,我正在开发Dalvik 38,我已经尝试在一个类中运行一个方法,该方法覆盖一个超类方法,如下所示: SuperClass x = new SubClass(); x.mymethod(); 这是一个教科书上简单的多态性示例,但它似乎只是生成常规的调用直接
SuperClass x = new SubClass();
x.mymethod();
这是一个教科书上简单的多态性示例,但它似乎只是生成常规的调用直接操作码
此外,我还不完全清楚方法句柄或调用站点是什么意思,以及多态调用为什么需要它们。Dalvik文档似乎没有对此进行详细阐述。invoke polymorphic
多态性是一个相当模糊的术语。例如,方法重写或重载通常被称为多态性。在调用多态指令的情况下,该术语指的是所谓的签名多态方法
签名多态方法可以接受任何类型和数量的参数。
从java源代码的角度来看,它们的外观和行为类似于任何带有一个vararg对象的方法。。。参数,但它们在字节码级别上有所不同。虽然varargs方法是通过传递一个包含所有附加参数的数组来调用的,但这对于调用多态方法不是必需的。多态方法可以用任何描述符调用。不需要创建参数数组或基本类型的装箱参数
现有的唯一签名多态方法是和,请参阅
下面是生成调用多态指令的代码:
void foo(MethodHandle handle) throws Throwable {
handle.invoke(10, 20);
handle.invokeExact("foo", "bar");
}
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
让我们编译它,运行dx并使用baksmali查看它:
调用自定义
调用自定义指令是dalvik的指令,相当于JVM的指令
动态调用不会调用静态已知的方法。它们提供用于解析调用站点的引导方法。CallSite基本上是一个包装器,它围绕着一个可能是可变的,也可能不是可变的。一旦InvokedDynamic指令的调用站点被解析,它将被JVM存储,并且从现在起,在执行该指令时被调用。整个过程在本手册中有更详细的描述
引导方法只是返回调用站点的常规java方法,必须至少有三个参数
第一个参数是一个实例,它可以访问可以从包含invokedynamic指令的方法访问的所有类/字段/方法。
第二个参数通常称为name。它是由invokedynamic指令定义的任意字符串。
第三个参数是MethodType。返回的调用站点必须与此类型完全匹配。这可以通过该方法实现。
可以传递进一步的参数。它们必须是存储在invokedynamic指令中的常量值。
注意:不要期望引导方法与上述签名完全匹配。例如,参数可以是Object类型。也可以针对一个vararg参数替换一些参数
InvokedDynamic指令是在JVM7中引入的,以支持动态语言,但java语言本身没有使用它。该指令首先在Java8中用于编译lambdas。因为java 9字符串连接是通过InvokedDynamic调用实现的
下面是生成调用动态指令的示例代码:
Supplier<String> someLambda = () -> "foo";
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;
让我们仔细看看Java9是如何编译字符串连接的。
下面是我们的示例代码:
String foo(Object a, Object b) {
return "a = " + a + ", b = " + b;
}
以及由此产生的指令:
Supplier<String> someLambda = () -> "foo";
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后作为参数传递给它的值
然后我们得到了呼叫站点0。。。块call_site_0是此callsite的名称,可用于区分其他相同的callsite。块存储一些作为参数传递给引导方法的信息
第一个值makeConcatWithConstants是作为第二个参数传递给引导方法的任意字符串。
第二个值是MethodType常量。它作为第三个参数传递给bootstrap方法,并与返回的调用站点的类型完全匹配。在本例中,我们得到了一个方法,该方法接受两个对象p1和p2,并返回一个字符串,即连接的字符串。
以下所有值都是传递给引导方法的附加常量参数。在本例中,只有一个字符串是最终连接字符串的模式。
从“@”符号开始的最后一件事是对引导方法的引用。invoke polymorphic
多态性是一个相当模糊的术语。例如,方法重写或重载通常被称为多态性。就
调用多态性指令,这个术语指的是所谓的签名多态性方法
签名多态方法可以接受任何类型和数量的参数。
从java源代码的角度来看,它们的外观和行为类似于任何带有一个vararg对象的方法。。。参数,但它们在字节码级别上有所不同。虽然varargs方法是通过传递一个包含所有附加参数的数组来调用的,但这对于调用多态方法不是必需的。多态方法可以用任何描述符调用。不需要创建参数数组或基本类型的装箱参数
现有的唯一签名多态方法是和,请参阅
下面是生成调用多态指令的代码:
void foo(MethodHandle handle) throws Throwable {
handle.invoke(10, 20);
handle.invokeExact("foo", "bar");
}
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
让我们编译它,运行dx并使用baksmali查看它:
调用自定义
调用自定义指令是dalvik的指令,相当于JVM的指令
动态调用不会调用静态已知的方法。它们提供用于解析调用站点的引导方法。CallSite基本上是一个包装器,它围绕着一个可能是可变的,也可能不是可变的。一旦InvokedDynamic指令的调用站点被解析,它将被JVM存储,并且从现在起,在执行该指令时被调用。整个过程在本手册中有更详细的描述
引导方法只是返回调用站点的常规java方法,必须至少有三个参数
第一个参数是一个实例,它可以访问可以从包含invokedynamic指令的方法访问的所有类/字段/方法。
第二个参数通常称为name。它是由invokedynamic指令定义的任意字符串。
第三个参数是MethodType。返回的调用站点必须与此类型完全匹配。这可以通过该方法实现。
可以传递进一步的参数。它们必须是存储在invokedynamic指令中的常量值。
注意:不要期望引导方法与上述签名完全匹配。例如,参数可以是Object类型。也可以针对一个vararg参数替换一些参数
InvokedDynamic指令是在JVM7中引入的,以支持动态语言,但java语言本身没有使用它。该指令首先在Java8中用于编译lambdas。因为java 9字符串连接是通过InvokedDynamic调用实现的
下面是生成调用动态指令的示例代码:
Supplier<String> someLambda = () -> "foo";
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;
让我们仔细看看Java9是如何编译字符串连接的。
下面是我们的示例代码:
String foo(Object a, Object b) {
return "a = " + a + ", b = " + b;
}
以及由此产生的指令:
Supplier<String> someLambda = () -> "foo";
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后作为参数传递给它的值
然后我们得到了呼叫站点0。。。块call_site_0是此callsite的名称,可用于区分其他相同的callsite。块存储一些作为参数传递给引导方法的信息
第一个值makeConcatWithConstants是作为第二个参数传递给引导方法的任意字符串。
第二个值是MethodType常量。它作为第三个参数传递给bootstrap方法,并与返回的调用站点的类型完全匹配。在本例中,我们得到了一个方法,该方法接受两个对象p1和p2,并返回一个字符串,即连接的字符串。
以下所有值都是传递给引导方法的附加常量参数。在本例中,只有一个字符串是最终连接字符串的模式。
从“@”符号开始的最后一件事是对引导方法的引用