nqp icon indicating copy to clipboard operation
nqp copied to clipboard

[JVM] Java limit for method code size can be hit by defining large hashes

Open usev6 opened this issue 3 years ago • 1 comments

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.

usev6 avatar Jun 12 '21 14:06 usev6

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!

usev6 avatar Jun 12 '21 15:06 usev6