We're getting the following interesting error: ```...
# announcements
s
We're getting the following interesting error:
Copy code
java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    com/example/Foo.bar(Lkotlin/jvm/functions/Function3;)Lcom/example/Other$ThingConsumer; @13: invokespecial
  Reason:
    Type 'kotlin/jvm/functions/Function3' (current frame, stack[3]) is not assignable to 'kotlin/jvm/internal/Ref$ObjectRef'
when attempting to use:
Copy code
runBlocking { consumer(connection, event) }
where
consumer: suspend (Connection, Event) -> Unit
is passed into the function calling
runBlocking
. We can hack around it by replacing it with something like
GlobalScope.async { consumer(connection, event) }.asCompletableFuture().get()
but it's less than ideal 😬 This is using Kotlin 1.4.10 and kotlinx.coroutines 1.4.0. Any thoughts?
e
something like this https://pl.kotl.in/_OgXgWaLB seems to work fine, can you try to find what else is needed to repro?
s
Hmm, it may be a bit tricky but let me try to get something small and reproducible.
This is proving challenging to reduce it down to something smaller. So far it appears to be something related to Guice attempting to inject that class, where the
runBlocking
call exists inside of a SAM. If the
runBlocking
is moved outside the SAM, but still kept within the same surrounding function it doesn't crash...
This should be a minimal reproducible example:
Copy code
package foo

import kotlinx.coroutines.runBlocking
import java.util.function.Consumer

class Example {
    fun <A> fn(consumer: suspend (A) -> Unit):
            Consumer<A> =
        Consumer { a: A ->
            runBlocking { consumer(a) }
        }
}

fun main() {
    val constructors = Example::class.java.constructors
    println("hi $constructors")
}
Resulting in:
Copy code
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    foo/Example.fn(Lkotlin/jvm/functions/Function2;)Ljava/util/function/Consumer; @11: invokespecial
  Reason:
    Type 'kotlin/jvm/functions/Function2' (current frame, stack[2]) is not assignable to 'kotlin/jvm/internal/Ref$ObjectRef'
  Current Frame:
    bci: @11
    flags: { }
    locals: { 'foo/Example', 'kotlin/jvm/functions/Function2' }
    stack: { uninitialized 6, uninitialized 6, 'kotlin/jvm/functions/Function2' }
  Bytecode:
    0x0000000: 2b12 0ab8 0010 bb00 1259 2bb7 0016 c000
    0x0000010: 18b0                                   

	at java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
	at java.lang.Class.getConstructors(Class.java:1651)
	at foo.ExampleKt.main(Example.kt:15)
	at foo.ExampleKt.main(Example.kt)
