1 /*
   2  * Copyright (c) 2004, 2015, 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 4195445 6204179
  27  *  @summary JDWP, JDI: Add return value to Method Exit Event
  28  *  @author Jim Holmlund
  29  *
  30  *  @modules jdk.jdi
  31  *  @run build TestScaffold VMConnection TargetListener TargetAdapter
  32  *  @run compile -g MethodExitReturnValuesTest.java
  33  *  @run driver MethodExitReturnValuesTest
  34  */
  35 import com.sun.jdi.*;
  36 import com.sun.jdi.event.*;
  37 import com.sun.jdi.request.*;
  38 import java.util.*;
  39 import java.net.URLClassLoader;
  40 import java.net.URL;
  41 import java.lang.reflect.Array;
  42 
  43 /*
  44  * This test has a debuggee which calls a static method
  45  * for each kind of JDI Value, and then an instance method
  46  * for each.
  47  * The debugger turns on MethodExitEvents and checks
  48  * that the right return values are included in the MethodExitEvents.
  49  * Each value is stored in a static var in the debuggee.  The debugger
  50  * gets the values from these static vars to check for correct
  51  * return values in the MethodExitEvents.
  52  */
  53 
  54 class MethodExitReturnValuesTarg {
  55     // These are the values that will be returned by the methods
  56     static URL[] urls = new URL[1];
  57     public static byte      byteValue = 89;
  58     public static char      charValue = 'x';
  59     public static double    doubleValue = 2.2;
  60     public static float     floatValue = 3.3f;
  61     public static int       intValue = 1;
  62     public static long      longValue = Long.MAX_VALUE;
  63     public static short     shortValue = 8;
  64     public static boolean   booleanValue = false;
  65 
  66     public static Class       classValue = Object.class;
  67     public static ClassLoader classLoaderValue;
  68     {
  69         try {
  70             urls[0] = new URL("hi there");
  71         } catch (java.net.MalformedURLException ee) {
  72         }
  73         classLoaderValue = new URLClassLoader(urls);
  74     }
  75 
  76     public static Thread      threadValue;
  77     public static ThreadGroup threadGroupValue;
  78     public static String      stringValue = "abc";
  79     public static int[]       intArrayValue = new int[] {1, 2, 3};
  80 
  81     public static MethodExitReturnValuesTarg  objectValue =
  82         new MethodExitReturnValuesTarg();
  83     public String ivar = stringValue;
  84 
  85     // arrays to be used in calls to native methods Array.get...(....)
  86     public static byte[]    arrByte      = new byte[]    {byteValue};
  87     public static char[]    arrChar      = new char[]    {charValue};
  88     public static double[]  arrDouble    = new double[]  {doubleValue};
  89     public static float[]   arrFloat     = new float[]   {floatValue};
  90     public static int[]     arrInt       = new int[]     {intValue};
  91     public static long[]    arrLong      = new long[]    {longValue};
  92     public static short[]   arrShort     = new short[]   {shortValue};
  93     public static boolean[] arrBoolean   = new boolean[] {booleanValue};
  94     public static Object[]  arrObject    = new Object[]  {objectValue};
  95 
  96     // Used to show which set of tests follows
  97     public static String s_show(String p1) { return p1;}
  98 
  99     // These are the static methods
 100     public static byte s_bytef()      { return byteValue; }
 101     public static char s_charf()      { return charValue; }
 102     public static double s_doublef()  { return doubleValue; }
 103     public static float s_floatf()    { return floatValue; }
 104     public static int s_intf()        { return intValue; }
 105     public static long s_longf()      { return longValue; }
 106     public static short s_shortf()    { return shortValue; }
 107     public static boolean s_booleanf(){ return booleanValue; }
 108     public static String s_stringf()  { return stringValue; }
 109     public static Class s_classf()    { return classValue; }
 110     public static ClassLoader s_classLoaderf()
 111                                       { return classLoaderValue; }
 112     public static Thread s_threadf()  { threadValue = Thread.currentThread();
 113                                         return threadValue; }
 114     public static ThreadGroup s_threadGroupf()
 115                                       { threadGroupValue = threadValue.getThreadGroup();
 116                                         return threadGroupValue; }
 117     public static int[] s_intArrayf() { return intArrayValue; }
 118     public static Object s_nullObjectf() { return null; }
 119     public static Object s_objectf()  { return objectValue; }
 120     public static void s_voidf()      {}
 121 
 122     // These are the instance methods
 123     public byte i_bytef()            { return byteValue; }
 124     public char i_charf()            { return charValue; }
 125     public double i_doublef()        { return doubleValue; }
 126     public float i_floatf()          { return floatValue; }
 127     public int i_intf()              { return intValue; }
 128     public long i_longf()            { return longValue; }
 129     public short i_shortf()          { return shortValue; }
 130     public boolean i_booleanf()      { return booleanValue; }
 131     public String i_stringf()        { return stringValue; }
 132     public Class i_classf()          { return classValue; }
 133     public ClassLoader i_classLoaderf()
 134                                      { return classLoaderValue; }
 135     public Thread i_threadf()        { return threadValue; }
 136     public ThreadGroup i_threadGroupf()
 137                                      { return threadGroupValue; }
 138     public int[] i_intArrayf()       { return intArrayValue; }
 139     public Object i_nullObjectf()    { return null; }
 140     public Object i_objectf()        { return objectValue; }
 141     public void i_voidf()            {}
 142 
 143     static void doit(MethodExitReturnValuesTarg xx) {
 144         s_show("==========  Testing static methods ================");
 145         s_bytef();
 146         s_charf();
 147         s_doublef();
 148         s_floatf();
 149         s_intf();
 150         s_longf();
 151         s_shortf();
 152         s_booleanf();
 153 
 154         s_stringf();
 155         s_classf();
 156         s_classLoaderf();
 157         s_threadf();
 158         s_threadGroupf();
 159         s_intArrayf();
 160         s_nullObjectf();
 161         s_objectf();
 162         s_voidf();
 163 
 164         s_show("==========  Testing instance methods ================");
 165         xx.i_bytef();
 166         xx.i_charf();
 167         xx.i_doublef();
 168         xx.i_floatf();
 169         xx.i_intf();
 170         xx.i_longf();
 171         xx.i_shortf();
 172         xx.i_booleanf();
 173         xx.i_stringf();
 174         xx.i_intArrayf();
 175         xx.i_classf();
 176         xx.i_classLoaderf();
 177         xx.i_threadf();
 178         xx.i_threadGroupf();
 179         xx.i_nullObjectf();
 180         xx.i_objectf();
 181         xx.i_voidf();
 182 
 183         // Prove it works for native methods too
 184         s_show("==========  Testing native methods ================");
 185         StrictMath.sin(doubleValue);
 186         Array.getByte(arrByte, 0);
 187         Array.getChar(arrChar, 0);
 188         Array.getDouble(arrDouble, 0);
 189         Array.getFloat(arrFloat, 0);
 190         Array.getInt(arrInt, 0);
 191         Array.getLong(arrLong, 0);
 192         Array.getShort(arrShort, 0);
 193         Array.getBoolean(arrBoolean, 0);
 194         Array.get(arrObject, 0);
 195         stringValue.intern();
 196     }
 197 
 198     public static void main(String[] args) {
 199         // The debugger will stop at the start of main,
 200         // enable method exit events, and then do
 201         // a resume.
 202 
 203         MethodExitReturnValuesTarg xx =
 204             new MethodExitReturnValuesTarg();
 205 
 206         doit(xx);
 207     }
 208 }
 209 
 210 
 211 
 212 public class MethodExitReturnValuesTest extends TestScaffold {
 213     // Classes which we are interested in
 214     private List includes = Arrays.asList(new String[] {
 215         "MethodExitReturnValuesTarg",
 216         "java.lang.reflect.Array",
 217         "java.lang.StrictMath",
 218         "java.lang.String"
 219     });
 220 
 221     static VirtualMachineManager vmm ;
 222     ClassType targetClass;
 223     Field theValueField;
 224     static int successes = 0;
 225     static final int expectedSuccesses = 44;  // determined by inspection :-)
 226 
 227     MethodExitReturnValuesTest(String args[]) {
 228         super(args);
 229     }
 230 
 231     public static void main(String[] args)      throws Exception {
 232         MethodExitReturnValuesTest meee = new MethodExitReturnValuesTest(args);
 233         vmm = Bootstrap.virtualMachineManager();
 234         meee.startTests();
 235     }
 236 
 237     // These are the methods that check for correct return values.
 238 
 239     void ckByteValue(Value retValue) {
 240         Field theValueField = targetClass.fieldByName("byteValue");
 241         ByteValue theValue = (ByteValue)targetClass.getValue(theValueField);
 242 
 243         byte vv = theValue.value();
 244         byte rv = ((ByteValue)retValue).value();
 245         if (vv != rv) {
 246             failure("failure: byte: expected " + vv + ", got " + rv);
 247         } else {
 248             System.out.println("Passed: byte " + rv);
 249             successes++;
 250         }
 251     }
 252 
 253     void ckCharValue(Value retValue) {
 254         Field theValueField = targetClass.fieldByName("charValue");
 255         CharValue theValue = (CharValue)targetClass.getValue(theValueField);
 256 
 257         char vv = theValue.value();
 258         char rv = ((CharValue)retValue).value();
 259         if (vv != rv) {
 260             failure("failure: char: expected " + vv + ", got " + rv);
 261         } else {
 262             System.out.println("Passed: char " + rv);
 263             successes++;
 264         }
 265     }
 266 
 267     void ckDoubleValue(Value retValue) {
 268         Field theValueField = targetClass.fieldByName("doubleValue");
 269         DoubleValue theValue = (DoubleValue)targetClass.getValue(theValueField);
 270 
 271         double vv = theValue.value();
 272         double rv = ((DoubleValue)retValue).value();
 273         if (vv != rv) {
 274             failure("failure: double: expected " + vv + ", got " + rv);
 275         } else {
 276             System.out.println("Passed: double " + rv);
 277             successes++;
 278         }
 279     }
 280 
 281     void ckFloatValue(Value retValue) {
 282         Field theValueField = targetClass.fieldByName("floatValue");
 283         FloatValue theValue = (FloatValue)targetClass.getValue(theValueField);
 284 
 285         float vv = theValue.value();
 286         float rv = ((FloatValue)retValue).value();
 287         if (vv != rv) {
 288             failure("failure: float: expected " + vv + ", got " + rv);
 289         } else {
 290             System.out.println("Passed: float " + rv);
 291             successes++;
 292         }
 293     }
 294 
 295     void ckIntValue(Value retValue) {
 296         Field theValueField = targetClass.fieldByName("intValue");
 297         IntegerValue theValue = (IntegerValue)targetClass.getValue(theValueField);
 298 
 299         int vv = theValue.value();
 300         int rv = ((IntegerValue)retValue).value();
 301         if (vv != rv) {
 302             failure("failure: int: expected " + vv + ", got " + rv);
 303         } else {
 304             System.out.println("Passed: int " + rv);
 305             successes++;
 306         }
 307     }
 308 
 309     void ckLongValue(Value retValue) {
 310         Field theValueField = targetClass.fieldByName("longValue");
 311         LongValue theValue = (LongValue)targetClass.getValue(theValueField);
 312 
 313         long vv = theValue.value();
 314         long rv = ((LongValue)retValue).value();
 315         if (vv != rv) {
 316             failure("failure: long: expected " + vv + ", got " + rv);
 317         } else {
 318             System.out.println("Passed: long " + rv);
 319             successes++;
 320         }
 321     }
 322 
 323     void ckShortValue(Value retValue) {
 324         Field theValueField = targetClass.fieldByName("shortValue");
 325         ShortValue theValue = (ShortValue)targetClass.getValue(theValueField);
 326 
 327         short vv = theValue.value();
 328         short rv = ((ShortValue)retValue).value();
 329         if (vv != rv) {
 330             failure("failure: short: expected " + vv + ", got " + rv);
 331         } else {
 332             System.out.println("Passed: short " + rv);
 333             successes++;
 334         }
 335     }
 336 
 337     void ckBooleanValue(Value retValue) {
 338         Field theValueField = targetClass.fieldByName("booleanValue");
 339         BooleanValue theValue = (BooleanValue)targetClass.getValue(theValueField);
 340 
 341         boolean vv = theValue.value();
 342         boolean rv = ((BooleanValue)retValue).value();
 343         if (vv != rv) {
 344             failure("failure: boolean: expected " + vv + ", got " + rv);
 345         } else {
 346             System.out.println("Passed: boolean " + rv);
 347             successes++;
 348         }
 349     }
 350 
 351     void ckStringValue(Value retValue) {
 352         Field theValueField = targetClass.fieldByName("stringValue");
 353         StringReference theValue = (StringReference)targetClass.getValue(theValueField);
 354 
 355         String vv = theValue.value();
 356         String rv = ((StringReference)retValue).value();
 357         if (vv != rv) {
 358             failure("failure: String: expected " + vv + ", got " + rv);
 359         } else {
 360             System.out.println("Passed: String: " + rv);
 361             successes++;
 362         }
 363     }
 364 
 365     void ckClassValue(Value retValue) {
 366         Field theValueField = targetClass.fieldByName("classValue");
 367         ClassObjectReference vv = (ClassObjectReference)targetClass.
 368             getValue(theValueField);
 369 
 370         ClassObjectReference rv = (ClassObjectReference)retValue;
 371         if (vv != rv) {
 372             failure("failure: Class: expected " + vv + ", got " + rv);
 373         } else {
 374             System.out.println("Passed: Class: " + rv);
 375             successes++;
 376         }
 377     }
 378 
 379     void ckClassLoaderValue(Value retValue) {
 380         Field theValueField = targetClass.fieldByName("classLoaderValue");
 381         ClassLoaderReference vv = (ClassLoaderReference)targetClass.
 382             getValue(theValueField);
 383 
 384         ClassLoaderReference rv = (ClassLoaderReference)retValue;
 385         if (vv != rv) {
 386             failure("failure: ClassLoader: expected " + vv + ", got " + rv);
 387         } else {
 388             System.out.println("Passed: ClassLoader: " + rv);
 389             successes++;
 390         }
 391     }
 392 
 393     void ckThreadValue(Value retValue) {
 394         Field theValueField = targetClass.fieldByName("threadValue");
 395         ThreadReference vv = (ThreadReference)targetClass.
 396             getValue(theValueField);
 397 
 398         ThreadReference rv = (ThreadReference)retValue;
 399         if (vv != rv) {
 400             failure("failure: Thread: expected " + vv + ", got " + rv);
 401         } else {
 402             System.out.println("Passed: Thread: " + rv);
 403             successes++;
 404         }
 405     }
 406 
 407     void ckThreadGroupValue(Value retValue) {
 408         Field theValueField = targetClass.fieldByName("threadGroupValue");
 409         ThreadGroupReference vv = (ThreadGroupReference)targetClass.
 410             getValue(theValueField);
 411 
 412         ThreadGroupReference rv = (ThreadGroupReference)retValue;
 413         if (vv != rv) {
 414             failure("failure: ThreadgGroup: expected " + vv + ", got " + rv);
 415         } else {
 416             System.out.println("Passed: ThreadGroup: " + rv);
 417             successes++;
 418         }
 419     }
 420 
 421     void ckArrayValue(Value retValue) {
 422         Field theValueField = targetClass.fieldByName("intArrayValue");
 423         ArrayReference theValue = (ArrayReference)targetClass.getValue(theValueField);
 424         IntegerValue theElem2 = (IntegerValue)theValue.getValue(2);
 425 
 426         ArrayReference theRetValue = (ArrayReference)retValue;
 427         IntegerValue retElem2 = (IntegerValue)theRetValue.getValue(2);
 428         int vv = theElem2.value();
 429         int rv = retElem2.value();
 430         if (vv != rv) {
 431             failure("failure: in[2]: expected " + vv + ", got " + rv);
 432         } else {
 433             System.out.println("Passed: int[2]: " + rv);
 434             successes++;
 435         }
 436     }
 437 
 438     void ckNullObjectValue(Value retValue) {
 439         if (retValue != null) {
 440             failure("failure: NullObject: expected " + null + ", got " + retValue);
 441         } else {
 442             System.out.println("Passed: NullObject: " + retValue);
 443             successes++;
 444         }
 445     }
 446 
 447     void ckObjectValue(Value retValue) {
 448         // We will check the ivar field which we know contains
 449         // the value of 'stringValue'
 450         Field theValueField = targetClass.fieldByName("stringValue");
 451         StringReference theValue = (StringReference)targetClass.getValue(theValueField);
 452 
 453         Field theIVarField = targetClass.fieldByName("ivar");
 454         ObjectReference theRetValue = (ObjectReference)retValue;
 455         StringReference theRetValField = (StringReference)theRetValue.getValue(theIVarField);
 456 
 457         String vv = theValue.value();
 458         String rv = theRetValField.value();
 459         if (vv != rv) {
 460             failure("failure: Object: expected " + vv + ", got " + rv);
 461        } else {
 462             System.out.println("Passed: Object: " + rv);
 463             successes++;
 464         }
 465     }
 466 
 467     void ckVoidValue(Value retValue) {
 468         System.out.println("Passed: Void");
 469         successes++;
 470     }
 471 
 472     void ckSinValue(Value retValue) {
 473         double rv = ((DoubleValue)retValue).value();
 474         double vv = StrictMath.sin(MethodExitReturnValuesTarg.doubleValue);
 475         if (rv != vv) {
 476             failure("failure: sin: expected " + vv + ", got " + rv);
 477         } else {
 478             System.out.println("Passed: sin " + rv);
 479             successes++;
 480         }
 481     }
 482 
 483     // This is the MethodExitEvent handler.
 484     public void methodExited(MethodExitEvent event) {
 485         String origMethodName = event.method().name();
 486 
 487         if (!includes.contains(event.method().declaringType().name())) {
 488             return;
 489         }
 490 
 491         if (vmm.majorInterfaceVersion() >= 1 &&
 492             vmm.minorInterfaceVersion() >= 6 &&
 493             vm().canGetMethodReturnValues()) {
 494             Value retValue = event.returnValue();
 495 
 496             if ("sin".equals(origMethodName)) {
 497                 ckSinValue(retValue);
 498                 return;
 499             }
 500 
 501             if (!origMethodName.startsWith("s_") &&
 502                 !origMethodName.startsWith("i_")) {
 503                 // Check native methods
 504                 if ("getByte".equals(origMethodName))         ckByteValue(retValue);
 505                 else if ("getChar".equals(origMethodName))    ckCharValue(retValue);
 506                 else if ("getDouble".equals(origMethodName))  ckDoubleValue(retValue);
 507                 else if ("getFloat".equals(origMethodName))   ckFloatValue(retValue);
 508                 else if ("getInt".equals(origMethodName))     ckIntValue(retValue);
 509                 else if ("getLong".equals(origMethodName))    ckLongValue(retValue);
 510                 else if ("getShort".equals(origMethodName))   ckShortValue(retValue);
 511                 else if ("getBoolean".equals(origMethodName)) ckBooleanValue(retValue);
 512                 else if ("getObject".equals(origMethodName))  ckObjectValue(retValue);
 513                 else if ("intern".equals(origMethodName))     ckStringValue(retValue);
 514                 return;
 515             }
 516 
 517             String methodName = origMethodName.substring(2);
 518             if ("show".equals(methodName)) {
 519                 System.out.println(retValue);
 520                 return;
 521             }
 522 
 523             if ("bytef".equals(methodName))             ckByteValue(retValue);
 524             else if ("charf".equals(methodName))        ckCharValue(retValue);
 525             else if ("doublef".equals(methodName))      ckDoubleValue(retValue);
 526             else if ("floatf".equals(methodName))       ckFloatValue(retValue);
 527             else if ("intf".equals(methodName))         ckIntValue(retValue);
 528             else if ("longf".equals(methodName))        ckLongValue(retValue);
 529             else if ("shortf".equals(methodName))       ckShortValue(retValue);
 530             else if ("booleanf".equals(methodName))     ckBooleanValue(retValue);
 531             else if ("stringf".equals(methodName))      ckStringValue(retValue);
 532             else if ("classf".equals(methodName))       ckClassValue(retValue);
 533             else if ("classLoaderf".equals(methodName)) ckClassLoaderValue(retValue);
 534             else if ("threadf".equals(methodName))      ckThreadValue(retValue);
 535             else if ("threadGroupf".equals(methodName)) ckThreadGroupValue(retValue);
 536             else if ("intArrayf".equals(methodName))    ckArrayValue(retValue);
 537             else if ("nullObjectf".equals(methodName))  ckNullObjectValue(retValue);
 538             else if ("objectf".equals(methodName))      ckObjectValue(retValue);
 539             else if ("voidf".equals(methodName))        ckVoidValue(retValue);
 540             else {
 541                 failure("failure: Unknown methodName: " + origMethodName);
 542             }
 543         } else {
 544             System.out.println("Return Value not available for method: " + origMethodName);
 545         }
 546     }
 547 
 548     protected void runTests() throws Exception {
 549         /*
 550          * Get to the top of main()
 551          * to determine targetClass and mainThread
 552          */
 553         BreakpointEvent bpe = startToMain("MethodExitReturnValuesTarg");
 554         targetClass = (ClassType)bpe.location().declaringType();
 555         mainThread = bpe.thread();
 556 
 557         theValueField = targetClass.fieldByName("theValue");
 558 
 559         /*
 560          * Ask for method exit events
 561          */
 562         MethodExitRequest exitRequest =
 563             eventRequestManager().createMethodExitRequest();
 564         exitRequest.addThreadFilter(mainThread);
 565 
 566         int sessionSuspendPolicy = EventRequest.SUSPEND_ALL;
 567         //sessionSuspendPolicy = EventRequest.SUSPEND_EVENT_THREAD;
 568         //sessionSuspendPolicy = EventRequest.SUSPEND_NONE;
 569         exitRequest.setSuspendPolicy(sessionSuspendPolicy);
 570         exitRequest.enable();
 571 
 572         /*
 573          * We are now set up to receive the notifications we want.
 574          * Here we go.  This adds 'this' as a listener so
 575          * that our handlers above will be called.
 576          */
 577 
 578         listenUntilVMDisconnect();
 579 
 580         if (successes != expectedSuccesses) {
 581             failure("failure: Expected " + expectedSuccesses + ", but got " + successes);
 582         }
 583         System.out.println("All done, " + successes + " passed");
 584 
 585 
 586         if (!testFailed) {
 587             System.out.println();
 588             System.out.println("MethodExitReturnValuesTest: passed");
 589         } else {
 590             System.out.println();
 591             System.out.println("MethodExitReturnValuesTest: failed");
 592             throw new Exception("MethodExitReturnValuesTest: failed");
 593         }
 594     }
 595 }