bug
bug copied to clipboard
VerifyError: Inconsistent stackmap frames at branch target
Reproduction steps
Java: Oracle HotSpot 17.0.6+9-LTS-190, OpenJDK Temurin-17.0.8+7. Scala versions: 2.13.10, 2.13.14.
I faced the behaviour using spray json ("io.spray" %% "spray-json" % "1.3.6"), and I couldn't minimise the snippet to clean scala, but the hint to the nature of the bug is the two workarounds at the bottom of this report.
import spray.json.DefaultJsonProtocol._
import spray.json._
object BadScalac {
def main(args: Array[String]): Unit = {
val msg = (
try Map("block" -> "try").toJson
catch { case _: Throwable => Map("block" -> "catch").toJson }
).prettyPrint + "\n"
println(msg)
}
}
Problem
It gets compiled successfully, but when I run I get
Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 152
Exception Details:
Location:
BadScalac$.main([Ljava/lang/String;)V @149: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @149
flags: { }
locals: { 'BadScalac$', '[Ljava/lang/String;' }
stack: { 'spray/json/JsValue' }
Stackmap Frame:
bci: @152
flags: { }
locals: { 'BadScalac$', '[Ljava/lang/String;' }
stack: { 'java/lang/Object', 'spray/json/JsValue' }
Bytecode:
0000000: bb00 1f59 1220 b700 23b2 0028 b200 2db6
0000010: 0031 b200 3604 bd00 3859 03b2 003b b200
0000020: 2d12 3db6 0041 1243 b600 4753 c000 49b6
0000030: 004d b600 53b6 0057 b200 5cb2 005c b600
0000040: 60b2 005c b600 60b6 0064 b600 6aa7 004b
0000050: 57b2 0028 b200 2db6 0031 b200 3604 bd00
0000060: 3859 03b2 003b b200 2d12 3db6 0041 126c
0000070: b600 4753 c000 49b6 004d b600 53b6 0057
0000080: b200 5cb2 005c b600 60b2 005c b600 60b6
0000090: 0064 b600 6aa7 0003 b600 72b6 0076 1278
00000a0: b600 76b6 007b 4db2 002d 2cb6 007f b1
Exception Handler Table:
bci [9, 77] => handler: 80
Stackmap Table:
same_locals_1_stack_item_extended(@80,Object[#29])
full_frame(@152,{Object[#2],Object[#132]},{Object[#4],Object[#110]})
at BadScalac.main(BadScalac.scala)
But it goes away when I rewrite the source code as
import spray.json.DefaultJsonProtocol._
import spray.json._
object BadScalac extends App {
val msg = (
try Map("block" -> "try").toJson
catch { case _: Throwable => Map("block" -> "catch").toJson }
).prettyPrint + "\n"
println(msg)
}
or even when I drop the last addition of the "\n" string:
object BadScalac {
def main(args: Array[String]): Unit = {
val msg = (
try Map("block" -> "try").toJson
catch { case _: Throwable => Map("block" -> "catch").toJson }
).prettyPrint
println(msg)
}
}
The both snippets above yield
{
"block": "try"
}
with and without the last newline respectively.
String concat is handled in the backend. Variants that induce Uncurry to lift the try will work.
Other superficially similar ops such as * 2 will work.
Welcome to Scala 2.13.14 (OpenJDK 64-Bit Server VM, Java 21.0.2).
Type in expressions for evaluation. Or try :help.
scala> def f() = (try "try" catch { case _: Throwable => "catch" }).toString + "nope"
java.lang.VerifyError: Inconsistent stackmap frames at branch target 20
Exception Details:
Location:
f()Ljava/lang/String; @17: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @17
flags: { }
locals: { '$iw' }
stack: { 'java/lang/String' }
Stackmap Frame:
bci: @20
flags: { }
locals: { '$iw' }
stack: { 'java/lang/Object', 'java/lang/String' }
Bytecode:
0000000: bb00 1259 1213 b700 1712 19a7 0009 5712
0000010: 1ba7 0003 b600 20b6 0024 1226 b600 24b6
0000020: 0027 b0
Exception Handler Table:
bci [9, 11] => handler: 14
Stackmap Table:
same_locals_1_stack_item_frame(@14,Object[#16])
full_frame(@20,{Object[#2]},{Object[#4],Object[#29]})
... 29 elided
scala>
just for fun
scala> object X { def f() = (try "try" catch (_ => "catch")).toString + "nope" }
-- [E008] Not Found Error: ---------------------------------------------------------------------------------------------
1 |object X { def f() = (try "try" catch (_ => "catch")).toString + "nope" }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| value toString is not a member of ?{ toString: ? }, but could be made available as an extension method.
|
| The following import might fix the problem:
|
| import scala.reflect.Selectable.reflectiveSelectable
|
1 error found
I guess we could fix this by backporting https://github.com/scala/scala3/pull/18619