com.markcrocker.purifier.testcases
Class ProblemCases

java.lang.Object
  |
  +--javax.microedition.midlet.MIDlet
        |
        +--com.markcrocker.purifier.testcases.ProblemCases
All Implemented Interfaces:
javax.microedition.lcdui.CommandListener, javax.microedition.lcdui.ItemStateListener

public class ProblemCases
extends javax.microedition.midlet.MIDlet
implements javax.microedition.lcdui.CommandListener, javax.microedition.lcdui.ItemStateListener

The ProblemCases class contains methods that are intended to illustrate cases where the Purifier has problems. It is also the home of several test cases that don't test what they're intended to test... this is a work area for getting them do work correctly.


Field Summary
(package private)  javax.microedition.lcdui.Display display
           
(package private)  javax.microedition.lcdui.TextBox Hello
           
(package private)  java.util.Vector vector
           
 
Constructor Summary
ProblemCases()
           
 
Method Summary
 void catchesThrowable(java.util.Hashtable ht)
           
 void catchesThrowableFinally(java.util.Hashtable ht)
           
 void commandAction(javax.microedition.lcdui.Command c, javax.microedition.lcdui.Displayable d)
           
 void destroyApp(boolean unconditional)
           
 void exceptionHandlerObjectConflict()
          This shows how an Exception handler will put it's Exception type into the same LocalVariables slot as a working variable and, for some reason, Sun's preverifier will resolve the conflict by saying the data is type java.lang.Object.
 void exceptionHandlerPrimitiveConflict()
          This is almost identical to the exceptionHandlerObjectConflict except that the conflict is between an exception and a primitive.
 void excessiveMerging()
          excessiveMerging demonstrates a case where the merging of a Frame from normal execution flow and a Frame from the fall through of an exception handler produce results in JustIce that disagree with Sun's preverifier.
 void excessiveMerging2()
          just demonstrates that cause of problem with excessiveMerging is not related to the fact that the problem is with a return instruction immediately following an exception fall through.
 void excessiveMerging3()
           
 void excessiveMerging4()
           
 void excessiveMerging4b()
          Still has excessive merge problem even though this uses an invokeinterface instruction just like the requiresPostExceptionMerge4 method.
 void excessiveMerging4c()
          Still has excessive merge problem even though this returns a primitive just like the excessiveMerging4.
 void failsPass3a()
          Fails JustIce verification pass3a.
 void itemStateChanged(javax.microedition.lcdui.Item item)
           
 void messedUpArgs(java.lang.Integer[] I)
           
 void minimalExcessiveMerging()
           
 void minimalExcessiveMergingb()
          Uses a custom class that has a private inner class that implements an interface and a method that returns that inner class.
 void minimalRequiresPostExceptionMerge()
           
 void nestedExceptionHandlersConflict(java.lang.Integer[] I)
          Intended to show uninitialized try block frame, but fails for two totally unrelated reasons.
 void nullObjectLateInit()
          This is a counter example to the nullObjectNeverInit case.
 void nullObjectNeverInit()
          This is very similar to the last method except that the String array is never intialized to point to a real reference.
 void nullObjectNeverInit2()
          This is very similar to the unresolvedNullObject method except that the String array is never intialized to point to a real reference.
 void passesPass3a()
          Almost identical to failsPass3a, but passes JustIce verification pass3a.
 void pauseApp()
           
 void postExceptionMergeNotRequired()
          Almost identical to requiresPostExceptionMerge3, but doesn't have the same issue because the array is declared before the try block.
 void postExceptionMergeNotRequired2()
          Almost identical to requiresPostExceptionMerge3, but doesn't have the same issue because the vector variable shows up in the catch statement.
 void remove(int index)
          Adds a null to the LocalVariables that is mysteriously converted to a java.lang.Object by BCEL JustICE Verifier (well Pass3bVerifier actually).
 void remove2(int index)
          Counter example to the remove(int) method above.
 void requiresPostExceptionMerge()
          Modified version of nullObjectNeverInit intended to illustrate problems with merging Frames from Exception handlers with Frames from normal execution
 void requiresPostExceptionMerge2()
           
 void requiresPostExceptionMerge3()
           
 void requiresPostExceptionMerge4()
          Doesn't have the same problem as excessiveMerging4 despite being virtually identical.
 void retrievesAPrimitiveArray(java.lang.Integer[] I)
          intended to show a problem that occurs with anewarry, but isn't formulated correctly to show that situation.
 void startApp()
           
 void unreferencedObjectSignature()
          Has an object signature that isn't in the ConstantPool Doesn't work with all of these other methods in this class too.
 void unresolvedNullObject()
          Test case the illustrates the Unresolved Null Object Issue.
 
Methods inherited from class javax.microedition.midlet.MIDlet
getAppProperty, notifyDestroyed, notifyPaused, resumeRequest
 
