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