Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

java.lang.VerifyError: Mismatched stack types #16

Open
tstockwell opened this Issue · 5 comments

2 participants

@tstockwell

The following test case will reproduce a problem that I think is a weaving issue.
I am using Java 1.6 update 25 and the latest version of kilim in the repository trunk.
I would attempt to fix this myself but I am having problems finding out what 'Mismatched stack types' even means :-)...

package com.googlecode.contraildb.tests;

import junit.framework.TestCase;
import kilim.Pausable;

public class ReproduceVerifyError extends TestCase {
    static class BadClass {

        public void doSomething() throws Pausable ,Exception {
            new String(getBytes());
        }
        public byte[] getBytes() throws Pausable {
            return "lkj;lkjad;fa".getBytes();
        }
    }

    public void testVerifyError() {
        /*
         * This line will throw the following error at runtime...
         * java.lang.VerifyError: (class: com/googlecode/contraildb/tests/BadClass, method: doSomething signature: (Lkilim/Fiber;)V) Mismatched stack types
         *   at com.googlecode.contraildb.tests.ReproduceVerifyError.testVerifyError(ReproduceVerifyError.java:8)
         */
        new BadClass();
    }
}

The VerifyError will go away if the doSomething method is written this way...

        public void doSomething() throws Pausable ,Exception {
            byte[] bytes= getBytes();
            new String(bytes);
        }

If it helps, here is a textual view of the instrumented class for BadClass...

// Compiled from ReproduceVerifyError.java (version 1.6 : 50.0, super bit)
static class com.googlecode.contraildb.tests.ReproduceVerifyError$BadClass {

  // Field descriptor #10 Z
  // Signature: Z
  public static final boolean $isWoven = true;

  // Method descriptor #13 ()V
  // Stack: 1, Locals: 1
  ReproduceVerifyError$BadClass();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [15]
    4  return
      Line numbers:
        [pc: 0, line: 7]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: com.googlecode.contraildb.tests.ReproduceVerifyError.BadClass

  // Method descriptor #19 (Lkilim/Fiber;)V
  // Stack: 4, Locals: 4
  public void doSomething(kilim.Fiber arg0) throws kilim.Pausable, java.lang.Exception;
      0  aload_1 [arg0]
      1  getfield kilim.Fiber.pc : int [29]
      4  tableswitch default: 28
          case 0: 36
          case 1: 31
     28  invokestatic kilim.Fiber.wrongPC() : void [32]
     31  aconst_null
     32  aload_0 [this]
     33  goto 40
     36  new java.lang.String [34]
     39  aload_0 [this]
     40  aload_1 [arg0]
     41  invokevirtual kilim.Fiber.down() : kilim.Fiber [38]
     44  invokevirtual com.googlecode.contraildb.tests.ReproduceVerifyError$BadClass.getBytes(kilim.Fiber) : byte[] [42]
     47  aload_1 [arg0]
     48  invokevirtual kilim.Fiber.up() : int [46]
     51  tableswitch default: 132
          case 0: 132
          case 1: 114
          case 2: 80
          case 3: 111
     80  pop
     81  new kilim.S_O [48]
     84  dup
     85  invokespecial kilim.S_O() [49]
     88  astore_2
     89  aload_2
     90  aload_0 [this]
     91  putfield kilim.State.self : java.lang.Object [55]
     94  aload_2
     95  iconst_1
     96  putfield kilim.State.pc : int [56]
     99  astore_3
    100  aload_2
    101  aload_3
    102  putfield kilim.S_O.f0 : java.lang.Object [59]
    105  aload_1 [arg0]
    106  aload_2
    107  invokevirtual kilim.Fiber.setState(kilim.State) : void [63]
    110  return
    111  pop
    112  pop
    113  return
    114  astore_2
    115  pop
    116  aload_1 [arg0]
    117  getfield kilim.Fiber.curState : kilim.State [67]
    120  checkcast kilim.S_O [48]
    123  astore_3
    124  aload_3
    125  getfield kilim.S_O.f0 : java.lang.Object [59]
    128  checkcast java.lang.String [34]
    131  aload_2
    132  invokespecial java.lang.String(byte[]) [70]
    135  return
      Line numbers:
        [pc: 36, line: 10]
        [pc: 135, line: 11]
      Local variable table:
        [pc: 36, pc: 136] local: this index: 0 type: com.googlecode.contraildb.tests.ReproduceVerifyError.BadClass

