--- old/hotspot/src/share/vm/prims/jvm.h 2016-09-13 13:17:21.000000000 -0700 +++ new/hotspot/src/share/vm/prims/jvm.h 2016-09-13 13:17:21.000000000 -0700 @@ -196,7 +196,8 @@ * java.lang.StackWalker */ enum { - JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2, + JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x02, + JVM_STACKWALK_GET_CALLER_CLASS = 0x04, JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20, JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100 }; --- old/hotspot/src/share/vm/prims/stackwalk.cpp 2016-09-13 13:17:23.000000000 -0700 +++ new/hotspot/src/share/vm/prims/stackwalk.cpp 2016-09-13 13:17:22.000000000 -0700 @@ -113,7 +113,10 @@ int bci = stream.bci(); if (method == NULL) continue; - if (!ShowHiddenFrames && StackWalk::skip_hidden_frames(mode)) { + + // skip hidden frames for default StackWalker option (i.e. SHOW_HIDDEN_FRAMES + // not set) and when StackWalker::getCallerClass is called + if (!ShowHiddenFrames && (skip_hidden_frames(mode) || get_caller_class(mode))) { if (method->is_hidden()) { if (TraceStackWalk) { tty->print(" hidden method: "); method->print_short_name(); @@ -139,7 +142,13 @@ Handle stackFrame(frames_array->obj_at(index)); fill_stackframe(stackFrame, method, bci); } else { - assert (use_frames_array(mode) == false, "Bad mode for get caller class"); + assert (get_caller_class(mode) == true, "Bad mode for get caller class"); + if (index == start_index && method->caller_sensitive()) { + THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), + err_msg("StackWalker::getCallerClass called from @CallerSensitive %s method", + method->name_and_sig_as_C_string())); + } + frames_array->obj_at_put(index, method->method_holder()->java_mirror()); } if (++frames_decoded >= max_nframes) break; --- old/hotspot/src/share/vm/prims/stackwalk.hpp 2016-09-13 13:17:24.000000000 -0700 +++ new/hotspot/src/share/vm/prims/stackwalk.hpp 2016-09-13 13:17:24.000000000 -0700 @@ -82,6 +82,9 @@ static void fill_live_stackframe(Handle stackFrame, const methodHandle& method, int bci, javaVFrame* jvf, TRAPS); + static inline bool get_caller_class(int mode) { + return (mode & JVM_STACKWALK_GET_CALLER_CLASS) != 0; + } static inline bool skip_hidden_frames(int mode) { return (mode & JVM_STACKWALK_SHOW_HIDDEN_FRAMES) == 0; } --- old/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java 2016-09-13 13:17:27.000000000 -0700 +++ new/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java 2016-09-13 13:17:26.000000000 -0700 @@ -71,6 +71,7 @@ // These flags must match the values maintained in the VM @Native private static final int DEFAULT_MODE = 0x0; @Native private static final int FILL_CLASS_REFS_ONLY = 0x2; + @Native private static final int GET_CALLER_CLASS = 0x4; @Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM @Native private static final int FILL_LIVE_STACK_FRAMES = 0x100; /* @@ -614,9 +615,7 @@ private Class caller; CallerClassFinder(StackWalker walker) { - super(walker, FILL_CLASS_REFS_ONLY); - assert (mode & FILL_CLASS_REFS_ONLY) == FILL_CLASS_REFS_ONLY - : "mode should contain FILL_CLASS_REFS_ONLY"; + super(walker, FILL_CLASS_REFS_ONLY|GET_CALLER_CLASS); } final class ClassBuffer extends FrameBuffer> { --- old/jdk/src/java.base/share/native/include/jvm.h 2016-09-13 13:17:29.000000000 -0700 +++ new/jdk/src/java.base/share/native/include/jvm.h 2016-09-13 13:17:28.000000000 -0700 @@ -179,6 +179,7 @@ */ enum { JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2, + JVM_STACKWALK_GET_CALLER_CLASS = 0x04, JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20, JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100 }; --- /dev/null 2016-09-13 13:17:32.000000000 -0700 +++ new/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/Main.java 2016-09-13 13:17:31.000000000 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8157464 + * @summary Basic test for StackWalker.getCallerClass() + * @library src + * @build java.base/java.util.CSM csm/* + * @run main/othervm csm/jdk.test.CallerSensitiveTest + * @run main/othervm csm/jdk.test.CallerSensitiveTest sm + */ +public class Main { +} + --- /dev/null 2016-09-13 13:17:34.000000000 -0700 +++ new/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/csm/jdk/test/CallerSensitiveTest.java 2016-09-13 13:17:33.000000000 -0700 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.CSM.Result; +import java.util.function.Supplier; + +/** + * This test invokes StackWalker::getCallerClass via static reference, + * reflection, MethodHandle, lambda. Also verify that + * StackWalker::getCallerClass can't be called from @CallerSensitive method. + */ +public class CallerSensitiveTest { + private static final String NON_CSM_CALLER_METHOD = "getCallerClass"; + private static final String CSM_CALLER_METHOD = "caller"; + + public static void main(String... args) throws Throwable { + boolean sm = false; + if (args.length > 0 && args[0].equals("sm")) { + sm = true; + PermissionCollection perms = new Permissions(); + perms.add(new StackFramePermission("retainClassReference")); + Policy.setPolicy(new Policy() { + @Override + public boolean implies(ProtectionDomain domain, Permission p) { + return perms.implies(p); + } + }); + System.setSecurityManager(new SecurityManager()); + } + + System.err.format("Test %s security manager.%n", + sm ? "with" : "without"); + + CallerSensitiveTest cstest = new CallerSensitiveTest(); + // test static call to java.util.CSM::caller and CSM::getCallerClass + cstest.staticMethodCall(); + // test java.lang.reflect.Method call + cstest.reflectMethodCall(); + // test java.lang.invoke.MethodHandle + cstest.invokeMethodHandle(Lookup1.lookup); + cstest.invokeMethodHandle(Lookup2.lookup); + // test method ref + cstest.lambda(); + + LambdaTest.lambda(); + + + if (failed > 0) { + throw new RuntimeException(failed + " test cases failed."); + } + } + + void staticMethodCall() { + java.util.CSM.caller(); + + Result result = java.util.CSM.getCallerClass(); + checkNonCSMCaller(CallerSensitiveTest.class, result); + } + + void reflectMethodCall() throws Throwable { + Method method1 = java.util.CSM.class.getMethod(CSM_CALLER_METHOD); + method1.invoke(null); + + Method method2 = java.util.CSM.class.getMethod(NON_CSM_CALLER_METHOD); + Result result = (Result) method2.invoke(null); + checkNonCSMCaller(CallerSensitiveTest.class, result); + } + + void invokeMethodHandle(Lookup lookup) throws Throwable { + MethodHandle mh1 = lookup.findStatic(java.util.CSM.class, CSM_CALLER_METHOD, + MethodType.methodType(Class.class)); + Class c = (Class)mh1.invokeExact(); + + MethodHandle mh2 = lookup.findStatic(java.util.CSM.class, NON_CSM_CALLER_METHOD, + MethodType.methodType(Result.class)); + Result result = (Result)mh2.invokeExact(); + checkNonCSMCaller(CallerSensitiveTest.class, result); + } + + void lambda() { + Result result = LambdaTest.getCallerClass.get(); + checkNonCSMCaller(CallerSensitiveTest.class, result); + + LambdaTest.caller.get(); + } + + static int failed = 0; + + static void checkNonCSMCaller(Class expected, Result result) { + if (result.callers.size() != 1) { + throw new RuntimeException("Expected result.callers contain one element"); + } + if (expected != result.callers.get(0)) { + System.err.format("ERROR: Expected %s but got %s%n", expected, + result.callers); + result.frames.stream() + .forEach(f -> System.err.println(" " + f)); + failed++; + } + } + + static class Lookup1 { + static Lookup lookup = MethodHandles.lookup(); + } + + static class Lookup2 { + static Lookup lookup = MethodHandles.lookup(); + } + + static class LambdaTest { + static Supplier> caller = java.util.CSM::caller; + static Supplier getCallerClass = java.util.CSM::getCallerClass; + + static void caller() { + caller.get(); + } + static Result getCallerClass() { + return getCallerClass.get(); + } + + static void lambda() { + Result result = LambdaTest.getCallerClass(); + checkNonCSMCaller(LambdaTest.class, result); + + LambdaTest.caller(); + } + } +} --- /dev/null 2016-09-13 13:17:36.000000000 -0700 +++ new/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/csm/module-info.java 2016-09-13 13:17:35.000000000 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module csm { + exports jdk.test; +} --- /dev/null 2016-09-13 13:17:38.000000000 -0700 +++ new/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/src/java.base/java/util/CSM.java 2016-09-13 13:17:37.000000000 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +import static java.lang.StackWalker.Option.*; +import java.lang.StackWalker.StackFrame; +import java.util.stream.Collectors; + +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; + +public class CSM { + private static StackWalker walker = + StackWalker.getInstance(EnumSet.of(RETAIN_CLASS_REFERENCE, + SHOW_HIDDEN_FRAMES, + SHOW_REFLECT_FRAMES)); + + public static class Result { + public final List> callers; + public final List frames; + Result(List> callers, + List frames) { + this.callers = callers; + this.frames = frames; + } + } + + /** + * Returns the caller of this caller-sensitive method returned by + * by Reflection::getCallerClass. + * + * StackWalker::getCallerClass is expected to throw UOE + */ + @CallerSensitive + public static Class caller() { + Class c1 = Reflection.getCallerClass(); + + try { + Class c2 = walker.getCallerClass(); + throw new RuntimeException("Exception not thrown by StackWalker::getCallerClass"); + } catch (UnsupportedOperationException e) {} + return c1; + } + + /** + * Returns the caller of this non-caller-sensitive method. + */ + public static Result getCallerClass() { + Class caller = walker.getCallerClass(); + return new Result(List.of(caller), dump()); + } + + static List dump() { + return walker.walk(s -> s.collect(Collectors.toList())); + } +}