Search code examples
javajvmbytecodejvm-languages

java.lang.VerifyError on constructor call


I recently started writing a compiler for my own language but it started to throw an exception when I call the main method. It works with my other test classes, but it doesn't want to work with this one. As far as I can tell, there isn't anything different with how the method is called between this class and others. Here's the exception.

Exception in thread "main" java.lang.VerifyError: (class: FizzBuzz/FizzBuzz, method: <init> signature: ()V) Incompatible object argument for function call
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2693)
at java.lang.Class.privateGetMethodRecursive(Class.java:3040)
at java.lang.Class.getMethod0(Class.java:3010)
at java.lang.Class.getMethod(Class.java:1776)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)

and here's the bytecode output from javap

public final class FizzBuzz.FizzBuzz {
  public FizzBuzz.FizzBuzz();
    Code:
       0: new           #16                 // class Lang/Int
       3: dup
       4: lconst_0
       5: invokespecial #19                 // Method Lang/Int."<init>":(J)V
       8: astore_1
       9: aload_1
      10: new           #16                 // class Lang/Int
      13: dup
      14: ldc2_w        #20                 // long 100l
      17: invokespecial #19                 // Method Lang/Int."<init>":(J)V
      20: invokevirtual #25                 // Method Lang/Int._lessThan:(LLang/Number;)LLang/Boolean;
      23: getfield      #31                 // Field Lang/Boolean.value:Z
      26: ifeq          140
      29: ldc           #33                 // String
      31: astore_2
      32: aload_1
      33: new           #16                 // class Lang/Int
      36: dup
      37: ldc2_w        #34                 // long 3l
      40: invokespecial #19                 // Method Lang/Int."<init>":(J)V
      43: invokevirtual #39                 // Method Lang/Int._modulus:(LLang/Int;)LLang/Int;
      46: new           #16                 // class Lang/Int
      49: dup
      50: lconst_0
      51: invokespecial #19                 // Method Lang/Int."<init>":(J)V
      54: invokevirtual #43                 // Method Lang/Int._equals:(Ljava/lang/Object;)LLang/Boolean;
      57: getfield      #31                 // Field Lang/Boolean.value:Z
      60: ifeq          70
      63: aload_2
      64: ldc           #45                 // String Fizz
      66: invokevirtual #51                 // Method Lang/String._add:(LLang/String;)LLang/String;
      69: astore_2
      70: nop
      71: aload_1
      72: new           #16                 // class Lang/Int
      75: dup
      76: ldc2_w        #52                 // long 5l
      79: invokespecial #19                 // Method Lang/Int."<init>":(J)V
      82: invokevirtual #39                 // Method Lang/Int._modulus:(LLang/Int;)LLang/Int;
      85: new           #16                 // class Lang/Int
      88: dup
      89: lconst_0
      90: invokespecial #19                 // Method Lang/Int."<init>":(J)V
      93: invokevirtual #43                 // Method Lang/Int._equals:(Ljava/lang/Object;)LLang/Boolean;
      96: getfield      #31                 // Field Lang/Boolean.value:Z
      99: ifeq          109
     102: aload_2
     103: ldc           #55                 // String Buzz
     105: invokevirtual #51                 // Method Lang/String._add:(LLang/String;)LLang/String;
     108: astore_2
     109: nop
     110: aload_2
     111: invokevirtual #59                 // Method Lang/String.isEmpty:()LLang/Boolean;
     114: invokevirtual #62                 // Method Lang/Boolean._not:()LLang/Boolean;
     117: getfield      #31                 // Field Lang/Boolean.value:Z
     120: ifeq          127
     123: aload_2
     124: invokestatic  #68                 // Method Lang/System.println:(Ljava/lang/Object;)V
     127: nop
     128: aload_1
     129: invokestatic  #68                 // Method Lang/System.println:(Ljava/lang/Object;)V
     132: aload_1
     133: invokevirtual #72                 // Method Lang/Int._increment:()LLang/Int;
     136: astore_1
     137: goto          9
     140: nop
     141: aload_0
     142: invokespecial #10                 // Method java/lang/Object."<init>":()V
     145: return
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
        141       5     0  this   LFizzBuzz/FizzBuzz;
          8     138     1     i   LLang/Int;
         31     115     2   str   LLang/String;

  public static void main(java.lang.String[]);
    Code:
       0: aload_0
       1: invokestatic  #80                 // Method Lang/System.setArguments:([Ljava/lang/String;)V
       4: new           #4                  // class FizzBuzz/FizzBuzz
       7: invokespecial #81                 // Method "<init>":()V
      10: return
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      11     0  args   [Ljava/lang/String;
}

Solution

  • In your code, you have the sequence

      29: ldc           #33             // String
      31: astore_2
    …
      63: aload_2
      64: ldc           #45             // String Fizz
      66: invokevirtual #51             // Method Lang/String._add:(LLang/String;)LLang/String;
    

    Of course, just because your language has a different String type doesn’t cause the JVM to use your custom type when it encounters an ldc instruction. You have to convert the java/lang/String instance created by the ldc instruction to a Lang/String instance first. Then you can invoke your add method.

    If your custom String is immutable and you want to implement compile time constants of your language’s Strings, you may use invokedynamic instructions pointing to the java/lang/String constant as static parameter. The bootstrap method may then convert it to your string type and return a constant method handle. Since the bootstrap method is only invoked once on the first execution of an invokedynamic instruction, you get the desired constant behavior.