  // Method descriptor #13 ()V
  // Stack: 0, Locals: 2
  public void doSomething() throws kilim.Pausable, java.lang.Exception;
    0  invokestatic kilim.Task.errNotWoven() : void [75]
    3  return


  // Method descriptor #40 (Lkilim/Fiber;)[B
  // Stack: 2, Locals: 2
  public byte[] getBytes(kilim.Fiber arg0) throws kilim.Pausable;
    0  aload_1 [arg0]
    1  ldc <String "lkj;lkjad;fa"> [77]
    3  invokevirtual java.lang.String.getBytes() : byte[] [80]
    6  areturn
      Line numbers:
        [pc: 1, line: 13]
      Local variable table:
        [pc: 1, pc: 7] local: this index: 0 type: com.googlecode.contraildb.tests.ReproduceVerifyError.BadClass

  // Method descriptor #78 ()[B
  // Stack: 1, Locals: 2
  public byte[] getBytes() throws kilim.Pausable;
    0  invokestatic kilim.Task.errNotWoven() : void [75]
    3  aconst_null
    4  areturn

  Inner classes:
    [inner class info: #2 com/googlecode/contraildb/tests/ReproduceVerifyError$BadClass, outer class info: #7 com/googlecode/contraildb/tests/ReproduceVerifyError
     inner name: #8 BadClass, accessflags: 8 static]
}
@tstockwell tstockwell closed this
@tstockwell tstockwell reopened this
@tstockwell

I have identified one problem in the generated code shown in the original post, though it is not the cause of the 'mismatched stack types' error....

In the getBytes(kilim.Fiber arg0) method... the stack will not be empty when the method returns.
This is because kilim inserted the aload_1 opcode at the beginning of the method which puts the incoming Fiber on the stack.

Kilim obviously detected that the getBytes(kilim.Fiber arg0) calls no Pausable methods, therefore it should have made no changes at all to the getBytes(kilim.Fiber arg0) method.

@kilim kilim referenced this issue from a commit
@sriram-srinivasan sriram-srinivasan Fix for Issue #16. If a method is marked pausable, but doesn't make a…
…ny pausable calls, then the prelude was being generated incorrectly: the fiber argument was loaed onto the stack, but not popped on return.

	modified:   src/kilim/analysis/MethodWeaver.java
	modified:   test/kilim/test/ex/ExBasicBlock.java
706cbb5
@kilim
Owner

Fixed in commit 706cbb5

@tstockwell

Made a little progress on the 'mismatched stack types' error.

I have created a very small method that reproduces the same verification error.
Here it is...

/ Method descriptor #16 (I)V
  // Stack: 2, Locals: 2
  public void doSomething(int arg0) throws kilim.Pausable, java.lang.Exception;
     0  iload_1 [arg0]
     1  bipush 0
     3  if_icmpne 12
     6  new java.lang.String [22]
     9  goto 18
    12  new java.lang.String [22]
    15  goto 18
    18  pop
    19  return

This method causes a 'mismatched stack types' verification error. I don't understand why, but when I do then I should have a solution to the original problem...

@kilim
Owner

This is most strange. I get (class: Foo, method: doSomething signature: (I)V) Inconsistent stack height 1 != 0 for your code, slightly changed to run it through my assembler: kilim.tools.Asm

.class public Foo
.super java/lang/Object
.method public <init>()V
    aload 0                                                                ; 0
    invokespecial java/lang/Object/<init>()V                               ; 1
    return        
   .limit stack 1
   .limit locals 1
.end method

.method public doSomething(I)V
   iload_1
   iconst_0
   if_icmpne lb
   new java/lang/String
   dup
   ldc "Foo"
   invokespecial java/lang/String/<init>(Ljava/lang/String;)V
   goto lb

