commons-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Max Kammerer (JIRA)" <j...@apache.org>
Subject [jira] [Updated] (BCEL-280) MethodGen.setMaxLocals() should take in account local variables defined in Local Variable table
Date Sat, 03 Sep 2016 10:27:20 GMT

     [ https://issues.apache.org/jira/browse/BCEL-280?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]

Max Kammerer updated BCEL-280:
------------------------------
    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:
{code:title=bug.kt|borderStyle=solid}
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)
}
{code}
{code:title=Bug.test() original bytecode|borderStyle=solid}
 // 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
{code}

  was:
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:
{code:title=bug.kt|borderStyle=solid}
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)
}
{code}
{code:title=Bug.test() original bytecode borderStyle=solid}
 // 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
{code}


> MethodGen.setMaxLocals() should take in account local variables defined in Local Variable
table
> -----------------------------------------------------------------------------------------------
>
>                 Key: BCEL-280
>                 URL: https://issues.apache.org/jira/browse/BCEL-280
>             Project: Commons BCEL
>          Issue Type: Bug
>          Components: Main
>    Affects Versions: 6.0
>         Environment: Ubuntu 14.04, Bcel 6.0
>            Reporter: Max Kammerer
>         Attachments: sample-project.zip
>
>
> 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:
> {code:title=bug.kt|borderStyle=solid}
> 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)
> }
> {code}
> {code:title=Bug.test() original bytecode|borderStyle=solid}
>  // 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
> {code}



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Mime
View raw message