Methods inherited from class java.lang.Object
, clone, equals, finalize, getClass, hashCode, notify, notifyAll, registerNatives, toString, wait, wait, wait
 

Field Detail

display

javax.microedition.lcdui.Display display

Hello

javax.microedition.lcdui.TextBox Hello

vector

java.util.Vector vector
Constructor Detail

ProblemCases

public ProblemCases()
Method Detail

startApp

public void startApp()
              throws javax.microedition.midlet.MIDletStateChangeException
Overrides:
startApp in class javax.microedition.midlet.MIDlet

pauseApp

public void pauseApp()
Overrides:
pauseApp in class javax.microedition.midlet.MIDlet

destroyApp

public void destroyApp(boolean unconditional)
                throws javax.microedition.midlet.MIDletStateChangeException
Overrides:
destroyApp in class javax.microedition.midlet.MIDlet

itemStateChanged

public void itemStateChanged(javax.microedition.lcdui.Item item)
Specified by:
itemStateChanged in interface javax.microedition.lcdui.ItemStateListener

commandAction

public void commandAction(javax.microedition.lcdui.Command c,
                          javax.microedition.lcdui.Displayable d)
Specified by:
commandAction in interface javax.microedition.lcdui.CommandListener

unresolvedNullObject

public void unresolvedNullObject()
Test case the illustrates the Unresolved Null Object Issue. There is also a method to ensure that the solution to this special case doesn't remove ITEM_Null objects where they really belong.

nullObjectNeverInit

public void nullObjectNeverInit()
This is very similar to the last method except that the String array is never intialized to point to a real reference. In this case, the StackMap should, and does, have an ITEM_Null in slot 1. Althogh unintentional, this also has the exceptionHandlerObjectConflict problem. See the nullObjectNeverInit2 method for an example that illustrates the case that this was intended to illustrate without the Exception problem in the way.

requiresPostExceptionMerge

public void requiresPostExceptionMerge()
Modified version of nullObjectNeverInit intended to illustrate problems with merging Frames from Exception handlers with Frames from normal execution

requiresPostExceptionMerge2

public void requiresPostExceptionMerge2()

requiresPostExceptionMerge3

public void requiresPostExceptionMerge3()

postExceptionMergeNotRequired

public void postExceptionMergeNotRequired()
Almost identical to requiresPostExceptionMerge3, but doesn't have the same issue because the array is declared before the try block. In the bytecode, the only difference is that inside of the Exception handler, the ArrayIndexOutOfBoundsException is stored in LocalVariables slot 3 and the Exception handler table entry starts at a different offset. Using a different LocalVariables slot is how the compiler avoids the conflict.

nullObjectLateInit

public void nullObjectLateInit()
This is a counter example to the nullObjectNeverInit case. In this case, the String[], s, is initialized inside of the try block, but only after the last instruction that can throw an ArrayIndexOutOfBoundsException, so the Exception handler still only sees the null object type during verification.

nullObjectNeverInit2

public void nullObjectNeverInit2()
This is very similar to the unresolvedNullObject method except that the String array is never intialized to point to a real reference. In this case, the StackMap should, and does, have an ITEM_Null in slot 1. Unlike the nullObjectNeverInit method, this case has been modified even mroe to avoid the exceptionHandlerObjectConflict problem, so it is somewhat less like the unresolvedNullObject case than the nullObjectNeverInit case is.

exceptionHandlerObjectConflict

public void exceptionHandlerObjectConflict()
This shows how an Exception handler will put it's Exception type into the same LocalVariables slot as a working variable and, for some reason, Sun's preverifier will resolve the conflict by saying the data is type java.lang.Object. The Purifier resolveConflicts method sees them as two different, incompatible objects and sets the slot to null.

exceptionHandlerPrimitiveConflict

public void exceptionHandlerPrimitiveConflict()
This is almost identical to the exceptionHandlerObjectConflict except that the conflict is between an exception and a primitive. This time both Sun and the Purifier agree that the conflict is unresolvable and the slot is set to null.

nestedExceptionHandlersConflict