v
A
VerifyError
should imho never happen, I'd recommend opening an issue for that
👍 1
e
that example passes for me on JDK 8, JDK 11, and JDK14 with Kotlin 1.4.10
s
hmm... let me triple check the version of the compiler
e
I agree that a VerifyError should never occur, I am curious to see the generated bytecode
s
I'll compile it from the command line, just in case IntelliJ isn't using what it says it should be...
v
Works fine on play.kotl.in with Kotlin 1.4.10 and Kotlin 1.3.72
s
I'm seeing
@Lkotlin/Metadata;(mv={1, 4, 0} ...
in the generated bytecode - does that suggest it's compiled with 1.4.0?
e
no, that's the version used by 1.4.x
s
right
e
Copy code
0x0000000: 2b12 0ab8 0010 bb00 1259 2bb7 0016 c000
    0x0000010: 18b0
disassembling by hand, that is
Copy code
aload_1
ldc 10
invokestatic #16
new #18
dup
aload_1
invokespecial #22
checkcast #24
areturn
s
I've got the bytecode generated by IntelliJ if that is useful
e
I think the VerifyError is pointing to the invokespecial but it would be nice to know what the pool constants are
s
That seems to align with:
Copy code
// access flags 0x11
  // signature <A:Ljava/lang/Object;>(Lkotlin/jvm/functions/Function2<-TA;-Lkotlin/coroutines/Continuation<-Lkotlin/Unit;>;+Ljava/lang/Object;>;)Ljava/util/function/Consumer<TA;>;
  // declaration: java.util.function.Consumer<A> fn<A>(kotlin.jvm.functions.Function2<? super A, ? super kotlin.coroutines.Continuation<? super kotlin.Unit>, ? extends java.lang.Object>)
  public final fn(Lkotlin/jvm/functions/Function2;)Ljava/util/function/Consumer;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
    // annotable parameter count: 1 (visible)
    // annotable parameter count: 1 (invisible)
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 1
    LDC "consumer"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullParameter (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 9 L1
    NEW foo/Example$fn$1
    DUP
    ALOAD 1
    INVOKESPECIAL foo/Example$fn$1.<init> (Lkotlin/jvm/functions/Function2;)V
    CHECKCAST java/util/function/Consumer
   L2
    LINENUMBER 11 L2
    ARETURN
   L3
    LOCALVARIABLE this Lfoo/Example; L0 L3 0
    LOCALVARIABLE consumer Lkotlin/jvm/functions/Function2; L0 L3 1
    MAXSTACK = 3
    MAXLOCALS = 2
e
hard to see how
kotlin/jvm/internal/Ref$ObjectRef
is involved at all
is Guice still in the picture somewhere?
s
No, it turns out Guice was just calling
Class:getConstructors()
which seems to be the trigger
Decompiled using Intellij and (while I don't really know what I'm looking for) this looks suspicious:
Copy code
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                  Intrinsics.checkNotNullParameter(completion, "completion");
                  Function2 var3 = new <anonymous constructor>(completion);
                  var3.p$ = (CoroutineScope)value;
                  return var3;
               }
Trying to return a
Function2
when it's expecting a
Continuation
e
all the types in the bytecode seem to line up
that decompile is for a different method?
s
It's within
fn
in that example above. Here's the whole class:
Copy code
public final class Example {
   @NotNull
   public final Consumer fn(@NotNull final Function2 consumer) {
      Intrinsics.checkNotNullParameter(consumer, "consumer");
      return (Consumer)(new Consumer() {
         public final void accept(final Object a) {
            BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new Function2((Continuation)null) {
               private CoroutineScope p$;
               Object L$0;
               int label;

               @Nullable
               public final Object invokeSuspend(@NotNull Object $result) {
                  Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                  CoroutineScope $this$runBlocking;
                  switch(this.label) {
                  case 0:
                     ResultKt.throwOnFailure($result);
                     $this$runBlocking = this.p$;
                     Function2 var10000 = consumer;
                     Object var10001 = a;
                     this.L$0 = $this$runBlocking;
                     this.label = 1;
                     if (var10000.invoke(var10001, this) == var3) {
                        return var3;
                     }
                     break;
                  case 1:
                     $this$runBlocking = (CoroutineScope)this.L$0;
                     ResultKt.throwOnFailure($result);
                     break;
                  default:
                     throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                  }

                  return Unit.INSTANCE;
               }

               @NotNull
               public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                  Intrinsics.checkNotNullParameter(completion, "completion");
                  Function2 var3 = new <anonymous constructor>(completion);
                  var3.p$ = (CoroutineScope)value;
                  return var3;
               }

               public final Object invoke(Object var1, Object var2) {
                  return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
               }
            }), 1, (Object)null);
         }
      });
   }
}
e
JVM bytecode is designed so that each part can be statically verified on its own
if you unzip this into a new directory and run
gradle
from the command line, does it still error? (your failing .kt from above + minimal build script)
it has no problems for me, so if it does for you, then that narrows the problem down to… the JVM maybe⁈
s
That works fine... 😕
I just tried using the
kotlinc
that is bundled with the IntelliJ plugin, and that also worked fine...
Is there any way to determine the compiler version used at runtime?
e
not as far as I know
if I drop the kotlin version to 1.4.0 or raise it to 1.4.20-RC it seems to work also
so maybe something else injected in your IDE
s
I think I might know what it could be...
We're stuck using sbt as a built tool at the moment, which I believe uses kotlin-scripting-compiler-embeddable to compile
well, it's something new to try understand 😅
e
tried to make a simple sbt build;
sbt run
here works too
even if it failed, it is still quite a mystery…
s
I have a sneaking suspicion that the sbt plugin is pulling in an older version of the embeddable compiler (1.3.50 maybe?)
Anyway, I'll poke at it some more but I think it's safe to assume that it is local to our build somehow. If I get to the bottom of it I'll report back 🙂
Thanks for taking the time to help.
e
sure, I'm really puzzled and would like to understand what's happening too!
s
So I've managed to replicate it with that bar.zip project ... by removing the
kotlinVersion
key (or changing the version explicitly to 1.3.50
So for some reason sbt isn't picking up the version that we're setting in our project and is defaulting to 1.3.50 🤦
e
oh no! hopefully that's easy to solve
and then you should introduce some 1.4-only feature like trailing commas to make sure you can't possibly be using an older version ;)
😅 1
v
But trailing commas are evil 😄
s
consider it more motivation to finish migrating over to gradle
👌 1
e
in the end, I think this is https://youtrack.jetbrains.com/issue/KT-29614 - so you could try 1.3.60 if you can't go all the way to 1.4.x