João Cardoso
João Cardoso

Reputation: 1

What's this error and how can I fix it? - java.lang.VerifyError: Bad local variable type

I'm implementing a YamlParser in Kotlin with Cojen Maker and we've stumbled upon a roadblock and have no idea of what this error is or why it's happening

We think it has something to do with the type casting on buildYamlParser() but without it it won't work. What do you guys think?

This is the code:

package pt.isel

import org.cojen.maker.ClassMaker
import org.cojen.maker.MethodMaker
import org.cojen.maker.Variable
import java.lang.reflect.Parameter
import kotlin.reflect.KClass

/**
 * A YamlParser that uses Cojen Maker to generate a parser.
 */
open class YamlParserCojen<T : Any>(
    private val type: KClass<T>,
    private val nrOfInitArgs: Int
) : AbstractYamlParser<T>(type) {

    companion object {
        private val yamlParsers: MutableMap<String, YamlParserCojen<*>> = mutableMapOf()

        private fun parserName(type: KClass<*>, nrOfInitArgs: Int): String {
            return "YamlParser${type.simpleName}$nrOfInitArgs"
        }

        /**
         * Creates a YamlParser for the given type using Cojen Maker if it does not already exist.
         * Keep it in an internal cache.
         */
        fun <T : Any> yamlParser(
            type: KClass<T>,
            nrOfInitArgs: Int = type.constructors.first().parameters.size
        ): AbstractYamlParser<T> {
            return yamlParsers.getOrPut(parserName(type, nrOfInitArgs)) {
                YamlParserCojen(type, nrOfInitArgs)
                    .buildYamlParser()
                    .finish()
                    .getConstructor(KClass::class.java, Integer::class.java)
                    .newInstance(type, nrOfInitArgs) as YamlParserCojen<*>
            } as YamlParserCojen<T>
        }
    }

    /**
     * Used to get a parser for other Type using the same parsing approach.
     */
    override fun <T : Any> yamlParser(type: KClass<T>) = YamlParserCojen.yamlParser(type)

    fun createNewParser(type: Class<T>) = YamlParserCojen.yamlParser(type.kotlin)

    /**
     * Do not change this method in YamlParserCojen.
     */
    override fun newInstance(args: Map<String, Any>): T {
        throw UnsupportedOperationException("This method is overridden in a subclass dynamically generated by buildYamlParser() function!")
    }

    private fun buildYamlParser(): ClassMaker {
        val cm = ClassMaker.begin(parserName(type, nrOfInitArgs)).extend(YamlParserCojen::class.java).public_()
        val init: MethodMaker = cm.addConstructor(KClass::class.java, Integer::class.java).public_()
        init.invokeSuperConstructor(init.param(0), init.param(1))
        val newInstance = cm.addMethod(Any::class.java, "newInstance", Map::class.java)
        val constructor = type.java.constructors.first { it.parameters.size == nrOfInitArgs }
        val params = constructor.parameters.map { getValueForParam(it, newInstance) }.toTypedArray()
        val returnValue = newInstance.new_(type.java, *params)
        newInstance.return_(returnValue)
        return cm
    }

    private fun getValueForParam(param: Parameter, mm: MethodMaker): Variable {
        val mapValue = mm.param(0).invoke("get", param.name).cast(String::class.java)
        return when (param.type) {
            String::class.java -> mapValue
            Int::class.java -> mm.`var`(Integer::class.java).invoke("parseInt", mapValue.cast(String::class.java))
            Long::class.java -> mm.`var`(Long::class.java).invoke("parseLong", mapValue.cast(String::class.java))
            Short::class.java -> mm.`var`(Short::class.java).invoke("parseShort", mapValue.cast(String::class.java))
            Float::class.java -> mm.`var`(Float::class.java).invoke("parseFloat", mapValue.cast(String::class.java))
            Double::class.java -> mm.`var`(Double::class.java).invoke("parseDouble", mapValue.cast(String::class.java))
            Boolean::class.java -> mm.`var`(Boolean::class.java).invoke("parseBoolean", mapValue.cast(String::class.java))
            List::class.java -> getValueForList(param, mm)
            else -> getValueForObject(param, mm)
        }
    }

    private fun getValueForObject(param: Parameter, mm: MethodMaker): Variable {
        val paramClass = param.type
        val mapValue = mm.param(0).invoke("get", param.name).cast(Map::class.java)
        val yamlParser = mm.`var`(YamlParserCojen::class.java)
            .invoke("createNewParser", paramClass)
        return yamlParser.invoke("newInstance", mapValue).cast(paramClass)
    }


    private fun getValueForList(param: Parameter, mm: MethodMaker): Variable {
        TODO()
    }
}