public void nestedExceptionHandlersConflict(java.lang.Integer[] I)
Intended to show uninitialized try block frame, but fails for two totally unrelated reasons. The bytecode for this method is:
0:    iconst_0
1:    istore_2
2:    iload_2
3:    ifne		#33
6:    aload_1
7:    iload_2
8:    aaload
9:    astore_3
10:   aload_3
11:   invokevirtual	java.lang.Integer.toString ()Ljava/lang/String; (11)
14:   astore		%4
16:   goto		#24
19:   astore		%4
21:   bipush		7
23:   istore_2
24:   goto		#30
27:   astore_3
28:   iconst_3
29:   istore_2
30:   iinc		%2	1
33:   return
At offset 24 there is a conflict between the String t, which is stored in LocalVariables slot 4 by the astore %4 instruction at offset 14 and the NullPointerException that also gets stored in slot 4 by the inner excetion handler at offset 19. The Purifier's resolveConflicts method sees that these are different and incompatible and sets the slot to null assuming that these are junk variables. Sun's StackMap has:
offset = 24
locals = {
(type=Object, (23)class=com.markcrocker.purifier.testcases.ProblemCases),
(type=Object, (102)class=[Ljava.lang.Integer;),
(type=Integer),
(type=Object, (21)class=java.lang.Integer),
(type=Object, (104)class=java.lang.Object)
}
Note that the JavaDoc generator messes up the formatting of this. See the actual source code for better formatting. The Purifier produces a StackMap that has the last entry as
(type=Object, class=java.lang.NullPointerException)
There seems to be a problem with what Sun's preverifier does... it assigns this slot to java.lang.Object. This doesn't make sense. The Purifier resolveConflicts method sets the entry to null. Setting it to anything else makes no sense because nothing after offset 24 uses slot 4. This may be one of those cases where it might work on an actual device despite a difference between Purifier and preverifier. The error in the StackMap at offset 30 is wrong because it depends on the offset at offset 24. This time, the resolveConflicts method does think it's junk and sets slot 4 to null. There is a similar test case in the UniqueProblemCases class:
See Also:
UniqueProblemCases.nestedExceptionHandlersConflictWithUnknown(java.lang.Integer[])

unreferencedObjectSignature

public void unreferencedObjectSignature()
Has an object signature that isn't in the ConstantPool Doesn't work with all of these other methods in this class too.

retrievesAPrimitiveArray

public void retrievesAPrimitiveArray(java.lang.Integer[] I)
intended to show a problem that occurs with anewarry, but isn't formulated correctly to show that situation. Need to come up with a better example because this one uses the wrong instruction.

remove

public void remove(int index)
Adds a null to the LocalVariables that is mysteriously converted to a java.lang.Object by BCEL JustICE Verifier (well Pass3bVerifier actually). Need to ensure that it does, indeed, get resolved to a null, because that's what it is, at least at the time of the start of the try block, which is what the Exception handler uses to initialize the catch block. Although the null does represent a java.lang.Object and that is the correct type for it to be resolved to in the long run, that isn't known at the start of the Exception handler, so the StackMapEntry for the start of the Exception handler should show that the type is a null, not an Object.

remove2

public void remove2(int index)
Counter example to the remove(int) method above. Since there are two instructions that can throw an exception and the object type has been established by the time we get to the second one, then the merging of the types at the start of the Exception handler must, therefore, include that object type rather than a null object type.

excessiveMerging

public void excessiveMerging()
excessiveMerging demonstrates a case where the merging of a Frame from normal execution flow and a Frame from the fall through of an exception handler produce results in JustIce that disagree with Sun's preverifier. Other examples, such as requiresPostExceptionMerge, show that such merging is required in at least some cases.

minimalExcessiveMerging

public void minimalExcessiveMerging()

minimalRequiresPostExceptionMerge

public void minimalRequiresPostExceptionMerge()

minimalExcessiveMergingb

public void minimalExcessiveMergingb()
Uses a custom class that has a private inner class that implements an interface and a method that returns that inner class. This is done to show that the key issue in minimalExcessiveMerging is not related to the Vector class itself, but to the fact that the Vector.elements() method returns a class that implements an interface.

excessiveMerging2

public void excessiveMerging2()
just demonstrates that cause of problem with excessiveMerging is not related to the fact that the problem is with a return instruction immediately following an exception fall through. Clear error is due to Exception fall through alone.

excessiveMerging3

public void excessiveMerging3()

excessiveMerging4

public void excessiveMerging4()

excessiveMerging4b

public void excessiveMerging4b()
Still has excessive merge problem even though this uses an invokeinterface instruction just like the requiresPostExceptionMerge4 method.

excessiveMerging4c

public void excessiveMerging4c()
Still has excessive merge problem even though this returns a primitive just like the excessiveMerging4.

requiresPostExceptionMerge4

public void requiresPostExceptionMerge4()
Doesn't have the same problem as excessiveMerging4 despite being virtually identical. The significant difference is that Vector.toString() returns a real class rather than an interface. This one actually requires a post Exception merge to get the correct results!

catchesThrowable

public void catchesThrowable(java.util.Hashtable ht)

catchesThrowableFinally

public void catchesThrowableFinally(java.util.Hashtable ht)

messedUpArgs

public void messedUpArgs(java.lang.Integer[] I)

passesPass3a

public void passesPass3a()
Almost identical to failsPass3a, but passes JustIce verification pass3a.

failsPass3a

public void failsPass3a()
Fails JustIce verification pass3a. Based on excessiveMerging

postExceptionMergeNotRequired2

public void postExceptionMergeNotRequired2()
Almost identical to requiresPostExceptionMerge3, but doesn't have the same issue because the vector variable shows up in the catch statement. Actually, can't prove this because fails pass3a. JustIce never tests this because it comes after failsPass3a, but when re-arranged, this fails pass3a too.