1 /*
   2  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  *  @test
  26  *  @bug 6431735
  27  *  @summary Unexpected ClassCastException in ThreadReference.forceEarlyReturn
  28  *  @author Jim Holmlund
  29  *
  30  *  @run build TestScaffold VMConnection TargetListener TargetAdapter
  31  *  @run compile -g EarlyReturnNegativeTest.java
  32  *  @run driver EarlyReturnNegativeTest
  33  */
  34 import com.sun.jdi.*;
  35 import com.sun.jdi.event.*;
  36 import com.sun.jdi.request.*;
  37 import java.util.*;
  38 import java.net.URLClassLoader;
  39 import java.net.URL;
  40 import java.lang.reflect.Array;
  41 
  42 /*
  43  * This test has a debuggee which calls an instance method
  44  * for each kind of JDI value return type.
  45  *
  46  * The debugger sets breakpoints in all methods.  When a breakpoint
  47  * is hit the debugger requests an early return and supplies a new
  48  * return value. The new value is not compatible with the method's
  49  * return type so an InvalidTypeException should be thrown.
  50  *
  51  * Each value is stored in a static var in the debuggee.  The debugger
  52  * gets the values from these static vars to pass back to the
  53  * debuggee in forceEarlyReturn.
  54  *
  55  * This test was created out of EarlyReturnTest.java.  Not all of the
  56  * debuggee methods are actually used, just the ones needed to test
  57  * for correct operation.  I left the others in just in case they come
  58  * in handy in the future.
  59  */
  60 
  61 class EarlyReturnNegativeTarg {
  62     /*
  63      * These are the values that will be used by methods
  64      * returning normally.
  65      */
  66     static URL[] urls = new URL[1];
  67     public static byte      byteValue = 89;
  68     public static char      charValue = 'x';
  69     public static double    doubleValue = 2.2;
  70     public static float     floatValue = 3.3f;
  71     public static int       intValue = 1;
  72     public static long      longValue = Long.MAX_VALUE;
  73     public static short     shortValue = 8;
  74     public static boolean   booleanValue = false;
  75 
  76     public static Class       classValue = Object.class;
  77     public static ClassLoader classLoaderValue;
  78     {
  79         try {
  80             urls[0] = new URL("hi there");
  81         } catch (java.net.MalformedURLException ee) {
  82         }
  83         classLoaderValue = new URLClassLoader(urls);
  84     }
  85 
  86     public static Thread      threadValue = Thread.currentThread();
  87     public static ThreadGroup threadGroupValue = threadValue.getThreadGroup();
  88     public static String      stringValue = "abc";
  89     public static int[]       intArrayValue = new int[] {1, 2, 3};
  90     public static Object[]    objectArrayValue = new Object[] {"a", "b", "c"};
  91 
  92     public static EarlyReturnNegativeTarg  objectValue =
  93         new EarlyReturnNegativeTarg();
  94     public String ivar = stringValue;
  95 
  96 
  97     // Used to show which set of tests follows
  98     public static String s_show(String p1) { return p1;}
  99 
 100     // These are the instance methods
 101     public byte i_bytef()            { return byteValue; }
 102     public char i_charf()            { return charValue; }
 103     public double i_doublef()        { return doubleValue; }
 104     public float i_floatf()          { return floatValue; }
 105     public int i_intf()              { return intValue; }
 106     public long i_longf()            { return longValue; }
 107     public short i_shortf()          { return shortValue; }
 108     public boolean i_booleanf()      { return booleanValue; }
 109     public String i_stringf()        { return stringValue; }
 110     public Class i_classf()          { return classValue; }
 111     public ClassLoader i_classLoaderf()
 112                                      { return classLoaderValue; }
 113     public Thread i_threadf()        { return threadValue; }
 114     public ThreadGroup i_threadGroupf()
 115                                      { return threadGroupValue; }
 116     public int[] i_intArrayf()       { return intArrayValue; }
 117     public Object[] i_objectArrayf() { return objectArrayValue; }
 118     public Object i_nullObjectf()    { return null; }
 119     public Object i_objectf()        { return objectValue; }
 120     public void i_voidf()            {}
 121 
 122     static void doit(EarlyReturnNegativeTarg xx) throws Exception {
 123         System.err.print("debugee in doit ");
 124 
 125         s_show("==========  Testing instance methods ================");
 126         xx.i_bytef();
 127         xx.i_charf();
 128         xx.i_doublef();
 129         xx.i_floatf();
 130         xx.i_intf();
 131         xx.i_longf();
 132         xx.i_shortf();
 133         xx.i_booleanf();
 134         xx.i_stringf();
 135         xx.i_intArrayf();
 136         xx.i_objectArrayf();
 137         xx.i_classf();
 138         xx.i_classLoaderf();
 139         xx.i_threadf();
 140         xx.i_threadGroupf();
 141         xx.i_nullObjectf();
 142         xx.i_objectf();
 143         xx.i_voidf();
 144 
 145     }
 146 
 147     public static void main(String[] args) throws Exception {
 148         /*
 149          * The debugger will stop at the start of main,
 150          * set breakpoints and then do a resume.
 151          */
 152         System.err.println("debugee in main");
 153 
 154         EarlyReturnNegativeTarg xx =
 155             new EarlyReturnNegativeTarg();
 156 
 157         doit(xx);
 158     }
 159 }
 160 
 161 
 162 
 163 public class EarlyReturnNegativeTest extends TestScaffold {
 164 
 165     static VirtualMachineManager vmm ;
 166     ClassType targetClass;
 167     Field theValueField;
 168 
 169     ByteValue byteVV;
 170     CharValue charVV;
 171     DoubleValue doubleVV;
 172     FloatValue floatVV;
 173     IntegerValue integerVV;
 174     LongValue longVV;
 175     ShortValue shortVV;
 176     BooleanValue booleanVV;
 177     ObjectReference objectVV;
 178     ArrayReference intArrayVV;
 179     ArrayReference objectArrayVV;
 180     VoidValue voidVV;
 181 
 182     EarlyReturnNegativeTest(String args[]) {
 183         super(args);
 184     }
 185 
 186     public static void main(String[] args)      throws Exception {
 187         EarlyReturnNegativeTest meee = new EarlyReturnNegativeTest(args);
 188         vmm = Bootstrap.virtualMachineManager();
 189         meee.startTests();
 190     }
 191 
 192     public BreakpointRequest setBreakpoint(String clsName,
 193                                            String methodName,
 194                                            String methodSignature) {
 195         ReferenceType rt = findReferenceType(clsName);
 196         if (rt == null) {
 197             rt = resumeToPrepareOf(clsName).referenceType();
 198         }
 199 
 200         Method method = findMethod(rt, methodName, methodSignature);
 201         if (method == null) {
 202             throw new IllegalArgumentException("Bad method name/signature");
 203         }
 204         BreakpointRequest bpr = eventRequestManager().createBreakpointRequest(method.location());
 205         bpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
 206         bpr.enable();
 207         return bpr;
 208     }
 209 
 210     void doEarly(ThreadReference tr, String methodName, Value val) {
 211         try {
 212             tr.forceEarlyReturn(val);
 213         } catch (InvalidTypeException ex) {
 214             System.out.println("Ok: " + methodName);
 215             return;
 216         } catch (Exception ex) {
 217             failure("failure: " + ex.toString());
 218             ex.printStackTrace();
 219             return;
 220         }
 221         failure("Expected InvalidTypeException for " + methodName + ", " + val + " but didn't get it.");
 222     }
 223 
 224     public void breakpointReached(BreakpointEvent event) {
 225         String origMethodName = event.location().method().name();
 226         String methodName = origMethodName.substring(2);
 227         ThreadReference tr = event.thread();
 228 
 229         if (vmm.majorInterfaceVersion() >= 1 &&
 230             vmm.minorInterfaceVersion() >= 6 &&
 231             vm().canForceEarlyReturn()) {
 232 
 233             /* There are some incompatible classes of values.  In the following,
 234              * we test each combination.
 235              */
 236             if ("shortf".equals(methodName)){
 237                 doEarly(tr, origMethodName, booleanVV);
 238                 doEarly(tr, origMethodName, objectVV);
 239                 doEarly(tr, origMethodName, voidVV);
 240                 doEarly(tr, origMethodName, intArrayVV);
 241                 doEarly(tr, origMethodName, objectArrayVV);
 242 
 243             } else if ("booleanf".equals(methodName)) {
 244                 doEarly(tr, origMethodName, shortVV);
 245                 doEarly(tr, origMethodName, objectVV);
 246                 doEarly(tr, origMethodName, voidVV);
 247                 doEarly(tr, origMethodName, intArrayVV);
 248                 doEarly(tr, origMethodName, objectArrayVV);
 249 
 250             } else if ("intArrayf".equals(methodName)) {
 251                 doEarly(tr, origMethodName, booleanVV);
 252                 doEarly(tr, origMethodName, shortVV);
 253                 doEarly(tr, origMethodName, voidVV);
 254                 doEarly(tr, origMethodName, objectVV);
 255                 doEarly(tr, origMethodName, objectArrayVV);
 256 
 257             } else if ("objectArrayf".equals(methodName)) {
 258                 doEarly(tr, origMethodName, booleanVV);
 259                 doEarly(tr, origMethodName, shortVV);
 260                 doEarly(tr, origMethodName, voidVV);
 261                 doEarly(tr, origMethodName, objectVV);
 262                 doEarly(tr, origMethodName, intArrayVV);
 263 
 264             } else if ("objectf".equals(methodName)) {
 265                 doEarly(tr, origMethodName, booleanVV);
 266                 doEarly(tr, origMethodName, shortVV);
 267                 doEarly(tr, origMethodName, voidVV);
 268 
 269              } else if ("voidf".equals(methodName)) {
 270                 doEarly(tr, origMethodName, booleanVV);
 271                 doEarly(tr, origMethodName, shortVV);
 272                 doEarly(tr, origMethodName, objectVV);
 273                 doEarly(tr, origMethodName, intArrayVV);
 274                 doEarly(tr, origMethodName, objectArrayVV);
 275 
 276             } else {
 277                 // just ignore others
 278                 System.out.println("Ignoring: " + methodName);
 279                 return;
 280             }
 281         } else {
 282             System.out.println("Cannot force early return for method: " + origMethodName);
 283         }
 284     }
 285 
 286     protected void runTests() throws Exception {
 287         /*
 288          * Get to the top of main()
 289          * to determine targetClass and mainThread
 290          */
 291 
 292         BreakpointEvent bpe = startToMain("EarlyReturnNegativeTarg");
 293         targetClass = (ClassType)bpe.location().declaringType();
 294         mainThread = bpe.thread();
 295 
 296         /*
 297          * We set and enable breakpoints on all of the interesting
 298          * methods called by doit().  In the breakpointReached()
 299          * handler we force an early return with a different return
 300          * value.
 301          *
 302          */
 303 
 304         setBreakpoint("EarlyReturnNegativeTarg", "i_bytef", "()B");
 305         setBreakpoint("EarlyReturnNegativeTarg", "i_charf", "()C");
 306         setBreakpoint("EarlyReturnNegativeTarg", "i_doublef", "()D");
 307         setBreakpoint("EarlyReturnNegativeTarg", "i_floatf", "()F");
 308         setBreakpoint("EarlyReturnNegativeTarg", "i_intf", "()I");
 309         setBreakpoint("EarlyReturnNegativeTarg", "i_longf", "()J");
 310         setBreakpoint("EarlyReturnNegativeTarg", "i_shortf", "()S");
 311         setBreakpoint("EarlyReturnNegativeTarg", "i_booleanf", "()Z");
 312         setBreakpoint("EarlyReturnNegativeTarg", "i_stringf", "()Ljava/lang/String;");
 313         setBreakpoint("EarlyReturnNegativeTarg", "i_intArrayf", "()[I");
 314         setBreakpoint("EarlyReturnNegativeTarg", "i_objectArrayf", "()[Ljava/lang/Object;");
 315         setBreakpoint("EarlyReturnNegativeTarg", "i_classf", "()Ljava/lang/Class;");
 316         setBreakpoint("EarlyReturnNegativeTarg", "i_classLoaderf", "()Ljava/lang/ClassLoader;");
 317         setBreakpoint("EarlyReturnNegativeTarg", "i_threadf", "()Ljava/lang/Thread;");
 318         setBreakpoint("EarlyReturnNegativeTarg", "i_threadGroupf", "()Ljava/lang/ThreadGroup;");
 319         setBreakpoint("EarlyReturnNegativeTarg", "i_nullObjectf", "()Ljava/lang/Object;");
 320         setBreakpoint("EarlyReturnNegativeTarg", "i_objectf", "()Ljava/lang/Object;");
 321         setBreakpoint("EarlyReturnNegativeTarg", "i_voidf", "()V");
 322 
 323         /* Create Value objects to be passed in forceEarlyReturn calls */
 324         Field theValueField = targetClass.fieldByName("byteValue");
 325         byteVV = (ByteValue)targetClass.getValue(theValueField);
 326 
 327         theValueField = targetClass.fieldByName("charValue");
 328         charVV = (CharValue)targetClass.getValue(theValueField);
 329 
 330         theValueField = targetClass.fieldByName("doubleValue");
 331         doubleVV = (DoubleValue)targetClass.getValue(theValueField);
 332 
 333         theValueField = targetClass.fieldByName("floatValue");
 334         floatVV = (FloatValue)targetClass.getValue(theValueField);
 335 
 336         theValueField = targetClass.fieldByName("intValue");
 337         integerVV = (IntegerValue)targetClass.getValue(theValueField);
 338 
 339         theValueField = targetClass.fieldByName("longValue");
 340         longVV = (LongValue)targetClass.getValue(theValueField);
 341 
 342         theValueField = targetClass.fieldByName("shortValue");
 343         shortVV = (ShortValue)targetClass.getValue(theValueField);
 344 
 345         theValueField = targetClass.fieldByName("booleanValue");
 346         booleanVV = (BooleanValue)targetClass.getValue(theValueField);
 347 
 348         theValueField = targetClass.fieldByName("objectValue");
 349         objectVV = (ObjectReference)targetClass.getValue(theValueField);
 350 
 351         theValueField = targetClass.fieldByName("intArrayValue");
 352         intArrayVV = (ArrayReference)targetClass.getValue(theValueField);
 353 
 354         theValueField = targetClass.fieldByName("objectArrayValue");
 355         objectArrayVV = (ArrayReference)targetClass.getValue(theValueField);
 356 
 357         voidVV = vm().mirrorOfVoid();
 358 
 359         /* Here we go.  This adds 'this' as a listener so
 360          * that our handlers above will be called.
 361          */
 362         listenUntilVMDisconnect();
 363 
 364         if (!testFailed) {
 365             System.out.println();
 366             System.out.println("EarlyReturnNegativeTest: passed");
 367         } else {
 368             System.out.println();
 369             System.out.println("EarlyReturnNegativeTest: failed");
 370             throw new Exception("EarlyReturnNegativeTest: failed");
 371         }
 372     }
 373 }