Uploaded image for project: 'Commons BCEL'
  1. Commons BCEL
  2. BCEL-280

MethodGen.setMaxLocals() should take in account local variables defined in Local Variable table

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 6.0
    • Fix Version/s: 6.x
    • Component/s: Main
    • Labels:
      None
    • Environment:

      Ubuntu 14.04, Bcel 6.0

      Description

      Kotlin compiler generates additional fake local variables that are not used in method code, after processing such methods with BCEL fake variables are kept in local variable table but maxLocals value decreased to number of locals used in method bytecode.
      As result "java.lang.ClassFormatError: Invalid index 6 in LocalVariableTable in class file test/Bug2" exception is thrown at runtime on attemt to load newly generated class.
      Sample code:

      bug.kt
      package bug
      
      import org.apache.bcel.classfile.ClassParser
      import org.apache.bcel.generic.ClassGen
      import org.apache.bcel.generic.MethodGen
      
      class Bug {
          fun test() {
              val list = arrayListOf(1, 2, 3)
              list.forEach {
                  println(it)
              }
             //here additional variable are generated - see bytecode below
          }
      }
      
      fun main(args: Array<String>) {
          val parser = ClassParser(ClassLoader.getSystemClassLoader().getResourceAsStream("bug/Bug.class"), "Bug.class");
          val javaClass = parser.parse();
          val classGen = ClassGen(javaClass)
          classGen.className = "test.Bug2"
          val originalMethod = classGen.methods.filter { it.name == "test" }.single()
          classGen.removeMethod(originalMethod)
          val newMethodGen = MethodGen(originalMethod, classGen.className, classGen.constantPool)
          newMethodGen.setMaxLocals()
      
          val newMethod = newMethodGen.method
          classGen.addMethod(newMethod)
      
          val bug2Class = classGen.javaClass
      
      
      //    Exception in thread "main" java.lang.ClassFormatError: Invalid index 6 in LocalVariableTable in class file test/Bug2
      //    at java.lang.ClassLoader.defineClass1(Native Method)
      //    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
      //    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
      //    at bug.BugKt$main$1.findClass(bug.kt:53)
      //    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
      //    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
      //    at bug.BugKt.main(bug.kt:58)
          object : ClassLoader(ClassLoader.getSystemClassLoader()) {
              @Throws(ClassNotFoundException::class)
              protected override fun findClass(name: String): Class<*> {
                  if (name == "test.Bug2") {
                      val bytes = bug2Class.bytes
                      return defineClass(name, bytes, 0, bytes.size)
                  }
      
                  return super.findClass(name)
              }
          }.loadClass("test.Bug2").newInstance()
      
          //8 != 6
          assert(originalMethod.code.maxLocals == newMethod.code.maxLocals)
      }
      
      Bug.test() original bytecode
       // access flags 0x11
        public final test()V
         L0
          LINENUMBER 9 L0
          ICONST_3
          ANEWARRAY java/lang/Integer
          DUP
          ICONST_0
          ICONST_1
          INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
          AASTORE
          DUP
          ICONST_1
          ICONST_2
          INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
          AASTORE
          DUP
          ICONST_2
          ICONST_3
          INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
          AASTORE
          INVOKESTATIC kotlin/collections/CollectionsKt.arrayListOf ([Ljava/lang/Object;)Ljava/util/ArrayList;
          ASTORE 1
         L1
          LINENUMBER 10 L1
          ALOAD 1
          CHECKCAST java/lang/Iterable
          ASTORE 2
          NOP
         L2
          LINENUMBER 56 L2
          ALOAD 2
          INVOKEINTERFACE java/lang/Iterable.iterator ()Ljava/util/Iterator;
          ASTORE 3
         L3
          ALOAD 3
          INVOKEINTERFACE java/util/Iterator.hasNext ()Z
          IFEQ L4
          ALOAD 3
          INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
          ASTORE 4
         L5
          ALOAD 4
          CHECKCAST java/lang/Number
          INVOKEVIRTUAL java/lang/Number.intValue ()I
          ISTORE 5
         L6
          LINENUMBER 11 L6
          NOP
         L7
          GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
          ILOAD 5
          INVOKEVIRTUAL java/io/PrintStream.println (I)V
         L8
         L9
          LINENUMBER 11 L9
         L10
          LINENUMBER 12 L10
         L11
          NOP
         L12
          GOTO L3
         L4
          LINENUMBER 57 L4
         L13
         L14
          LINENUMBER 14 L14
          RETURN
         L15
          LOCALVARIABLE it I L6 L11 5
          LOCALVARIABLE $i$a$1$forEach I L6 L11 6
          LOCALVARIABLE element$iv Ljava/lang/Object; L5 L12 4
          LOCALVARIABLE $receiver$iv Ljava/lang/Iterable; L2 L13 2
          LOCALVARIABLE $i$f$forEach I L2 L13 7
          LOCALVARIABLE list Ljava/util/ArrayList; L1 L15 1
          LOCALVARIABLE this Lbug/Bug; L0 L15 0
          MAXSTACK = 4
          MAXLOCALS = 8
      

        Attachments

        1. sample-project.zip
          12 kB
          Max Kammerer

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              kammerer Max Kammerer
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: