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;
}
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 String
s, 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.