/* * Copyright (c) 2006, 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 6431735 * @summary Unexpected ClassCastException in ThreadReference.forceEarlyReturn * @author Jim Holmlund * * @run build TestScaffold VMConnection TargetListener TargetAdapter * @run compile -g EarlyReturnNegativeTest.java * @run driver EarlyReturnNegativeTest */ 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 an instance method * for each kind of JDI value return type. * * The debugger sets breakpoints in all methods. When a breakpoint * is hit the debugger requests an early return and supplies a new * return value. The new value is not compatible with the method's * return type so an InvalidTypeException should be thrown. * * Each value is stored in a static var in the debuggee. The debugger * gets the values from these static vars to pass back to the * debuggee in forceEarlyReturn. * * This test was created out of EarlyReturnTest.java. Not all of the * debuggee methods are actually used, just the ones needed to test * for correct operation. I left the others in just in case they come * in handy in the future. */ class EarlyReturnNegativeTarg { /* * 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 Object[] objectArrayValue = new Object[] {"a", "b", "c"}; public static EarlyReturnNegativeTarg objectValue = new EarlyReturnNegativeTarg(); public String ivar = stringValue; // Used to show which set of tests follows public static String s_show(String p1) { return p1;} // These are the instance methods public byte i_bytef() { 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_objectArrayf() { return objectArrayValue; } public Object i_nullObjectf() { return null; } public Object i_objectf() { return objectValue; } public void i_voidf() {} static void doit(EarlyReturnNegativeTarg xx) throws Exception { System.err.print("debugee in doit "); s_show("========== Testing instance methods ================"); xx.i_bytef(); xx.i_charf(); xx.i_doublef(); xx.i_floatf(); xx.i_intf(); xx.i_longf(); xx.i_shortf(); xx.i_booleanf(); xx.i_stringf(); xx.i_intArrayf(); xx.i_objectArrayf(); xx.i_classf(); xx.i_classLoaderf(); xx.i_threadf(); xx.i_threadGroupf(); xx.i_nullObjectf(); xx.i_objectf(); xx.i_voidf(); } 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"); EarlyReturnNegativeTarg xx = new EarlyReturnNegativeTarg(); doit(xx); } } public class EarlyReturnNegativeTest extends TestScaffold { static VirtualMachineManager vmm ; ClassType targetClass; Field theValueField; ByteValue byteVV; CharValue charVV; DoubleValue doubleVV; FloatValue floatVV; IntegerValue integerVV; LongValue longVV; ShortValue shortVV; BooleanValue booleanVV; ObjectReference objectVV; ArrayReference intArrayVV; ArrayReference objectArrayVV; VoidValue voidVV; EarlyReturnNegativeTest(String args[]) { super(args); } public static void main(String[] args) throws Exception { EarlyReturnNegativeTest meee = new EarlyReturnNegativeTest(args); vmm = Bootstrap.virtualMachineManager(); meee.startTests(); } 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; } void doEarly(ThreadReference tr, String methodName, Value val) { try { tr.forceEarlyReturn(val); } catch (InvalidTypeException ex) { System.out.println("Ok: " + methodName); return; } catch (Exception ex) { failure("failure: " + ex.toString()); ex.printStackTrace(); return; } failure("Expected InvalidTypeException for " + methodName + ", " + val + " but didn't get it."); } public void breakpointReached(BreakpointEvent event) { String origMethodName = event.location().method().name(); String methodName = origMethodName.substring(2); ThreadReference tr = event.thread(); if (vm().canForceEarlyReturn()) { /* There are some incompatible classes of values. In the following, * we test each combination. */ if ("shortf".equals(methodName)){ doEarly(tr, origMethodName, booleanVV); doEarly(tr, origMethodName, objectVV); doEarly(tr, origMethodName, voidVV); doEarly(tr, origMethodName, intArrayVV); doEarly(tr, origMethodName, objectArrayVV); } else if ("booleanf".equals(methodName)) { doEarly(tr, origMethodName, shortVV); doEarly(tr, origMethodName, objectVV); doEarly(tr, origMethodName, voidVV); doEarly(tr, origMethodName, intArrayVV); doEarly(tr, origMethodName, objectArrayVV); } else if ("intArrayf".equals(methodName)) { doEarly(tr, origMethodName, booleanVV); doEarly(tr, origMethodName, shortVV); doEarly(tr, origMethodName, voidVV); doEarly(tr, origMethodName, objectVV); doEarly(tr, origMethodName, objectArrayVV); } else if ("objectArrayf".equals(methodName)) { doEarly(tr, origMethodName, booleanVV); doEarly(tr, origMethodName, shortVV); doEarly(tr, origMethodName, voidVV); doEarly(tr, origMethodName, objectVV); doEarly(tr, origMethodName, intArrayVV); } else if ("objectf".equals(methodName)) { doEarly(tr, origMethodName, booleanVV); doEarly(tr, origMethodName, shortVV); doEarly(tr, origMethodName, voidVV); } else if ("voidf".equals(methodName)) { doEarly(tr, origMethodName, booleanVV); doEarly(tr, origMethodName, shortVV); doEarly(tr, origMethodName, objectVV); doEarly(tr, origMethodName, intArrayVV); doEarly(tr, origMethodName, objectArrayVV); } else { // just ignore others System.out.println("Ignoring: " + methodName); return; } } else { System.out.println("Cannot force early return for method: " + origMethodName); } } protected void runTests() throws Exception { /* * Get to the top of main() * to determine targetClass and mainThread */ BreakpointEvent bpe = startToMain("EarlyReturnNegativeTarg"); targetClass = (ClassType)bpe.location().declaringType(); mainThread = bpe.thread(); /* * 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. * */ setBreakpoint("EarlyReturnNegativeTarg", "i_bytef", "()B"); setBreakpoint("EarlyReturnNegativeTarg", "i_charf", "()C"); setBreakpoint("EarlyReturnNegativeTarg", "i_doublef", "()D"); setBreakpoint("EarlyReturnNegativeTarg", "i_floatf", "()F"); setBreakpoint("EarlyReturnNegativeTarg", "i_intf", "()I"); setBreakpoint("EarlyReturnNegativeTarg", "i_longf", "()J"); setBreakpoint("EarlyReturnNegativeTarg", "i_shortf", "()S"); setBreakpoint("EarlyReturnNegativeTarg", "i_booleanf", "()Z"); setBreakpoint("EarlyReturnNegativeTarg", "i_stringf", "()Ljava/lang/String;"); setBreakpoint("EarlyReturnNegativeTarg", "i_intArrayf", "()[I"); setBreakpoint("EarlyReturnNegativeTarg", "i_objectArrayf", "()[Ljava/lang/Object;"); setBreakpoint("EarlyReturnNegativeTarg", "i_classf", "()Ljava/lang/Class;"); setBreakpoint("EarlyReturnNegativeTarg", "i_classLoaderf", "()Ljava/lang/ClassLoader;"); setBreakpoint("EarlyReturnNegativeTarg", "i_threadf", "()Ljava/lang/Thread;"); setBreakpoint("EarlyReturnNegativeTarg", "i_threadGroupf", "()Ljava/lang/ThreadGroup;"); setBreakpoint("EarlyReturnNegativeTarg", "i_nullObjectf", "()Ljava/lang/Object;"); setBreakpoint("EarlyReturnNegativeTarg", "i_objectf", "()Ljava/lang/Object;"); setBreakpoint("EarlyReturnNegativeTarg", "i_voidf", "()V"); /* Create Value objects to be passed in forceEarlyReturn calls */ Field theValueField = targetClass.fieldByName("byteValue"); byteVV = (ByteValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("charValue"); charVV = (CharValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("doubleValue"); doubleVV = (DoubleValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("floatValue"); floatVV = (FloatValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("intValue"); integerVV = (IntegerValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("longValue"); longVV = (LongValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("shortValue"); shortVV = (ShortValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("booleanValue"); booleanVV = (BooleanValue)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("objectValue"); objectVV = (ObjectReference)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("intArrayValue"); intArrayVV = (ArrayReference)targetClass.getValue(theValueField); theValueField = targetClass.fieldByName("objectArrayValue"); objectArrayVV = (ArrayReference)targetClass.getValue(theValueField); voidVV = vm().mirrorOfVoid(); /* Here we go. This adds 'this' as a listener so * that our handlers above will be called. */ listenUntilVMDisconnect(); if (!testFailed) { System.out.println(); System.out.println("EarlyReturnNegativeTest: passed"); } else { System.out.println(); System.out.println("EarlyReturnNegativeTest: failed"); throw new Exception("EarlyReturnNegativeTest: failed"); } } }