[ASM 6.2] Regression in computing max stack size
I work on an open source project called OpenLiberty (https://github.com/OpenLiberty/open-liberty) and we have been using ASM for a few years now. We have been able to seamlessly upgrade from ASM 5.0 all the way up to ASM 6.1.1 over the years. However, currently I am trying to upgrade us to v6.2 and there appears to be a regression in computing max stack size. We use ASM for auto-injecting trace points, and utilize the ClassWriter.COMPUTE_MAXS flag.
With the upgrade to 6.2, I am seeing errors such as this:
java.lang.VerifyError: Operand stack overflow
Exception Details:
Location:
com/ibm/ws/threading/internal/java.lang.VerifyError: Operand stack overflow Exception D ExecutorServiceImpl.createExecutor()V @216: aload_2
Reason:
Exceeded max stack size.
Current Frame:
bci: @216
flags: { }
locals: { 'com/ibm/ws/threading/internal/ExecutorServiceImpl', 'java/util/concurrent/ThreadPoolExecutor', 'java/lang/String', integer, integer, long, long_2nd, 'java/util/concurrent/BlockingQueue', 'java/util/concurrent/RejectedExecutionHandler' }
stack: { 'com/ibm/ws/threading/internal/ExecutorServiceImpl', uninitialized 180, uninitialized 180, integer, integer, long, long_2nd, 'java/util/concurrent/TimeUnit', 'java/util/concurrent/BlockingQueue', uninitialized 208, uninitialized 208, 'java/lang/String' }
Bytecode:
0x0000000: 2ab4 0005 c700 04b1 2ab4 0003 c600 0a2a
0x0000010: b400 03b6 000f 2ab4 0002 4c2a 2ab4 0005
0x0000020: 1211 b900 1202 00c0 009f b500 04bb 0014
0x0000030: 59b7 0015 2ab4 0004 b600 1612 17b6 0016
0x0000040: b600 184d 2ab4 0005 1219 b900 1202 00b8
0x0000050: 001a b800 1b3e 2ab4 0005 121c b900 1202
0x0000060: 00b8 001a b800 1b36 042a b400 0512 1db9
0x0000070: 0012 0200 b800 1ab8 001e 3705 1504 9d00
0x0000080: 0712 2036 041d 9c00 0905 b800 2168 3e1d
0x0000090: 1504 b800 223e bb00 2359 12cb 1103 e811
0x00000a0: 03e8 b700 253a 07bb 0026 5919 072a b700
0x00000b0: 273a 082a bb00 9e59 1d15 0416 05b2 0029
0x00000c0: 1907 2ab4 000a c600 0a2a b400 0aa7 000f
0x00000d0: bb00 2a59 2ab4 0004 2cb7 002b 1908 b700
0x00000e0: 2cb5 0002 2abb 002d 592a 2ab4 0002 b700
0x00000f0: 2eb5 0003 2bc6 0008 2a2b b700 10b1
Stackmap Table:
same_frame(@8)
same_frame(@22)
full_frame(@133,{Object[#157],Object[#158],Object[#159],Integer,Integer,Long},{})
same_frame(@143)
full_frame(@208,{Object[#157],Object[#158],Object[#159],Integer,Integer,Long,Object[#160],Object[#161]},{Object[#157],Uninitialized[#180],Uninitialized[#180],Integer,Integer,Long,Object[#294],Object[#160]})
full_frame(@220,{Object[#157],Object[#158],Object[#159],Integer,Integer,Long,Object[#160],Object[#161]},{Object[#157],Uninitialized[#180],Uninitialized[#180],Integer,Integer,Long,Object[#294],Object[#160],Object[#163]})
same_frame(@253)
at com.ibm.ws.threading.internal.PolicyExecutorTest.testExpedite(PolicyExecutorTest.java:77)
<rest of stack just junit calling the test case>
Issue reproduced on:
- Mac and Linux
- Oracle JDK 8 (1.8.0_151) and IBM JDK 7 (build pxa6470_27sr3fp40-20160422_01(SR3 FP40))
For reference, the PolicyExecutorTest and ExecutorServiceImpl can be found here: https://github.com/OpenLiberty/open-liberty/blob/master/dev/com.ibm.ws.threading/test/com/ibm/ws/threading/internal/PolicyExecutorTest.java https://github.com/OpenLiberty/open-liberty/blob/master/dev/com.ibm.ws.threading/src/com/ibm/ws/threading/internal/ExecutorServiceImpl.java
I see there were two patches made in 6.2 which altered the behavior of computing max stack size: #317823 (closed) #317827 (closed)
In issue 317827, Eric alluded to this potentially breaking existing users.
I attempted to use the CheckClassAdapter
to validate the instrumentation, and surprisingly it passed just fine:
InputStream is = ExecutorServiceImpl.class.getResourceAsStream("/com/ibm/ws/threading/internal/ExecutorServiceImpl.class");
ClassReader reader = new ClassReader(is);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new CheckClassAdapter(writer, true);
reader.accept(visitor, 0);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
CheckClassAdapter.verify(new ClassReader(writer.toByteArray()), false, pw);
Assert.assertTrue(sw.toString().isEmpty());
There is certainly a behavior change between ASM 6.1.1 and 6.2 somewhere, it's just a matter of tracking down what it is. Any pointers on how to proceed from here would be much appreciated.