test/jdk/com/sun/jdi/EarlyReturnTest.java
changeset 47216 71c04702a3d5
parent 44423 306c020eb154
child 49118 dbbbf6d7cf6e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/EarlyReturnTest.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,804 @@
+/*
+ * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6175634
+ * @summary Allow early return from methods
+ *
+ * @bug 6431720
+ * @summary Unexpected InvalidTypeException when call ThreadReference.forceEarlyReturn with VoidValue
+ *
+ * @bug 6432855
+ * @summary Need a way to create JDI VoidValue for use in ThreadReference.forceEarlyReturn
+ *
+ * @author Tim Bell (based on MethodExitReturnValuesTest by Jim Holmlund)
+ *
+ * @run build TestScaffold VMConnection TargetListener TargetAdapter
+ * @run compile -g EarlyReturnTest.java
+ * @run driver EarlyReturnTest
+ */
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+import com.sun.jdi.request.*;
+import java.util.*;
+import java.net.URLClassLoader;
+import java.net.URL;
+import java.lang.reflect.Array;
+
+/*
+ * This test has a debuggee which calls a static method
+ * for each kind of JDI Value, and then an instance method
+ * for each.
+ *
+ * The debugger sets breakpoints in all methods.  When a breakpoint
+ * is hit the debugger requests an early return and supplies a new
+ * return value.  It then checks that the correct return values are
+ * included in the MethodExitEvents.
+ *
+ * Each value is stored in a static var in the debuggee.  The debugger
+ * gets the values from these static vars to check for correct
+ * return values in the MethodExitEvents.
+ */
+
+class EarlyReturnTarg {
+    static boolean debuggerWatching = false;
+    static int failureCount = 0;
+    /*
+     * These are the values that will be used by methods
+     * returning normally.
+     */
+    static URL[] urls = new URL[1];
+    public static byte      byteValue = 89;
+    public static char      charValue = 'x';
+    public static double    doubleValue = 2.2;
+    public static float     floatValue = 3.3f;
+    public static int       intValue = 1;
+    public static long      longValue = Long.MAX_VALUE;
+    public static short     shortValue = 8;
+    public static boolean   booleanValue = false;
+
+    public static Class       classValue = Object.class;
+    public static ClassLoader classLoaderValue;
+    {
+        try {
+            urls[0] = new URL("hi there");
+        } catch (java.net.MalformedURLException ee) {
+        }
+        classLoaderValue = new URLClassLoader(urls);
+    }
+
+    public static Thread      threadValue = Thread.currentThread();
+    public static ThreadGroup threadGroupValue = threadValue.getThreadGroup();
+    public static String      stringValue = "abc";
+    public static int[]       intArrayValue = new int[] {1, 2, 3};
+
+    public static EarlyReturnTarg  objectValue =
+        new EarlyReturnTarg();
+    public String ivar = stringValue;
+
+    /*
+     * These are the values that will be used by methods
+     * returning early.  These are != the normal values
+     * defined above.
+     */
+    static URL[] eurls = new URL[1];
+    public static byte      ebyteValue = 42;
+    public static char      echarValue = 'a';
+    public static double    edoubleValue = 6.6;
+    public static float     efloatValue = 9.9f;
+    public static int       eintValue = 7;
+    public static long      elongValue = Long.MIN_VALUE;
+    public static short     eshortValue = 3;
+    public static boolean   ebooleanValue = true;
+
+    public static Class eclassValue = String.class;
+    public static ClassLoader eclassLoaderValue;
+    {
+        try {
+            urls[0] = new URL("been there, done that");
+        } catch (java.net.MalformedURLException ee) {
+        }
+        classLoaderValue = new URLClassLoader(urls);
+    }
+    public static Thread ethreadValue;
+    public static ThreadGroup ethreadGroupValue;
+    public static String estringValue = "wxyz";
+    public static int[]       eintArrayValue = new int[] {10, 11, 12};
+
+    public static java.util.Date eobjectValue = new java.util.Date();
+
+    // Used to check the return values seen on the debugee side
+    public static boolean chk(byte v) {
+        return v == (debuggerWatching ? ebyteValue: byteValue);
+    }
+    public static boolean chk(char v) {
+        return v == (debuggerWatching ? echarValue: charValue);
+    }
+    public static boolean chk(double v) {
+        return v == (debuggerWatching ? edoubleValue: doubleValue);
+    }
+    public static boolean chk(float v) {
+        return v == (debuggerWatching ? efloatValue: floatValue);
+    }
+    public static boolean chk(int v) {
+        return v == (debuggerWatching ? eintValue: intValue);
+    }
+    public static boolean chk(long v) {
+        return v == (debuggerWatching ? elongValue: longValue);
+    }
+    public static boolean chk(short v) {
+        return v == (debuggerWatching ? eshortValue: shortValue);
+    }
+    public static boolean chk(boolean v) {
+        return v == (debuggerWatching ? ebooleanValue: booleanValue);
+    }
+    public static boolean chk(String v) {
+        return v.equals(debuggerWatching ? estringValue: stringValue);
+    }
+    public static boolean chk(Object v) {
+        return v.equals(debuggerWatching ? eobjectValue: objectValue);
+    }
+
+    // Used to show which set of tests follows
+    public static String s_show(String p1) { return p1;}
+
+    // These are the static methods
+    public static byte s_bytef(int p1){ return byteValue; }
+    public static char s_charf()      { return charValue; }
+    public static double s_doublef()  { return doubleValue; }
+    public static float s_floatf()    { return floatValue; }
+    public static int s_intf()        { return intValue; }
+    public static long s_longf()      { return longValue; }
+    public static short s_shortf()    { return shortValue; }
+    public static boolean s_booleanf(){ return booleanValue; }
+    public static String s_stringf()  { return stringValue; }
+    public static Class s_classf()    { return classValue; }
+    public static ClassLoader s_classLoaderf()
+                                      { return classLoaderValue; }
+    public static Thread s_threadf()  { return threadValue; }
+    public static ThreadGroup s_threadGroupf()
+                                      { return threadGroupValue; }
+    public static int[] s_intArrayf() { return intArrayValue; }
+    public static Object s_nullObjectf() { return null; }
+    public static Object s_objectf()  { return objectValue; }
+    public static void s_voidf()      { System.err.println("debugee in s_voidf");}
+
+    // These are the instance methods
+    public byte i_bytef(int p1)      { return byteValue; }
+    public char i_charf()            { return charValue; }
+    public double i_doublef()        { return doubleValue; }
+    public float i_floatf()          { return floatValue; }
+    public int i_intf()              { return intValue; }
+    public long i_longf()            { return longValue; }
+    public short i_shortf()          { return shortValue; }
+    public boolean i_booleanf()      { return booleanValue; }
+    public String i_stringf()        { return stringValue; }
+    public Class i_classf()          { return classValue; }
+    public ClassLoader i_classLoaderf()
+                                     { return classLoaderValue; }
+    public Thread i_threadf()        { return threadValue; }
+    public ThreadGroup i_threadGroupf()
+                                     { return threadGroupValue; }
+    public int[] i_intArrayf()       { return intArrayValue; }
+    public Object i_nullObjectf()    { return null; }
+    public Object i_objectf()        { return objectValue; }
+    public void i_voidf()            {}
+
+    static void doit(EarlyReturnTarg xx) throws Exception {
+        System.err.print("debugee in doit ");
+        if (debuggerWatching) {
+            System.err.println("with a debugger watching.  Early returns expected.");
+        } else {
+            System.err.println("with no debugger watching.  Normal returns.");
+        }
+
+        s_show("==========  Testing static methods ================");
+        if (!chk( s_bytef(88))) failureCount++;
+        if (!chk( s_charf())) failureCount++;
+        if (!chk( s_doublef())) failureCount++;
+        if (!chk( s_floatf())) failureCount++;
+        if (!chk( s_intf())) failureCount++;
+        if (!chk( s_longf())) failureCount++;
+        if (!chk( s_shortf())) failureCount++;
+        if (!chk( s_booleanf())) failureCount++;
+
+        if (!chk( s_stringf())) failureCount++;
+        s_classf();
+        s_classLoaderf();
+        s_threadf();
+        s_threadGroupf();
+        s_intArrayf();
+        s_nullObjectf();
+        if (!chk( s_objectf())) failureCount++;
+        s_voidf();
+
+        s_show("==========  Testing instance methods ================");
+        if (!chk( xx.i_bytef(89))) failureCount++;
+        if (!chk( xx.i_charf())) failureCount++;
+        if (!chk( xx.i_doublef())) failureCount++;
+        if (!chk( xx.i_floatf())) failureCount++;
+        if (!chk( xx.i_intf())) failureCount++;
+        if (!chk( xx.i_longf())) failureCount++;
+        if (!chk( xx.i_shortf())) failureCount++;
+        if (!chk( xx.i_booleanf())) failureCount++;
+        if (!chk( xx.i_stringf())) failureCount++;
+        xx.i_intArrayf();
+        xx.i_classf();
+        xx.i_classLoaderf();
+        xx.i_threadf();
+        xx.i_threadGroupf();
+        xx.i_nullObjectf();
+        if (!chk( xx.i_objectf())) failureCount++;
+        xx.i_voidf();
+
+    }
+
+    /** Hang so that test fails */
+    static void hang() {
+        try {
+            // ten minute nap
+            Thread.currentThread().sleep(10 * 60 * 1000);
+        } catch (InterruptedException exc) {
+            // shouldn't happen
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        // The debugger will stop at the start of main,
+        // set breakpoints and then do a resume.
+        System.err.println("debugee in main");
+        EarlyReturnTarg xx =
+            new EarlyReturnTarg();
+
+        doit(xx);
+        if (debuggerWatching && failureCount > 0) {
+            hang();
+            throw new Exception("EarlyReturnTarg: failed");
+        }
+    }
+}
+
+
+
+public class EarlyReturnTest extends TestScaffold {
+
+
+    /*
+     * Class patterns for which we don't want events (copied
+     * from the "Trace.java" example):
+     *     http://java.sun.com/javase/technologies/core/toolsapis/jpda/
+     */
+    private String[] excludes = {
+        "javax.*",
+        "sun.*",
+        "com.sun.*",
+        "com.oracle.*",
+        "oracle.*"};
+
+    static VirtualMachineManager vmm ;
+    ClassType targetClass;
+    Field theValueField;
+    static int earlyReturns = 0;
+    static final int expectedEarlyReturns = 34; // determined by inspection :-)
+
+    EarlyReturnTest(String args[]) {
+        super(args);
+    }
+
+    public static void main(String[] args)      throws Exception {
+        EarlyReturnTest meee = new EarlyReturnTest(args);
+        vmm = Bootstrap.virtualMachineManager();
+        meee.startTests();
+    }
+
+    // chkXXX methods lifted directly from MethodExitReturnValuesTest
+    // These methods check for correct return values.  Thanks, Jim!
+    void ckByteValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("ebyteValue");
+        ByteValue theValue = (ByteValue)targetClass.getValue(theValueField);
+
+        byte vv = theValue.value();
+        byte rv = ((ByteValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: byte: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: byte " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckCharValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("echarValue");
+        CharValue theValue = (CharValue)targetClass.getValue(theValueField);
+
+        char vv = theValue.value();
+        char rv = ((CharValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: char: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: char " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckDoubleValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("edoubleValue");
+        DoubleValue theValue = (DoubleValue)targetClass.getValue(theValueField);
+
+        double vv = theValue.value();
+        double rv = ((DoubleValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: double: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: double " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckFloatValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("efloatValue");
+        FloatValue theValue = (FloatValue)targetClass.getValue(theValueField);
+
+        float vv = theValue.value();
+        float rv = ((FloatValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: float: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: float " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckIntValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("eintValue");
+        IntegerValue theValue = (IntegerValue)targetClass.getValue(theValueField);
+
+        int vv = theValue.value();
+        int rv = ((IntegerValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: int: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: int " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckLongValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("elongValue");
+        LongValue theValue = (LongValue)targetClass.getValue(theValueField);
+
+        long vv = theValue.value();
+        long rv = ((LongValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: long: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: long " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckShortValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("eshortValue");
+        ShortValue theValue = (ShortValue)targetClass.getValue(theValueField);
+
+        short vv = theValue.value();
+        short rv = ((ShortValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: short: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: short " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckBooleanValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("ebooleanValue");
+        BooleanValue theValue = (BooleanValue)targetClass.getValue(theValueField);
+
+        boolean vv = theValue.value();
+        boolean rv = ((BooleanValue)retValue).value();
+        if (vv != rv) {
+            failure("failure: boolean: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: boolean " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckStringValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("estringValue");
+        StringReference theValue = (StringReference)targetClass.getValue(theValueField);
+
+        String vv = theValue.value();
+        String rv = ((StringReference)retValue).value();
+        if (vv != rv) {
+            failure("failure: String: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: String: " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckClassValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("eclassValue");
+        ClassObjectReference vv = (ClassObjectReference)targetClass.
+            getValue(theValueField);
+
+        ClassObjectReference rv = (ClassObjectReference)retValue;
+        if (vv != rv) {
+            failure("failure: Class: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: Class: " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckClassLoaderValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("eclassLoaderValue");
+        ClassLoaderReference vv = (ClassLoaderReference)targetClass.
+            getValue(theValueField);
+
+        ClassLoaderReference rv = (ClassLoaderReference)retValue;
+        if (vv != rv) {
+            failure("failure: ClassLoader: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: ClassLoader: " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckThreadValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("ethreadValue");
+        ThreadReference vv = (ThreadReference)targetClass.
+            getValue(theValueField);
+
+        ThreadReference rv = (ThreadReference)retValue;
+        if (vv != rv) {
+            failure("failure: Thread: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: Thread: " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckThreadGroupValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("ethreadGroupValue");
+        ThreadGroupReference vv = (ThreadGroupReference)targetClass.
+            getValue(theValueField);
+
+        ThreadGroupReference rv = (ThreadGroupReference)retValue;
+        if (vv != rv) {
+            failure("failure: ThreadgGroup: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: ThreadGroup: " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckArrayValue(Value retValue) {
+        Field theValueField = targetClass.fieldByName("eintArrayValue");
+        ArrayReference theValue = (ArrayReference)targetClass.getValue(theValueField);
+        IntegerValue theElem2 = (IntegerValue)theValue.getValue(2);
+
+        ArrayReference theRetValue = (ArrayReference)retValue;
+        IntegerValue retElem2 = (IntegerValue)theRetValue.getValue(2);
+        int vv = theElem2.value();
+        int rv = retElem2.value();
+        if (vv != rv) {
+            failure("failure: in[2]: expected " + vv + ", got " + rv);
+        } else {
+            System.out.println("Passed: int[2]: " + rv);
+            earlyReturns++;
+        }
+    }
+
+    void ckNullObjectValue(Value retValue) {
+        if (retValue != null) {
+            failure("failure: NullObject: expected " + null + ", got " + retValue);
+        } else {
+            System.out.println("Passed: NullObject: " + retValue);
+            earlyReturns++;
+        }
+    }
+
+    void ckObjectValue(Value retValue) {
+        ObjectReference theRetValue = (ObjectReference)retValue;
+
+        Field theIVarField = targetClass.fieldByName("eobjectValue");
+        ObjectReference theRetValField = (ObjectReference)targetClass.getValue(theIVarField);
+
+        if (! theRetValue.equals(theRetValField)) {
+            failure("failure: Object: expected " + theIVarField + ", got " + theRetValField);
+        } else {
+            System.out.println("Passed: Object: " + theRetValField);
+            earlyReturns++;
+        }
+    }
+
+    void ckVoidValue(Value retValue) {
+        System.out.println("Passed: Void");
+        earlyReturns++;
+    }
+
+    public BreakpointRequest setBreakpoint(String clsName,
+                                           String methodName,
+                                           String methodSignature) {
+        ReferenceType rt = findReferenceType(clsName);
+        if (rt == null) {
+            rt = resumeToPrepareOf(clsName).referenceType();
+        }
+
+        Method method = findMethod(rt, methodName, methodSignature);
+        if (method == null) {
+            throw new IllegalArgumentException("Bad method name/signature");
+        }
+        BreakpointRequest bpr = eventRequestManager().createBreakpointRequest(method.location());
+        bpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
+        bpr.enable();
+        return bpr;
+    }
+
+    public void breakpointReached(BreakpointEvent event) {
+        String origMethodName = event.location().method().name();
+        String methodName = origMethodName.substring(2);
+        ThreadReference tr = event.thread();
+
+        if (vm().canForceEarlyReturn()) {
+
+            try {
+
+                if ("bytef".equals(methodName)){
+                    Field theValueField = targetClass.fieldByName("ebyteValue");
+                    ByteValue theValue = (ByteValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                    /*
+                     * See what happens if we access the stack after the force
+                     * and before the resume.  Disabling this since spec says
+                     * the stack is undefined.  This type of code can be used to
+                     * pursue just what that means.
+                     *
+                     * StackFrame sf = tr.frame(0);
+                     * List<Value> ll = sf.getArgumentValues();
+                     * for (Value vv: ll) {
+                     *     System.out.println("vv = " + vv);
+                     * }
+                     */
+                } else if ("charf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("echarValue");
+                    CharValue theValue = (CharValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("doublef".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("edoubleValue");
+                    DoubleValue theValue = (DoubleValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("floatf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("efloatValue");
+                    FloatValue theValue = (FloatValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("intf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("eintValue");
+                    IntegerValue theValue = (IntegerValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("longf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("elongValue");
+                    LongValue theValue = (LongValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("shortf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("eshortValue");
+                    ShortValue theValue = (ShortValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("booleanf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("ebooleanValue");
+                    BooleanValue theValue = (BooleanValue)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("stringf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("estringValue");
+                    StringReference theValue = (StringReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("classf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("eclassValue");
+                    ClassObjectReference theValue = (ClassObjectReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("classLoaderf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("eclassLoaderValue");
+                    ClassLoaderReference theValue = (ClassLoaderReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("threadf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("ethreadValue");
+                    ThreadReference theValue = (ThreadReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("threadGroupf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("ethreadGroupValue");
+                    ThreadGroupReference theValue = (ThreadGroupReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("intArrayf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("eintArrayValue");
+                    ArrayReference theValue = (ArrayReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("nullObjectf".equals(methodName)) {
+                    tr.forceEarlyReturn(null);
+                } else if ("objectf".equals(methodName)) {
+                    Field theValueField = targetClass.fieldByName("eobjectValue");
+                    ObjectReference theValue = (ObjectReference)targetClass.getValue(theValueField);
+                    tr.forceEarlyReturn(theValue);
+                } else if ("voidf".equals(methodName)) {
+                    VoidValue theValue = vm().mirrorOfVoid();
+                    tr.forceEarlyReturn(theValue);
+                } else {
+                    failure("failure: Unknown methodName: " + origMethodName);
+                }
+
+            } catch (Exception ex) {
+                failure("failure: " + ex.toString());
+                ex.printStackTrace();
+            }
+        } else {
+            System.out.println("Cannot force early return for method: " + origMethodName);
+        }
+    }
+
+    // This is the MethodExitEvent handler.
+    public void methodExited(MethodExitEvent event) {
+        String origMethodName = event.method().name();
+        if (vm().canGetMethodReturnValues()) {
+            Value retValue = event.returnValue();
+
+            if (!origMethodName.startsWith("s_") &&
+                !origMethodName.startsWith("i_")) {
+                // Skip all uninteresting methods
+                return;
+            }
+
+            String methodName = origMethodName.substring(2);
+            if ("show".equals(methodName)) {
+                System.out.println(retValue);
+                return;
+            }
+
+            if ("bytef".equals(methodName))             ckByteValue(retValue);
+            else if ("charf".equals(methodName))        ckCharValue(retValue);
+            else if ("doublef".equals(methodName))      ckDoubleValue(retValue);
+            else if ("floatf".equals(methodName))       ckFloatValue(retValue);
+            else if ("intf".equals(methodName))         ckIntValue(retValue);
+            else if ("longf".equals(methodName))        ckLongValue(retValue);
+            else if ("shortf".equals(methodName))       ckShortValue(retValue);
+            else if ("booleanf".equals(methodName))     ckBooleanValue(retValue);
+            else if ("stringf".equals(methodName))      ckStringValue(retValue);
+            else if ("classf".equals(methodName))       ckClassValue(retValue);
+            else if ("classLoaderf".equals(methodName)) ckClassLoaderValue(retValue);
+            else if ("threadf".equals(methodName))      ckThreadValue(retValue);
+            else if ("threadGroupf".equals(methodName)) ckThreadGroupValue(retValue);
+            else if ("intArrayf".equals(methodName))    ckArrayValue(retValue);
+            else if ("nullObjectf".equals(methodName))  ckNullObjectValue(retValue);
+            else if ("objectf".equals(methodName))      ckObjectValue(retValue);
+            else if ("voidf".equals(methodName))        ckVoidValue(retValue);
+            else {
+                failure("failure: Unknown methodName: " + origMethodName);
+            }
+        } else {
+            System.out.println("Return Value not available for method: " + origMethodName);
+        }
+    }
+
+    protected void runTests() throws Exception {
+        /*
+         * Get to the top of main()
+         * to determine targetClass and mainThread
+         */
+
+        BreakpointEvent bpe = startToMain("EarlyReturnTarg");
+        targetClass = (ClassType)bpe.location().declaringType();
+        mainThread = bpe.thread();
+
+        /*
+         * Ask for method exit events
+         */
+        MethodExitRequest exitRequest =
+            eventRequestManager().createMethodExitRequest();
+
+        for (int i=0; i<excludes.length; ++i) {
+            exitRequest.addClassExclusionFilter(excludes[i]);
+        }
+        int sessionSuspendPolicy = EventRequest.SUSPEND_ALL;
+        //sessionSuspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
+        //sessionSuspendPolicy = EventRequest.SUSPEND_NONE;
+        exitRequest.setSuspendPolicy(sessionSuspendPolicy);
+        exitRequest.enable();
+
+        /*
+         * Turn on the flag so debugee knows to check for early
+         * return values instead of regular return values.
+         */
+        Field flagField = targetClass.fieldByName("debuggerWatching");
+        targetClass.setValue(flagField, vm().mirrorOf(true));
+
+
+        /*
+         * We set and enable breakpoints on all of the interesting
+         * methods called by doit().  In the breakpointReached()
+         * handler we force an early return with a different return
+         * value.
+         *
+         * The MethodExitEvent handler will keep score.
+         */
+
+        setBreakpoint("EarlyReturnTarg", "s_bytef", "(I)B");
+        setBreakpoint("EarlyReturnTarg", "s_charf", "()C");
+        setBreakpoint("EarlyReturnTarg", "s_doublef", "()D");
+        setBreakpoint("EarlyReturnTarg", "s_floatf", "()F");
+        setBreakpoint("EarlyReturnTarg", "s_intf", "()I");
+        setBreakpoint("EarlyReturnTarg", "s_longf", "()J");
+        setBreakpoint("EarlyReturnTarg", "s_shortf", "()S");
+        setBreakpoint("EarlyReturnTarg", "s_booleanf", "()Z");
+
+        setBreakpoint("EarlyReturnTarg", "s_stringf", "()Ljava/lang/String;");
+        setBreakpoint("EarlyReturnTarg", "s_classf", "()Ljava/lang/Class;");
+        setBreakpoint("EarlyReturnTarg", "s_classLoaderf", "()Ljava/lang/ClassLoader;");
+        setBreakpoint("EarlyReturnTarg", "s_threadf", "()Ljava/lang/Thread;");
+        setBreakpoint("EarlyReturnTarg", "s_threadGroupf", "()Ljava/lang/ThreadGroup;");
+        setBreakpoint("EarlyReturnTarg", "s_intArrayf", "()[I");
+        setBreakpoint("EarlyReturnTarg", "s_nullObjectf", "()Ljava/lang/Object;");
+        setBreakpoint("EarlyReturnTarg", "s_objectf", "()Ljava/lang/Object;");
+        setBreakpoint("EarlyReturnTarg", "s_voidf", "()V");
+
+        setBreakpoint("EarlyReturnTarg", "i_bytef", "(I)B");
+        setBreakpoint("EarlyReturnTarg", "i_charf", "()C");
+        setBreakpoint("EarlyReturnTarg", "i_doublef", "()D");
+        setBreakpoint("EarlyReturnTarg", "i_floatf", "()F");
+        setBreakpoint("EarlyReturnTarg", "i_intf", "()I");
+        setBreakpoint("EarlyReturnTarg", "i_longf", "()J");
+        setBreakpoint("EarlyReturnTarg", "i_shortf", "()S");
+        setBreakpoint("EarlyReturnTarg", "i_booleanf", "()Z");
+        setBreakpoint("EarlyReturnTarg", "i_stringf", "()Ljava/lang/String;");
+        setBreakpoint("EarlyReturnTarg", "i_intArrayf", "()[I");
+        setBreakpoint("EarlyReturnTarg", "i_classf", "()Ljava/lang/Class;");
+        setBreakpoint("EarlyReturnTarg", "i_classLoaderf", "()Ljava/lang/ClassLoader;");
+        setBreakpoint("EarlyReturnTarg", "i_threadf", "()Ljava/lang/Thread;");
+        setBreakpoint("EarlyReturnTarg", "i_threadGroupf", "()Ljava/lang/ThreadGroup;");
+        setBreakpoint("EarlyReturnTarg", "i_nullObjectf", "()Ljava/lang/Object;");
+        setBreakpoint("EarlyReturnTarg", "i_objectf", "()Ljava/lang/Object;");
+        setBreakpoint("EarlyReturnTarg", "i_voidf", "()V");
+
+        /* Here we go.  This adds 'this' as a listener so
+         * that our handlers above will be called.
+         */
+        listenUntilVMDisconnect();
+
+        if (earlyReturns != expectedEarlyReturns) {
+            failure("failure: Expected " + expectedEarlyReturns +
+                    ", but got " + earlyReturns);
+        }
+        System.out.println("All done, " + earlyReturns + " passed");
+
+
+        if (!testFailed) {
+            System.out.println();
+            System.out.println("EarlyReturnTest: passed");
+        } else {
+            System.out.println();
+            System.out.println("EarlyReturnTest: failed");
+            throw new Exception("EarlyReturnTest: failed");
+        }
+    }
+}