You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何int[]与String[]合并为Object[]触发JVM验证错误?

数组类型合并与JVM验证错误的疑问

仔细阅读JVMS §4.10.2.2后,我注意到以下段落:

若对应值均为数组引用类型,则检查其维度。若数组类型维度相同,则合并后的值是二者首个公共超类型数组的实例引用。(若任一或两个数组类型的元素类型为基本类型,则用Object替代元素类型。)
[...]
即使int[]与String[]也可合并,结果为Object[],因为计算首个公共超类型时用Object替代了int。

int[]并非Object[]的子类型,这一点很有意思,于是我做了如下测试:

package test.se17;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;

import java.lang.invoke.MethodHandles;

public class CastArrayDump {
    
    public static byte[] dump() throws Exception {
        
        ClassWriter classWriter = new ClassWriter(0);
        MethodVisitor methodVisitor;
        
        classWriter.visit(V1_5, ACC_PUBLIC | ACC_SUPER, "test/se17/CastArray", null,
                "java/lang/Object", null);
        
        {
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "cast",
                    "(Ljava/lang/Object;)[Ljava/lang/Object;", null, null);
            methodVisitor.visitCode();
            Label end = new Label();
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitTypeInsn(INSTANCEOF, "[I");
            Label notint = new Label();
            methodVisitor.visitJumpInsn(IFEQ, notint);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitTypeInsn(CHECKCAST, "[I");
            methodVisitor.visitJumpInsn(GOTO, end);
            methodVisitor.visitLabel(notint);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitTypeInsn(INSTANCEOF, "[Ljava/lang/String;");
            Label notobj = new Label();
            methodVisitor.visitJumpInsn(IFEQ, notobj);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitTypeInsn(CHECKCAST, "[Ljava/lang/String;");
            methodVisitor.visitJumpInsn(GOTO, end);
            methodVisitor.visitLabel(notobj);
            methodVisitor.visitMethodInsn(INVOKESTATIC, "test/se17/CastArray",
                    "newIllegalArgumentException", "()Ljava/lang/IllegalArgumentException;", false);
            methodVisitor.visitInsn(ATHROW);
            methodVisitor.visitLabel(end);
            methodVisitor.visitInsn(ARETURN);
            methodVisitor.visitMaxs(2, 2);
            methodVisitor.visitEnd();
        }
        {
            methodVisitor =
                    classWriter.visitMethod(ACC_PRIVATE | ACC_STATIC, "newIllegalArgumentException",
                            "()Ljava/lang/IllegalArgumentException;", null, null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
            methodVisitor.visitInsn(DUP);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException",
                    "<init>", "()V", false);
            methodVisitor.visitInsn(ARETURN);
            methodVisitor.visitMaxs(2, 0);
            methodVisitor.visitEnd();
        }
        classWriter.visitEnd();
        
        return classWriter.toByteArray();
    }
    
    public static void main(String[] args) throws IllegalAccessException, Exception {
        MethodHandles.lookup().defineClass(dump());
        test();
    }
    
    private static void test() {
        System.out.println(CastArray.cast(new String[0]));
    }
}

注意:CastArrayDump是针对具有相同签名cast方法的存根CastArray编译的。

运行此代码时,我得到以下异常:

Exception in thread "main" java.lang.VerifyError: (class: test/se17/CastArray, method: cast signature: (Ljava/lang/Object;)[Ljava/lang/Object;) Wrong return type in function
    at java.base/java.lang.ClassLoader.defineClass0(Native Method)
    at java.base/java.lang.System$2.defineClass(System.java:2307)
    at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2439)
    at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2416)
    at java.base/java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:1843)
    at test.se17/test.se17.CastArrayDump.main(CastArrayDump.java:69)

在cast方法中,控制流在end标签处合并。栈上的内容要么是String[],要么是int[]——按道理应合并为Object[]。

areturn指令类型安全的前提是:封闭方法的声明返回类型ReturnType为引用类型,且可从传入的操作数栈中合法弹出匹配ReturnType的类型。

现在,栈上应该是Object[],因为我们刚完成了合并。但不知为何实际并非如此,导致出现“Wrong return type in function”错误。

因此,我肯定遗漏了JVMS中禁止此操作的相关内容。JVMS中哪部分条款规定此操作不可行?

(明确说明:我从一开始就没指望这个能成功)


内容的提问来源于stack exchange,提问作者Johannes Kuhn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:55:54