1 /*
   2  * Copyright (c) 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 8140450
  27  * @summary Basic test for StackWalker.getCallerClass()
  28  * @run main/othervm -XX:-MemberNameInStackFrame GetCallerClassTest
  29  * @run main/othervm -XX:+MemberNameInStackFrame GetCallerClassTest
  30  * @run main/othervm GetCallerClassTest sm
  31  */
  32 
  33 import static java.lang.StackWalker.Option.*;
  34 import java.lang.invoke.MethodHandle;
  35 import java.lang.invoke.MethodHandles;
  36 import java.lang.invoke.MethodType;
  37 import java.lang.reflect.InvocationTargetException;
  38 import java.lang.reflect.Method;
  39 import java.security.Permission;
  40 import java.security.PermissionCollection;
  41 import java.security.Permissions;
  42 import java.security.Policy;
  43 import java.security.ProtectionDomain;
  44 import java.util.Arrays;
  45 import java.util.List;
  46 
  47 public class GetCallerClassTest {
  48     private final StackWalker walker;
  49     private final boolean expectUOE;
  50 
  51     public GetCallerClassTest(StackWalker sw, boolean expect) {
  52         this.walker = sw;
  53         this.expectUOE = expect;
  54     }
  55     public static void main(String... args) throws Exception {
  56         if (args.length > 0 && args[0].equals("sm")) {
  57             PermissionCollection perms = new Permissions();
  58             perms.add(new StackFramePermission("retainClassReference"));
  59             Policy.setPolicy(new Policy() {
  60                 @Override
  61                 public boolean implies(ProtectionDomain domain, Permission p) {
  62                     return perms.implies(p);
  63                 }
  64             });
  65             System.setSecurityManager(new SecurityManager());
  66         }
  67         new GetCallerClassTest(StackWalker.getInstance(), true).test();
  68         new GetCallerClassTest(StackWalker.getInstance(RETAIN_CLASS_REFERENCE), false).test();
  69     }
  70 
  71     public void test() {
  72         new TopLevelCaller().run();
  73         new Nested().createNestedCaller().run();
  74         new InnerClassCaller().run();
  75         new ReflectionTest().run();
  76 
  77         List<Thread> threads = Arrays.asList(
  78                 new Thread(new TopLevelCaller()),
  79                 new Thread(new Nested().createNestedCaller()),
  80                 new Thread(new InnerClassCaller()),
  81                 new Thread(new ReflectionTest())
  82         );
  83         threads.stream().forEach(Thread::start);
  84         threads.stream().forEach(t -> {
  85             try {
  86                 t.join();
  87             } catch (InterruptedException e) {
  88                 throw new RuntimeException(e);
  89             }
  90         });
  91     }
  92 
  93     public static void staticGetCallerClass(StackWalker stackWalker,
  94                                             Class<?> expected,
  95                                             boolean expectUOE) {
  96         try {
  97             Class<?> c = stackWalker.getCallerClass();
  98             assertEquals(c, expected);
  99             if (expectUOE) { // Should have thrown
 100                 throw new RuntimeException("Didn't get expected exception");
 101             }
 102         } catch (RuntimeException e) { // also catches UOE
 103             if (expectUOE && causeIsUOE(e)) {
 104                 return; /* expected */
 105             }
 106             System.err.println("Unexpected exception:");
 107             throw e;
 108         }
 109     }
 110 
 111     public static void reflectiveGetCallerClass(StackWalker stackWalker,
 112                                                 Class<?> expected,
 113                                                 boolean expectUOE) {
 114         try {
 115             Method m = StackWalker.class.getMethod("getCallerClass");
 116             Class<?> c = (Class<?>) m.invoke(stackWalker);
 117             assertEquals(c, expected);
 118             if (expectUOE) { // Should have thrown
 119                 throw new RuntimeException("Didn't get expected exception");
 120             }
 121         } catch (Throwable e) {
 122             if (expectUOE && causeIsUOE(e)) {
 123                 return; /* expected */
 124             }
 125             System.err.println("Unexpected exception:");
 126             throw new RuntimeException(e);
 127         }
 128     }
 129 
 130     public static void methodHandleGetCallerClass(StackWalker stackWalker,
 131                                                   Class<?> expected,
 132                                                   boolean expectUOE) {
 133         MethodHandles.Lookup lookup = MethodHandles.lookup();
 134         try {
 135             MethodHandle mh = lookup.findVirtual(StackWalker.class, "getCallerClass",
 136                                                  MethodType.methodType(Class.class));
 137             Class<?> c = (Class<?>) mh.invokeExact(stackWalker);
 138             assertEquals(c, expected);
 139             if (expectUOE) { // Should have thrown
 140                 throw new RuntimeException("Didn't get expected exception");
 141             }
 142         } catch (Throwable e) {
 143             if (expectUOE && causeIsUOE(e)) {
 144                 return; /* expected */
 145             }
 146             System.err.println("Unexpected exception:");
 147             throw new RuntimeException(e);
 148         }
 149     }
 150 
 151     public static void assertEquals(Class<?> c, Class<?> expected) {
 152         if (expected != c) {
 153             throw new RuntimeException(c + " != " + expected);
 154         }
 155     }
 156 
 157     /** Is there an UnsupportedOperationException in there? */
 158     public static boolean causeIsUOE(Throwable t) {
 159         while (t != null) {
 160             if (t instanceof UnsupportedOperationException) {
 161                 return true;
 162             }
 163             t = t.getCause();
 164         }
 165         return false;
 166     }
 167 
 168     class TopLevelCaller implements Runnable {
 169         public void run() {
 170             GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
 171             GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
 172             GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
 173         }
 174     }
 175 
 176     class Nested {
 177         NestedClassCaller createNestedCaller() { return new NestedClassCaller(); }
 178         class NestedClassCaller implements Runnable {
 179             public void run() {
 180                 GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
 181                 GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
 182                 GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
 183             }
 184         }
 185     }
 186 
 187     class InnerClassCaller implements Runnable {
 188         public void run() {
 189             new Inner().test();
 190         }
 191         class Inner {
 192             void test() {
 193                 GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
 194                 GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
 195                 GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
 196             }
 197         }
 198     }
 199 
 200     class ReflectionTest implements Runnable {
 201         final MethodType methodType =
 202             MethodType.methodType(void.class, StackWalker.class, Class.class, boolean.class);
 203 
 204         public void run() {
 205             callMethodHandle();
 206             callMethodHandleRefl();
 207             callMethodInvoke();
 208             callMethodInvokeRefl();
 209         }
 210         void callMethodHandle() {
 211             MethodHandles.Lookup lookup = MethodHandles.publicLookup();
 212             try {
 213                 MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
 214                                                     "staticGetCallerClass",
 215                                                     methodType);
 216                 mh.invokeExact(walker, ReflectionTest.class, expectUOE);
 217             } catch (Throwable e) {
 218                 throw new RuntimeException(e);
 219             }
 220         }
 221         void callMethodHandleRefl() {
 222             MethodHandles.Lookup lookup = MethodHandles.publicLookup();
 223             try {
 224                 MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
 225                                                     "reflectiveGetCallerClass",
 226                                                     methodType);
 227                 mh.invokeExact(walker, ReflectionTest.class, expectUOE);
 228             } catch (Throwable e) {
 229                 throw new RuntimeException(e);
 230             }
 231         }
 232         void callMethodInvoke() {
 233             try {
 234                 Method m = GetCallerClassTest.class.getMethod("staticGetCallerClass",
 235                                StackWalker.class, Class.class, boolean.class);
 236                 m.invoke(null, walker, ReflectionTest.class, expectUOE);
 237             } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
 238                 throw new RuntimeException(e);
 239             }
 240         }
 241         void callMethodInvokeRefl() {
 242             try {
 243                 Method m = GetCallerClassTest.class.getMethod("reflectiveGetCallerClass",
 244                                StackWalker.class, Class.class, boolean.class);
 245                 m.invoke(null, walker, ReflectionTest.class, expectUOE);
 246             } catch (UnsupportedOperationException e) {
 247                 throw e;
 248             } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
 249                 throw new RuntimeException(e);
 250             }
 251         }
 252     }
 253 }