/*
* Copyright (c) 2006, 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 main 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 (vmm.majorInterfaceVersion() >= 1 &&
vmm.minorInterfaceVersion() >= 6 &&
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");
}
}
}