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