Heath Thomann created OPENJPA-2298:
--------------------------------------

             Summary: VerifyError/Stack underflow due to extra 'return' 
statements generated by PCEnhancer.
                 Key: OPENJPA-2298
                 URL: https://issues.apache.org/jira/browse/OPENJPA-2298
             Project: OpenJPA
          Issue Type: Bug
          Components: Enhance, instrumentation
    Affects Versions: 2.2.0, 2.3.0
            Reporter: Heath Thomann
            Assignee: Heath Thomann
            Priority: Critical


I've been assisting Kevin Sutter with a 'VerifyError' and am opening this JIRA 
on his behalf.  He has discovered a situation where the following VerifyError 
occurs:

Exception data: java.lang.VerifyError: JVMVRFY036 stack underflow; 
class=irwwbase/PersistentTimerStatsJPA, 
method=pcNewObjectIdInstance(Ljava/lang/Object;)Ljava/lang/Object;, pc=26
    at java.lang.J9VMInternals.verifyImpl(Native Method)
    at java.lang.J9VMInternals.verify(J9VMInternals.java:85)
    at java.lang.J9VMInternals.initialize(J9VMInternals.java:162)
    at java.lang.Class.forNameImpl(Native Method)
    at java.lang.Class.forName(Class.java:176)
    at 
org.apache.openjpa.meta.MetaDataRepository.classForName(MetaDataRepository.java:1552)
    at 
org.apache.openjpa.meta.MetaDataRepository.loadPersistentTypesInternal(MetaDataRepository.java:1528)
    at 
org.apache.openjpa.meta.MetaDataRepository.loadPersistentTypes(MetaDataRepository.java:1506)
    at 
org.apache.openjpa.kernel.AbstractBrokerFactory.loadPersistentTypes(AbstractBrokerFactory.java:282)
    at 
org.apache.openjpa.kernel.AbstractBrokerFactory.initializeBroker(AbstractBrokerFactory.java:238)
    at 
org.apache.openjpa.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:212)
    at 
org.apache.openjpa.kernel.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:156)
    at 
org.apache.openjpa.persistence.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:227)
    ...........


This issue occurs on, and is unique to, Java 7 and our enhancement code (as 
well as the entity definition).  That is, in PCEnhancer we are generating extra 
'returns' in methods which throw an exception.  This has an effect on ASM 
post-processing needed on Java 7 which ultimately causes the VerifyError.  This 
description probably makes no sense without more details and code to go along 
with it.  So let me now show how this looks in code.  First, I'm attaching an 
entity, named PersistentTimerStatsJPA.java, and a PK class named 
'PersistentTimerStatsPK.java'.  While I won't go into exact details on how to 
use these classes to recreate the issue, I am providing them for the reader's 
reference.
Next, lets look at enhanced code before ASM post-processing occurs.  Take this 
code:

  public java.lang.Object pcNewObjectIdInstance(java.lang.Object);
    flags: ACC_PUBLIC

    Code:
      stack=3, locals=2, args_size=2
         0: new           #207                // class 
java/lang/IllegalArgumentException
         3: dup           
         4: ldc_w         #367                // String The id type \"class 
irwwbase.PersistentTimerStatsPK\" specified by persistent type \"class 
irwwbase.PersistentTimerStatsJPA\" does not have a public class 
irwwbase.PersistentTimerStatsPK(String) or class 
irwwbase.PersistentTimerStatsPK(Class, String) constructor.
         7: invokespecial #368                // Method 
java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
        10: athrow        
        11: return        

As you can see, we have a "10: athrow" and "11: return".  This extraneous "11: 
return" hasn't caused problems in the past, but as will be explained in more 
details below, with ASM post-processing necessary in Java 7, this "11: return" 
causes problems.  To see this, lets look at the code above after going through 
ASM post-processing:

  public java.lang.Object pcNewObjectIdInstance(java.lang.Object);
    flags: ACC_PUBLIC

    Code:
      stack=3, locals=2, args_size=2
         0: new           #205                // class 
java/lang/IllegalArgumentException
         3: dup           
         4: ldc_w         #363                // String The id type \"class 
irwwbase.PersistentTimerStatsPK\" specified by persistent type \"class 
irwwbase.PersistentTimerStatsJPA\" does not have a public class 
irwwbase.PersistentTimerStatsPK(String) or class 
irwwbase.PersistentTimerStatsPK(Class, String) constructor.
         7: invokespecial #364                // Method 
java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
        10: athrow        
        11: athrow        
      StackMapTable: number_of_entries = 1
           frame_type = 255 /* full_frame */
          offset_delta = 11
          locals = []
          stack = [ class java/lang/Throwable ]

Notice that the "11: return" was changed to a second "11: athrow".  To tie this 
all together, let me blatantly copy/paste a detailed description sent to me by 
Kevin: 

"When JPA was attempting to generate the bytecodes for throwing an exception, 
we were accidentally including a return bytecode as well.  Of course, in a 
normal Java environment, this would have been flagged since the return would 
have been unreachable code.  But, since we were generating the bytecodes during 
enhancement, nothing is going to flag this situation.  But, due to the Java 7 
class validation that is now being performed, we had to introduce the ASM 
post-processing.  Normally, these simple enhanced methods would not have been 
touched by ASM.  But, since these methods now had multiple exit points (due to 
the throw and return bytecodes), these methods were being flagged as 
"complicated" and needed Stack Map table adjustments performed by ASM.  
Unfortunately, this additional ASM code now introduced the situation where too 
many items would have been popped off the stack (if the code could have 
actually executed as coded).  This is the Stack Underflow exception that was 
happening during Class load time."

Basically, Kevin's fix was to find all of the areas where we generate a "throw" 
followed by a "return", and remove the "return".  Since this method is only 
attempting to throw an exception, we really don't need the second "return" 
instruction.  I'm providing Kevin's patch, which is named 'VerifyError.patch'.

Thanks,

Heath

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to