   new java/lang/String
   dup
   ldc "Bar"
   invokespecial java/lang/String/<init>(Ljava/lang/String;)V
   goto lb
lb:     
   pop
   return
   .limit stack 3
   .limit locals 2
.end method

However, if I invoke pop right after the invokespecials (and of course remove the pop before the return, it works fine. I don't get it.

@tstockwell

Stackmap frames are the puzzle piece that is missing.
The ASM manual has a good intro to stackmap frames.
Below is some code that uses ASM to generate a .class file that duplicates the VerifyError.
If you comment out the section labeled 'CREATE STACKMAP FRAMES' then run it, you will get the 'Mismatched stack types' error when you try to load the generated class. If you uncomment the 'CREATE STACKMAP FRAMES' section and regenerate the .class then the VerifyError goes away.

This is because the Java 1.6 verifier does not do sophisticated flow analysis, it relies on these pre-generated stackmap frames to verify methods quickly. Without the proper stackmap frames the verifier will fail.

My next step will be to figure out what stackmap frames need to be created to make the original kilim-generated code work....

package com.googlecode.contraildb.tests;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.attrs.StackMapFrame;
import org.objectweb.asm.attrs.StackMapTableAttribute;
import org.objectweb.asm.attrs.StackMapType;

@SuppressWarnings({"rawtypes", "unchecked"})
public class CompileTestsDump implements Opcodes {

    public static void main(String[] args) throws Exception {
        byte[] bytes= dump();
        File file= new File("com/googlecode/contraildb/tests/GeneratedTest.class");
        OutputStream out= new FileOutputStream(file);
        out.write(bytes);
        out.flush();
        out.close();
        System.out.println("Successfully generated "+file);
    }

    public static byte[] dump () throws Exception {

        ClassWriter cw = new ClassWriter(false);
        MethodVisitor mv;

        cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "com/googlecode/contraildb/tests/GeneratedTest", null, "java/lang/Object", null);

        {
            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
            mv.visitCode();
            mv.visitTypeInsn(NEW, "com/googlecode/contraildb/tests/GeneratedTest");
            mv.visitInsn(DUP);
            mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/contraildb/tests/GeneratedTest", "<init>", "()V");
            mv.visitInsn(ICONST_0);
            mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/contraildb/tests/GeneratedTest", "compare", "(I)V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(2, 1);
            mv.visitEnd();
        }
        {
            mv = cw.visitMethod(ACC_PUBLIC, "compare", "(I)V", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitVarInsn(ILOAD, 1);
            Label l1 = new Label();
            mv.visitJumpInsn(IFNE, l1);
            mv.visitTypeInsn(NEW, "java/lang/String");
            Label l2 = new Label();
            mv.visitJumpInsn(GOTO, l2);
            mv.visitLabel(l1);
            mv.visitTypeInsn(NEW, "java/lang/String");
            mv.visitLabel(l2);
            mv.visitInsn(POP);
            mv.visitInsn(RETURN);

            // CREATE STACKMAP FRAMES
            {
                List frames = new ArrayList();
                {
                    List locals = new ArrayList();
                    {
                        StackMapType attrframe0Info0 = StackMapType.getTypeInfo( StackMapType.ITEM_Object);
                        attrframe0Info0.setObject("com/googlecode/contraildb/tests/GeneratedTest");
                        locals.add(attrframe0Info0);
                    }
                    List stack = Collections.EMPTY_LIST;
                    StackMapFrame attrframe0 = new StackMapFrame(l0, locals, stack);
                    frames.add(attrframe0);
                }
                {
                    List locals = new ArrayList();
                    {
                        StackMapType attrframe1Info0 = StackMapType.getTypeInfo( StackMapType.ITEM_Object);
                        attrframe1Info0.setObject("com/googlecode/contraildb/tests/GeneratedTest");
                        locals.add(attrframe1Info0);
                    }
                    List stack = Collections.EMPTY_LIST;
                    StackMapFrame attrframe1 = new StackMapFrame(l1, locals, stack);
                    frames.add(attrframe1);
                }
                {
                    List locals = new ArrayList();
                    {
                        StackMapType attrframe2Info0 = StackMapType.getTypeInfo( StackMapType.ITEM_Object);
                        attrframe2Info0.setObject("com/googlecode/contraildb/tests/GeneratedTest");
                        locals.add(attrframe2Info0);
                    }
                    List stack = new ArrayList();
                    {
                        StackMapType attrframe2Info0 = StackMapType.getTypeInfo( StackMapType.ITEM_Top);
                        stack.add(attrframe2Info0);
                    }
                    StackMapFrame attrframe2 = new StackMapFrame(l2, locals, stack);
                    frames.add(attrframe2);
                }
                StackMapTableAttribute attr = new StackMapTableAttribute(frames);
                mv.visitAttribute(attr);
            }


            mv.visitMaxs(1, 2);
            mv.visitEnd();
        }
        cw.visitEnd();

        return cw.toByteArray();
    }

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.