/* * 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 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