This is the Student class:

package pt.isel.test

class Student @JvmOverloads constructor (
    val name: String,
    val nr: Int,
    val from: String,
    val address: Address? = null,
    val grades: List<Grade> = emptyList()
)

This is the test:

   @Test
    fun parseStudentWithAddress() {
        val yaml = """
                name: Maria Candida
                nr: 873435
                address:
                  street: Rua Rosa
                  nr: 78
                  city: Lisbon
                from: Oleiros"""
        val st = YamlParserCojen.yamlParser(Student::class, 4).parseObject(yaml.reader())
        assertEquals("Maria Candida", st.name)
        assertEquals(873435, st.nr)
        assertEquals("Oleiros", st.from)
        assertEquals("Rua Rosa", st.address?.street)
        assertEquals(78, st.address?.nr)
        assertEquals("Lisbon", st.address?.city)
    }

And this is the error:

java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    YamlParserStudent4-7.newInstance(Ljava/util/Map;)Ljava/lang/Object; @65: aload
  Reason:
    Type top (current frame, locals[6]) is not assignable to reference type
  Current Frame:
    bci: @65
    flags: { }
    locals: { 'YamlParserStudent4-7', 'java/util/Map', 'java/lang/String', integer, 'java/lang/String', 'java/util/Map' }
    stack: { }
  Bytecode:
    0000000: 2b12 32b9 0011 0200 c000 134d 2b12 34b9
    0000010: 0011 0200 c000 13b8 0019 3e2b 1236 b900
    0000020: 1102 00c0 0013 3a04 2b12 38b9 0011 0200
    0000030: c000 1357 2b12 38b9 0011 0200 c000 0d3a
    0000040: 0519 0612 23b6 001d 1905 b600 21c0 0023
    0000050: 3a07 bb00 2559 2c1d 1904 1907 b700 28b0
    0000060:                                        

    at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3549)
    at java.base/java.lang.Class.getConstructor0(Class.java:3754)
    at java.base/java.lang.Class.getConstructor(Class.java:2442)
    at pt.isel.YamlParserCojen$Companion.yamlParser(YamlParserCojen.kt:36)
    at pt.isel.YamlParserCojenTest.parseStudentWithAddress(YamlParserCojenTest.kt:32)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:119)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:94)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:89)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

Upvotes: 0

Views: 417

Answers (1)

boneill
boneill

Reputation: 1526

A VerifyError from something like this is usually caused by accessing a local variable which hasn't been assigned yet. Pass the -Dorg.cojen.maker.ClassMaker.DEBUG=true argument to the jvm, and then it will write the generated class to the temp directory. It can then be disassembled using javap or another tool.

Update: I was able to reproduce the problem, and sure enough, there's a local variable being accessed which hasn't been assigned. This looks like an coding exercise, and so I don't want to give away the answer. Look for the invocation of a method which requires an instance. The code would work if the method being invoked was static. Because the code is written in Kotlin (instead of Java), I found it a bit difficult to spot the problem.

I just updated the Cojen/Maker library to detect simple mistakes like this, and it now throws an exception before the VerifyError can occur. You'll have to build the library locally to get the change for now.

Upvotes: 0

Related Questions