nqp
nqp copied to clipboard
[JVM] Java limit for method code size can be hit by defining large hashes
In https://github.com/rakudo/rakudo/pull/4397 an unexpected error showed up: Java's limit of 65535 bytes for the code length of methods is hit, if a hash with many entries is defined.
The error can be provoked by just defining a hash with 2244 entries, like so:
$ ## define function that creates a nqp::hash with a given number of entries
$ create_hash () { name=$1; elems=$2; echo "my \$$name := nqp::hash("; for ((i=0; i<$elems; i++)); do echo "'key$i',$i,"; done; echo ');'; }
$ { create_hash foo 2243; } > test.nqp; ./nqp-j test.nqp ## 2243 entries are still ok
$ { create_hash foo 2244; } > test.nqp; ./nqp-j test.nqp ## 2244 entries hit the limit
java.lang.ClassFormatError: Invalid method Code length 65536 in class file 826811A4E482E077B970F459C3BCF17E44B6DD8E
in jvm (gen/jvm/stage2/NQPHLL.nqp:101)
in execute_stage (gen/jvm/stage2/NQPHLL.nqp:1481)
in run (gen/jvm/stage2/NQPHLL.nqp:1519)
in <anon> (gen/jvm/stage2/NQPHLL.nqp:1522)
in compile (gen/jvm/stage2/NQPHLL.nqp:1514)
in eval (gen/jvm/stage2/NQPHLL.nqp:1186)
in evalfiles (gen/jvm/stage2/NQPHLL.nqp:1460)
in command_eval (gen/jvm/stage2/NQPHLL.nqp:1350)
in command_line (gen/jvm/stage2/NQPHLL.nqp:1309)
in MAIN (gen/jvm/stage2/NQP.nqp:4183)
in <mainline> (gen/jvm/stage2/NQP.nqp:4179)
in <anon> (gen/jvm/stage2/NQP.nqp)
$ { create_hash foo 2245; } > test.nqp; ./nqp-j test.nqp ## even more entries give a different error (very likely a secondary problem)
java.lang.NoClassDefFoundError: org/objectweb/asm/tree/MethodNode
in classfile (gen/jvm/stage2/NQPHLL.nqp:92)
in execute_stage (gen/jvm/stage2/NQPHLL.nqp:1481)
in run (gen/jvm/stage2/NQPHLL.nqp:1519)
in <anon> (gen/jvm/stage2/NQPHLL.nqp:1522)
in compile (gen/jvm/stage2/NQPHLL.nqp:1514)
in eval (gen/jvm/stage2/NQPHLL.nqp:1186)
in evalfiles (gen/jvm/stage2/NQPHLL.nqp:1460)
in command_eval (gen/jvm/stage2/NQPHLL.nqp:1350)
in command_line (gen/jvm/stage2/NQPHLL.nqp:1309)
in MAIN (gen/jvm/stage2/NQP.nqp:4183)
in <mainline> (gen/jvm/stage2/NQP.nqp:4179)
in <anon> (gen/jvm/stage2/NQP.nqp)
Splitting the date into two smaller hashes does not help:
$ { create_hash foo 1122; create_hash bar 1122; } > test.nqp; ./nqp-j test.nqp
java.lang.NoClassDefFoundError: org/objectweb/asm/tree/MethodNode
in classfile (gen/jvm/stage2/NQPHLL.nqp:92)
in execute_stage (gen/jvm/stage2/NQPHLL.nqp:1481)
in run (gen/jvm/stage2/NQPHLL.nqp:1519)
in <anon> (gen/jvm/stage2/NQPHLL.nqp:1522)
in compile (gen/jvm/stage2/NQPHLL.nqp:1514)
in eval (gen/jvm/stage2/NQPHLL.nqp:1186)
in evalfiles (gen/jvm/stage2/NQPHLL.nqp:1460)
in command_eval (gen/jvm/stage2/NQPHLL.nqp:1350)
in command_line (gen/jvm/stage2/NQPHLL.nqp:1309)
in MAIN (gen/jvm/stage2/NQP.nqp:4183)
in <mainline> (gen/jvm/stage2/NQP.nqp:4179)
in <anon> (gen/jvm/stage2/NQP.nqp)
$ { create_hash foo 1121; create_hash bar 1121; } > test.nqp; ./nqp-j test.nqp ## works fine
$
Actually, it's not just entries in the hashes, that leads to the error -- other code could contribute as well. But since the error first showed up with a large hash, I sticked to that example.
This all makes sense, since we compile the code to java bytecode. I'm not sure of the details -- e.g. what exactly will be compiled as a Java method, though.
Btw, rakudo-j doesn't seem to be affected:
$ { echo 'use nqp;'; create_hash foo 25000; } > test.nqp; ~/bin/rakudo.jvm/bin/rakudo-j test.nqp
$
It blows up with Class file too large!
for larger values, though:
$ { echo 'use nqp;'; create_hash foo 50000; } > test.nqp; ~/bin/rakudo.jvm/bin/rakudo-j test.nqp
===SORRY!===
java.lang.RuntimeException: java.lang.RuntimeException: Class file too large!