英文:
How to dynamically cast an object (of class Object) to the method return type using ASM?
问题
我已经翻译了您提供的代码部分。请注意,代码中的特殊字符如"
可能需要根据上下文替换为正常的引号。
要注意的是,ASM是一个复杂的字节码操作库,错误可能出现在许多地方,包括字节码生成、类型转换等。关于java.lang.VerifyError: Incompatible object argument for function call
错误,通常是因为字节码中的类型转换不匹配导致的。以下是您的代码的翻译:
我想要做的是使用ASM修改一个方法:
1. 我将一个对象(Object类的对象)推入堆栈
2. 我想将该对象强制转换为该方法的返回类型
3. 返回转换后的对象。
我的方法Visitor适配器中的代码:
```java
public void visitCode() {
mv.visitCode();
if (needModify){
// 将所有方法参数打包成一个对象数组并推入堆栈
...
// selfReturnTypeDotClassName是返回类型的点类名
mv.visitLdcInsn(selfReturnTypeDotClassName);
// 推入对象(Object类的对象)
mv.visitMethodInsn(INVOKESTATIC, MyClass, "getOutputObj",
"([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false);
// 将对象转换为返回类型
castPeekOnStack(selfReturnType);
mv.visitInsn(selfReturnType.getOpcode(IRETURN));
}
}
MyClass
中的 getOutputObj
方法(它尝试将先前记录的Json字符串恢复为对象,使用 Gson
):
public static Object getOutputObj(Object[] args, String methodId, String returnTypeDotClassName){
HashMap<String, String> inOutMap = getInOutMapOfMethod(methodId);
// GSON 是 Gson 类的一个实例
String inputJson = GSON.toJson(args);
String outputJson = inOutMap.get(inputJson);
return recoverObjFromJson(outputJson, returnTypeDotClassName);
}
public static Object recoverObjFromJson(String outputJson, String returnTypeDotClassName){
try{
// 该对象先前被打包为长度为1的对象数组。
Object obj = GSON.fromJson(outputJson, Object[].class)[0];
return obj;
}catch (Exception e){
e.printStackTrace();
MyEkstaziAgent.log(String.format("Gson Error: fromJson failed for arguments %s, %s",
outputJson, returnTypeDotClassName));
return null;
}
}
我的第一个版本的 castPeekOnStack
方法:
public void castPeekOnStack(Type targetType){
switch (targetType.getSort()) {
// 不确定
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, targetType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, targetType.getInternalName());
break;
}
}
我在仅具有 int
返回类型的基准上尝试了这段代码。然后我收到了 java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
错误。我认为当我将对象推送到堆栈时,如果它表示值,它默认为类型 Double
。所以我有第二个版本:
public void castPeekOnStack(Type targetType){
switch (targetType.getSort()) {
// 不确定
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "byteValue", "()B", false);
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "shortValue", "()S", false);
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "intValue", "()I", false);
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "floatValue", "()F", false);
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "longValue", "()J", false);
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case Type.ARRAY:
mv.visit
<details>
<summary>英文:</summary>
What I want to do is to modify a method using ASM:
1. I push an object (of class Object) to the stack
2. I want to cast that object to the return type of that method
3. Return that casted object.
My code in the methodVisitor adapter:
```java
public void visitCode() {
mv.visitCode();
if (needModify){
// package all the method arguments to an Object array and push to the stack
...
// selfReturnTypeDotClassName is the dot class name of return type
mv.visitLdcInsn(selfReturnTypeDotClassName);
// push the object (of class Object)
mv.visitMethodInsn(INVOKESTATIC, MyClass, "getOutputObj",
"([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false);
// cast the object to the return type
castPeekOnStack(selfReturnType);
mv.visitInsn(selfReturnType.getOpcode(IRETURN));
}
}
The method getOutputObj
in MyClass
(It tries to recover previously recorded Json String to an object using Gson
):
public static Object getOutputObj(Object[] args, String methodId, String returnTypeDotClassName){
HashMap<String, String> inOutMap = getInOutMapOfMethod(methodId);
// `GSON` is an instance of class `Gson`
String inputJson = GSON.toJson(args);
String outputJson = inOutMap.get(inputJson);
return recoverObjFromJson(outputJson, returnTypeDotClassName);
}
public static Object recoverObjFromJson(String outputJson, String returnTypeDotClassName){
try{
// the object is previously packaged as an object array with length 1.
Object obj = GSON.fromJson(outputJson, Object[].class)[0];
return obj;
}catch (Exception e){
e.printStackTrace();
MyEkstaziAgent.log(String.format("Gson Error: fromJson failed for arguments %s, %s",
outputJson, returnTypeDotClassName));
return null;
}
}
My first version of method castPeekOnStack
:
public void castPeekOnStack(Type targetType){
switch (targetType.getSort()) {
// not sure
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, targetType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, targetType.getInternalName());
break;
}
}
I tried this code on a benchmark whose methods only have int
return type. Then I get java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
. I think when I push the object to the stack, it is of type Double
by default if it represents value. So I have the second version:
public void castPeekOnStack(Type targetType){
switch (targetType.getSort()) {
// not sure
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "byteValue", "()B", false);
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "shortValue", "()S", false);
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "intValue", "()I", false);
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "floatValue", "()F", false);
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "longValue", "()J", false);
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, targetType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, targetType.getInternalName());
break;
}
}
However, I got java.lang.VerifyError: (class: com/D, method: p signature: ()I) Incompatible object argument for function call
. I am stuck here, I have no idea why this error is thrown.
答案1
得分: 1
以下是翻译好的部分:
问题似乎是:我在一个不是java/lang/Double
类的对象上使用了mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "intValue", "()I", false);
。我需要先进行checkcast java/lang/Double
操作。我使用了castPeekOnStack
方法的第三个版本,错误已经消失:
public void castPeekOnStack(Type targetType){
switch (targetType.getSort()) {
// 不确定的情况
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "byteValue", "()B", false);
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "shortValue", "()S", false);
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "intValue", "()I", false);
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "floatValue", "()F", false);
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "longValue", "()J", false);
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, targetType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, targetType.getInternalName());
break;
}
}
然而,我还没有在广泛的情况下测试这个方法,我不确定它是否适用于其他返回类型。
上述解决方案只能处理对象为值的情况。当我尝试对引用类型进行强制转换时,它会抛出类似于com.google.gson.internal.LinkedTreeMap cannot be cast to ...
的异常。因此,我从Json中恢复对象的方式可能存在问题。
因此,在recoverObjFromJson
方法中,我直接将Json强制转换为我想要的类型。需要注意的是,虽然通过fromJson
方法将对象转换为我指定的类型,但recoverObjFromJson
方法的返回类型仍然是Object
,因此我仍然需要在堆栈上进行强制转换。
public static Object recoverObjFromJson(String outputJson, String returnTypeDotClassName){
try{
Object obj = GSON.fromJson(outputJson, getClassObjByName(returnTypeDotClassName));
return obj;
}catch (Exception e){
e.printStackTrace();
MyEkstaziAgent.log(String.format("Gson Error: fromJson failed for arguments %s, %s",
outputJson, returnTypeDotClassName));
return null;
}
}
最后,这个recoverObjFromJson
方法与castPeekOnStack
的第一个版本配合得很好。
英文:
It seems the problem is: I use mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "intValue", "()I", false);
on an object which is not of class java/lang/Double
. I need to checkcast java/lang/Double
first. I used the third version of method castPeekOnStack
, the error has gone:
public void castPeekOnStack(Type targetType){
switch (targetType.getSort()) {
// not sure
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "byteValue", "()B", false);
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "shortValue", "()S", false);
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "intValue", "()I", false);
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "floatValue", "()F", false);
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "longValue", "()J", false);
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, targetType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, targetType.getInternalName());
break;
}
}
However I haven't test the method on a wide range of cases, I am not sure if it can work for other return types.
The solution above can only handle cases that the object is a value. When I try to cast a reference type, It throws something like com.google.gson.internal.LinkedTreeMap cannot be cast to ...
. So the way I recover object from Json must have some problems.
So in method recoverObjFromJson
, I directly cast the Json to the type I want. It should be noted that, although by fromJson
the object is casted to the type I designate, the return type of method recoverObjFromJson
is still Object
, so I still need to cast it on the stack.
public static Object recoverObjFromJson(String outputJson, String returnTypeDotClassName){
try{
Object obj = GSON.fromJson(outputJson, getClassObjByName(returnTypeDotClassName));
return obj;
}catch (Exception e){
e.printStackTrace();
MyEkstaziAgent.log(String.format("Gson Error: fromJson failed for arguments %s, %s",
outputJson, returnTypeDotClassName));
return null;
}
}
Finally, this recoverObjFromJson
works well with the first version of castPeekOnStack
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论