undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,227 @@
/*
* Copyright (c) 2015, 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 8143214
* @summary Verify that StackWalker works well when one instance of StackWalker
* is used by several threads sequentially or concurrently.
* @run testng AcrossThreads
*/
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class AcrossThreads {
static final StackWalker WALKERS[] = new StackWalker[] {
StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
};
@DataProvider
public StackWalker[][] walkerProvider() {
return new StackWalker[][] {
new StackWalker[] { WALKERS[0] },
new StackWalker[] { WALKERS[1] },
new StackWalker[] { WALKERS[2] }
};
}
@Test(dataProvider = "walkerProvider")
public void test(StackWalker walker) {
Thread t1 = new T1(walker);
// call methods of one instance of StackWalker sequentially in T1, T2, T3.
t1.start();
try {
t1.join();
} catch (InterruptedException e) { }
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 100; i++) {
threads.add(new T1(walker));
threads.add(new T2(walker));
threads.add(new T3(walker));
}
// call methods of one instance of StackWalker concurrently in several threads.
threads.parallelStream().forEach(t -> {
t.setDaemon(true);
t.start();
});
threads.parallelStream().forEach(t -> {
try {
t.join();
} catch (InterruptedException e) { }
});
}
interface Consumer {
final int LOOPS = 5;
public void consume();
default public void assertWalker(StackWalker walker, int n) {
if (--n == 0) {
Map<String, Integer> methods = new HashMap<String, Integer>();
walker.forEach(f -> {
Integer i = methods.putIfAbsent(f.getMethodName(), 1);
if (i != null) {
methods.put(f.getMethodName(), i + 1);
}
});
// verify that walker.forEach(...) reaches the specified methods.
assertTrue(methods.get("consume") == 1);
assertTrue(methods.get("run") == 1);
assertTrue(methods.get("assertWalker") == LOOPS);
// verify that walker.walk(...) reaches the specified methods.
assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
.filter(mn -> mn.equals("consume"))
.count()) == 1);
assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
.filter(mn -> mn.equals("run"))
.count()) == 1);
assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
.filter(mn -> mn.equals("assertWalker"))
.count()) == LOOPS);
} else {
assertWalker(walker, n);
}
}
}
class T1 extends Thread implements Consumer {
final StackWalker walker;
public T1(StackWalker walker) {
this.walker = walker;
}
public void run() {
consume();
Thread t2 = new T2(walker);
t2.start();
try {
t2.join();
} catch (InterruptedException e) { }
consume();
}
public void consume() {
assertWalker(walker, LOOPS);
// verify walker.walk() reaches T1 class through methods run() and consume().
assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
.count()) == 2);
assertCallerClass(walker);
assertEquals(T1.class, walker.getCallerClass());
}
}
class T2 extends Thread implements Consumer {
final StackWalker walker;
public T2(StackWalker walker) {
this.walker = walker;
}
public void run() {
consume();
Thread t3 = new T3(walker);
t3.start();
try {
t3.join();
} catch (InterruptedException e) { }
consume();
}
public void consume() {
assertWalker(walker, LOOPS);
// verify walker.walk() reaches T2 class through methods run() and consume().
assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass())
.count()) == 2);
// verify T1 is not reached, even if call is invoked
// from test()->T1.start()->T1.run()->T2
assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
.count()) == 0);
assertCallerClass(walker);
assertEquals(T2.class, walker.getCallerClass());
}
}
class T3 extends Thread implements Consumer {
final StackWalker walker;
public T3(StackWalker walker) {
this.walker = walker;
}
public void run() {
consume();
}
public void consume() {
assertWalker(walker, LOOPS);
// verify walker.walk() reaches T1 class through methods run() and consume().
assertTrue(walker.walk(s -> s.filter(f -> T3.class == f.getDeclaringClass())
.count()) == 2);
// verify T1, T2 is not reached, even if call is invoked
// from test() -> T1.start() -> T1.run() -> T2.start() -> T2.run() -> T3
assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass())
.count()) == 0);
assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
.count()) == 0);
assertCallerClass(walker);
assertEquals(T3.class, walker.getCallerClass());
}
}
static void assertCallerClass(StackWalker walker) {
// verify walker.getCallerClass() get the expected class.
call(walker);
}
static void call(StackWalker walker) {
Class<?> c = walker.getCallerClass();
assertEquals(c, AcrossThreads.class);
}
}

View file

@ -0,0 +1,235 @@
/*
* Copyright (c) 2015, 2020, 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 8140450 8173898
* @summary Basic test for the StackWalker::walk method
* @run testng Basic
*/
import java.lang.StackWalker.StackFrame;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.StackWalker.Option.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class Basic {
private static boolean verbose = false;
@DataProvider(name = "stackDepths")
public static Object[][] stackDepths() {
return new Object[][] {
{ new int[] { 12 }, new int[] { 4, 8, 12} },
{ new int[] { 18 }, new int[] { 8, 16, 20} },
{ new int[] { 32 }, new int[] { 16, 32, 64} },
};
}
/**
* For a stack of a given depth, it creates a StackWalker with an estimate.
* Test walking different number of frames
*/
@Test(dataProvider = "stackDepths")
public static void test(int[] depth, int[] estimates) {
Basic test = new Basic(depth[0]);
for (int estimate : estimates) {
test.walk(estimate);
}
}
@Test
public static void testWalkFromConstructor() throws Exception {
System.out.println("testWalkFromConstructor:");
List<String> found = ((ConstructorNewInstance)ConstructorNewInstance.class.getMethod("create")
.invoke(null)).collectedFrames();
assertEquals(List.of(ConstructorNewInstance.class.getName()+"::<init>",
ConstructorNewInstance.class.getName()+"::create",
Basic.class.getName()+"::testWalkFromConstructor"),
found);
}
@Test
public static void testMethodSignature() throws Exception {
List<StackFrame> frames = new StackBuilder(16, 16).build();
Map<String, MethodType> methodTypes = StackBuilder.methodTypes();
for (StackFrame f : frames) {
MethodType type = methodTypes.get(f.getMethodName());
if (type != null) {
System.out.format("%s.%s %s%n", f.getClassName(), f.getMethodName(),
f.getDescriptor());
String descriptor = f.getDescriptor();
if (!descriptor.equals(type.toMethodDescriptorString())) {
throw new RuntimeException("Expected: " + type.toMethodDescriptorString()
+ " got: " + f.getDescriptor());
}
if (!f.getMethodType().equals(type)) {
throw new RuntimeException("Expected: " + type
+ " got: " + f.getMethodType());
}
// verify descriptor returned by getDescriptor() before and after
// getMethodType() is called
if (!descriptor.equals(f.getDescriptor())) {
throw new RuntimeException("Mismatched: " + descriptor
+ " got: " + f.getDescriptor());
}
}
}
}
private final int depth;
Basic(int depth) {
this.depth = depth;
}
/** For TestNG */
public Basic() {
depth = 0;
}
/*
* Setup a stack builder with the expected stack depth
* Walk the stack and count the frames.
*/
void walk(int estimate) {
int limit = Math.min(depth, 16);
List<StackFrame> frames = new StackBuilder(depth, limit).build();
System.out.format("depth=%d estimate=%d expected=%d walked=%d%n",
depth, estimate, limit, frames.size());
assertEquals(limit, frames.size());
}
static class ConstructorNewInstance {
static final StackWalker walker =
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
List<String> testFramesOrReflectionFrames;
public ConstructorNewInstance() {
testFramesOrReflectionFrames = walker.walk(this::parse);
}
public List<String> collectedFrames() {
return testFramesOrReflectionFrames;
}
public boolean accept(StackFrame f) {
// Frames whose class names don't contain "."
// are our own test frames. These are the ones
// we expect.
// Frames whose class names contain ".reflect."
// are reflection frames. None should be present,
// since they are supposed to be filtered by
// by StackWalker. If we find any, we want to fail.
if (!f.getClassName().contains(".")
|| f.getClassName().contains(".reflect.")) {
System.out.println(" " + f);
return true;
}
// Filter out all other frames (in particular
// those from the test framework) in order to
// have predictable results.
return false;
}
public String frame(StackFrame f) {
return f.getClassName() + "::" + f.getMethodName();
}
List<String> parse(Stream<StackFrame> s) {
return s.filter(this::accept)
.map(this::frame)
.collect(Collectors.toList());
}
public static ConstructorNewInstance create() throws Exception {
return ConstructorNewInstance.class.getConstructor().newInstance();
}
}
static class StackBuilder {
private final int stackDepth;
private final int limit;
private int depth = 0;
private List<StackFrame> result;
StackBuilder(int stackDepth, int limit) {
this.stackDepth = stackDepth; // build method;
this.limit = limit;
}
List<StackFrame> build() {
trace("build");
m1();
return result;
}
void m1() {
trace("m1");
m2();
}
List m2() {
trace("m2");
m3();
return null;
}
int m3() {
trace("m3");
m4(null);
return 0;
}
void m4(Object o) {
trace("m4");
int remaining = stackDepth-depth-1;
if (remaining >= 4) {
m1();
} else {
filler(remaining);
}
}
void filler(int i) {
trace("filler");
if (i == 0)
walk();
else
filler(--i);
}
void walk() {
StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
result = walker.walk(s -> s.limit(limit).collect(Collectors.toList()));
}
void trace(String methodname) {
++depth;
if (verbose)
System.out.format("%2d: %s%n", depth, methodname);
}
static Map<String, MethodType> methodTypes() throws Exception {
return Map.of("m1", MethodType.methodType(void.class),
"m2", MethodType.methodType(List.class),
"m3", MethodType.methodType(int.class),
"m4", MethodType.methodType(void.class, Object.class));
}
}
}

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2015, 2025, 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 8140450
* @summary Test if the getCallerClass method returns empty optional
* @requires test.thread.factory == null
* @library /test/lib
* @run main CallerFromMain exec
*/
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class CallerFromMain {
private static final StackWalker sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
public static void main(String[] args) throws Exception {
if (args.length > 0) {
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder("CallerFromMain");
OutputAnalyzer output = ProcessTools.executeProcess(pb);
System.out.println(output.getOutput());
output.shouldHaveExitValue(0);
return;
}
// StackWalker::getCallerClass
// CallerFromMain::main
// no caller
try {
Class<?> c = sw.getCallerClass();
throw new RuntimeException("UOE not thrown. Caller: " + c);
} catch (IllegalCallerException e) {}
// StackWalker::getCallerClass
// Runnable::run
// Thread::run
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from Thread.run: " + c);
assertThreadClassAsCaller(c);
}
});
t1.setDaemon(true);
t1.start();
// StackWalker::getCallerClass
// CallerFromMain::doit
// Thread::run
Thread t2 = new Thread(CallerFromMain::doit);
t2.setDaemon(true);
t2.start();
// StackWalker::getCallerClass
// MyRunnable::run
// Thread::run
Thread t3 = new Thread(new MyRunnable());
t3.setDaemon(true);
t3.start();
// StackWalker::getCallerClass
// Runnable::run
// MyThread::run
Thread t4 = new MyThread(new Runnable() {
@Override
public void run() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from MyThread.run: " + c);
if (c != MyThread.class) {
throw new RuntimeException("Expected MyThread.class but got " + c);
}
}
});
t4.setDaemon(true);
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
static class MyThread extends Thread {
final Runnable runnable;
MyThread(Runnable runnable) {
super("MyThread");
this.runnable = runnable;
}
public void run() {
runnable.run();
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from Thread::run: " + c);
assertThreadClassAsCaller(c);
}
}
static void doit() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from CallerFromMain.doit: " + c);
assertThreadClassAsCaller(c);
}
static void assertThreadClassAsCaller(Class<?> caller) {
if (caller != Thread.class) {
throw new RuntimeException("Expected Thread.class but got " + caller);
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016, 2024, 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 8210375
* @summary Basic test for StackWalker.getCallerClass()
* @library src
* @modules java.base/jdk.internal.reflect
* @build java.base/java.util.CSM csm/*
* @run main/othervm csm/jdk.test.CallerSensitiveTest
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames csm/jdk.test.CallerSensitiveTest
*/
public class Main {
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2016, 2024, 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.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 REFLECTIVE_GET_CALLER_METHOD = "getCallerClassReflectively";
private static final String CSM_CALLER_METHOD = "caller";
public static void main(String... args) throws Throwable {
CallerSensitiveTest cstest = new CallerSensitiveTest();
// test static call to java.util.CSM::caller and CSM::getCallerClass
cstest.staticMethodCall();
// test reflective call to StackWalker::getCallerClass
cstest.invokeMethod();
// 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 result2 = (Result) method2.invoke(null);
checkNonCSMCaller(CallerSensitiveTest.class, result2);
Method method3 = java.util.CSM.class.getMethod(REFLECTIVE_GET_CALLER_METHOD);
Result result3 = (Result) method3.invoke(null);
checkNonCSMCaller(CallerSensitiveTest.class, result3);
}
void invokeMethod() throws Throwable {
Result result = java.util.CSM.getCallerClassReflectively();
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 result2 = (Result)mh2.invokeExact();
checkNonCSMCaller(CallerSensitiveTest.class, result2);
MethodHandle mh3 = lookup.findStatic(java.util.CSM.class, REFLECTIVE_GET_CALLER_METHOD,
MethodType.methodType(Result.class));
Result result3 = (Result)mh3.invokeExact();
checkNonCSMCaller(CallerSensitiveTest.class, result3);
}
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<Class<?>> caller = java.util.CSM::caller;
static Supplier<Result> 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();
}
}
}

View file

@ -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;
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2016, 2023, 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.lang.reflect.Method;
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,
DROP_METHOD_INFO,
SHOW_HIDDEN_FRAMES,
SHOW_REFLECT_FRAMES));
public static class Result {
public final List<Class<?>> callers;
public final List<StackWalker.StackFrame> frames;
Result(List<Class<?>> callers,
List<StackFrame> 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. Returned " + c2.getName());
} catch (UnsupportedOperationException e) {}
return c1;
}
/**
* Returns the caller of this non-caller-sensitive method returned
* by StackWalker::getCallerClass
*/
public static Result getCallerClass() {
Class<?> caller = walker.getCallerClass();
return new Result(List.of(caller), dump());
}
private static final Method GET_CALLER_CLASS;
static {
Method m = null;
try {
m = StackWalker.class.getMethod("getCallerClass");
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
GET_CALLER_CLASS = m;
}
/**
* Returns the caller of this non-caller-sensitive method returned
* by StackWalker::getCallerClass invoked via core reflection
*/
public static Result getCallerClassReflectively() throws ReflectiveOperationException {
Class<?> caller = (Class<?>)GET_CALLER_CLASS.invoke(walker);
return new Result(List.of(caller), dump());
}
static List<StackFrame> dump() {
return walker.walk(s -> s.collect(Collectors.toList()));
}
}

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2015, 2025, 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 8143214
* @summary Verify outputs of Thread.dumpStack() and Throwable.printStackTrace().
* This test should also been run against jdk9 successfully except of
* VM option MemberNameInStackFrame.
* @requires test.thread.factory == null
* @run main/othervm DumpStackTest
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Consumer;
public class DumpStackTest {
public static void main(String args[]) {
test();
testThread();
testLambda();
testMethodInvoke();
testMethodHandle();
}
static class CallFrame {
final String classname;
final String methodname;
CallFrame(Class<?> c, String methodname) {
this(c.getName(), methodname);
}
CallFrame(String classname, String methodname) {
this.classname = classname;
this.methodname = methodname;
}
String getClassName() {
return classname;
}
String getMethodName() {
return methodname;
}
String getFileName() {
int i = classname.lastIndexOf('.');
int j = classname.lastIndexOf('$');
String name = classname.substring(i+1, j >= 0 ? j : classname.length());
return name + ".java";
}
@Override
public String toString() {
return classname + "." + methodname + "(" + getFileName() + ")";
}
}
static void test() {
CallFrame[] callStack = new CallFrame[] {
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "test"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"), // non-public class
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run"),
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
getStackTrace(callStack);
}
static void getStackTrace(CallFrame[] callStack) {
// this method is the top of the stack
callStack[0] = new CallFrame(DumpStackTest.class, "getStackTrace");
try {
throw new RuntimeException();
} catch(RuntimeException ex) {
assertStackTrace(ex.getStackTrace(), callStack);
}
}
static void testThread() {
Thread t1 = new Thread() {
public void run() {
c();
}
void c() {
CallFrame[] callStack = new CallFrame[] {
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(this.getClass(), "c"),
new CallFrame(this.getClass(), "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
};
t1.start();
try {
t1.join();
} catch(InterruptedException e) {}
}
static void testLambda() {
Consumer<Void> c = (x) -> consumeLambda();
c.accept(null);
}
static void consumeLambda() {
CallFrame[] callStack = new CallFrame[]{
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "consumeLambda"),
new CallFrame(DumpStackTest.class, "lambda$testLambda$0"),
new CallFrame(DumpStackTest.class, "testLambda"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
static void testMethodInvoke() {
try {
Method m = DumpStackTest.class.getDeclaredMethod("methodInvoke");
m.invoke(null);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
static void methodInvoke() {
CallFrame[] callStack = new CallFrame[] {
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "methodInvoke"),
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(DumpStackTest.class, "testMethodInvoke"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
static void testMethodHandle() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
MethodHandle handle = lookup.findStatic(DumpStackTest.class, "methodHandle",
MethodType.methodType(void.class));
handle.invoke();
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
static void methodHandle() {
CallFrame[] callStack = new CallFrame[]{
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "methodHandle"),
new CallFrame(DumpStackTest.class, "testMethodHandle"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
static void assertStackTrace(StackTraceElement[] actual, CallFrame[] expected) {
System.out.println("--- Actual ---");
Arrays.stream(actual).forEach(e -> System.out.println(e));
System.out.println("--- Expected ---");
Arrays.stream(expected).forEach(e -> System.out.println(e));
for (int i = 0, j = 0; i < actual.length; i++) {
// filter test framework classes
if (actual[i].getClassName().startsWith("com.sun.javatest.regtest"))
continue;
assertEquals(actual[i], expected[j++], i);
}
}
static void assertEquals(StackTraceElement actual, CallFrame expected, int idx) {
if (!actual.getClassName().equals(expected.getClassName()) ||
!actual.getFileName().equals(expected.getFileName()) ||
!actual.getMethodName().equals(expected.getMethodName())) {
throw new RuntimeException("StackTraceElements mismatch at index " + idx +
". Expected [" + expected + "], but get [" + actual + "]");
}
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2015, 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 8143214
* @summary Verify StackWalker works well when embedded in another
* StackWalker's functions.
* @run testng/othervm EmbeddedStackWalkTest
*/
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.EnumSet;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class EmbeddedStackWalkTest {
static final StackWalker WALKERS[] = new StackWalker[] {
StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
};
static final int BIG_LOOP = 30;
static final int SMALL_LOOP = 5;
@DataProvider
public StackWalker[][] walkerProvider() {
return new StackWalker[][] {
new StackWalker[] { WALKERS[0] },
new StackWalker[] { WALKERS[1] },
new StackWalker[] { WALKERS[2] }
};
}
@Test(dataProvider = "walkerProvider")
public void test(StackWalker walker) {
C1.call(walker, BIG_LOOP);
}
// line numbers are hardcoded for now.
// Should annotate the line numbers and auto-generated these constants
// for test verification instead
static final int BEGIN_LINE = 71; // the begin line number of approximate range.
static final int END_LINE = 136; // the end line number of approximate range.
static class C1 { // here is the begin line number of approximate range, L71.
public static void call(StackWalker walker, int loops) {
if (loops == 0) {
String caller = walker.walk(s ->
s.map(StackFrame::getClassName)
.filter(cn -> !cn.startsWith("jdk.internal.reflect.") && !cn.startsWith("java.lang.invoke"))
.skip(2).findFirst()
).get();
assertEquals(caller, C1.class.getName());
walker.forEach(f -> C2.testEmbeddedWalker());
} else {
call(walker, --loops);
}
}
}
static class C2 {
static final StackWalker embeddedWalkers[] = new StackWalker[] {
StackWalker.getInstance(),
StackWalker.getInstance(SHOW_REFLECT_FRAMES),
StackWalker.getInstance(SHOW_HIDDEN_FRAMES)
};
public static void testEmbeddedWalker() {
walk(SMALL_LOOP);
}
static void walk(int loops) {
if (loops == 0) {
Arrays.stream(embeddedWalkers)
.forEach(walker -> run(walker));
} else {
walk(--loops);
}
}
static void run(StackWalker walker) {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = null;
try {
handle = lookup.findStatic(C2.class, "call",
MethodType.methodType(void.class, StackWalker.class));
handle.invoke(walker);
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
static void call(StackWalker walker) {
String caller = walker.walk(s ->
s.map(StackFrame::getClassName)
.filter(cn -> !cn.startsWith("jdk.internal.reflect.") && !cn.startsWith("java.lang.invoke"))
.skip(2).findFirst()
).get();
assertEquals(caller, C2.class.getName());
verify(walker, C1.class, "call");
verify(walker, C2.class, "call");
verify(walker, C2.class, "run");
verify(walker, C2.class, "walk");
verify(walker, C2.class, "testEmbeddedWalker");
} // here is the end line number of approximate range, L136.
static void verify(StackWalker walker, Class<?> c, String mn) {
final String fileName = "EmbeddedStackWalkTest.java";
walker.walk(s -> {
s.limit(BIG_LOOP)
.filter(f -> c.getName().equals(f.getClassName()) && mn.equals(f.getMethodName()))
.forEach(f -> {
assertEquals(f.getFileName(), fileName);
int line = f.getLineNumber();
assertTrue(line >= BEGIN_LINE && line <= END_LINE);
StackTraceElement st = f.toStackTraceElement();
assertEquals(c.getName(), st.getClassName());
assertEquals(mn, st.getMethodName());
assertEquals(st.getFileName(), fileName);
line = st.getLineNumber();
assertTrue(line >= BEGIN_LINE && line <= END_LINE);
});
return null;
});
}
}
}

View file

@ -0,0 +1,265 @@
/*
* Copyright (c) 2015, 2024, 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 8140450 8152893 8189291 8210375
* @summary Basic test for StackWalker.getCallerClass()
* @run main/othervm GetCallerClassTest
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames GetCallerClassTest
*/
import static java.lang.StackWalker.Option.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
public class GetCallerClassTest {
private final StackWalker walker;
private final boolean expectUOE;
public GetCallerClassTest(StackWalker sw, boolean expect) {
this.walker = sw;
this.expectUOE = expect;
}
public static void main(String... args) throws Exception {
new GetCallerClassTest(StackWalker.getInstance(), true).test();
new GetCallerClassTest(StackWalker.getInstance(RETAIN_CLASS_REFERENCE), false).test();
new GetCallerClassTest(StackWalker.getInstance(EnumSet.of(RETAIN_CLASS_REFERENCE,
SHOW_HIDDEN_FRAMES)), false).test();
}
public void test() {
new TopLevelCaller().run();
new LambdaTest().run();
new Nested().createNestedCaller().run();
new InnerClassCaller().run();
new ReflectionTest().run();
List<Thread> threads = Arrays.asList(
new Thread(new TopLevelCaller()),
new Thread(new LambdaTest()),
new Thread(new Nested().createNestedCaller()),
new Thread(new InnerClassCaller()),
new Thread(new ReflectionTest())
);
threads.stream().forEach(Thread::start);
threads.stream().forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
public static void staticGetCallerClass(StackWalker stackWalker,
Class<?> expected,
boolean expectUOE) {
try {
Class<?> c = stackWalker.getCallerClass();
assertEquals(c, expected);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (RuntimeException e) { // also catches UOE
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw e;
}
}
public static void reflectiveGetCallerClass(StackWalker stackWalker,
Class<?> expected,
boolean expectUOE) {
try {
// Use reflection to call Method::invoke that invokes StackWalker::getCallerClass
Method m = StackWalker.class.getMethod("getCallerClass");
Method invoke = Method.class.getMethod("invoke", Object.class, Object[].class);
Class<?> c = (Class<?>) invoke.invoke(m, new Object[] { stackWalker, null });
assertEquals(c, expected);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (Throwable e) {
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw new RuntimeException(e);
}
}
public static void methodHandleGetCallerClass(StackWalker stackWalker,
Class<?> expected,
boolean expectUOE) {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
MethodHandle mh = lookup.findVirtual(StackWalker.class, "getCallerClass",
MethodType.methodType(Class.class));
Class<?> c = (Class<?>) mh.invokeExact(stackWalker);
assertEquals(c, expected);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (Throwable e) {
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw new RuntimeException(e);
}
}
public static void assertEquals(Class<?> c, Class<?> expected) {
if (expected != c) {
throw new RuntimeException("Got " + c + ", but expected " + expected);
}
}
/** Is there an UnsupportedOperationException in there? */
public static boolean causeIsUOE(Throwable t) {
while (t != null) {
if (t instanceof UnsupportedOperationException) {
return true;
}
t = t.getCause();
}
return false;
}
class TopLevelCaller implements Runnable {
public void run() {
GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
}
}
class LambdaTest implements Runnable {
public void run() {
Runnable lambdaRunnable = () -> {
try {
Class<?> c = walker.getCallerClass();
assertEquals(c, LambdaTest.class);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (Throwable e) {
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw new RuntimeException(e);
}
};
lambdaRunnable.run();
}
}
class Nested {
NestedClassCaller createNestedCaller() { return new NestedClassCaller(); }
class NestedClassCaller implements Runnable {
public void run() {
GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
}
}
}
class InnerClassCaller implements Runnable {
public void run() {
new Inner().test();
}
class Inner {
void test() {
GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
}
}
}
class ReflectionTest implements Runnable {
final MethodType methodType =
MethodType.methodType(void.class, StackWalker.class, Class.class, boolean.class);
public void run() {
callMethodHandle();
callMethodHandleRefl();
callMethodInvoke();
callMethodInvokeRefl();
}
void callMethodHandle() {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
"staticGetCallerClass",
methodType);
mh.invokeExact(walker, ReflectionTest.class, expectUOE);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
void callMethodHandleRefl() {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
"reflectiveGetCallerClass",
methodType);
mh.invokeExact(walker, ReflectionTest.class, expectUOE);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
void callMethodInvoke() {
try {
Method m = GetCallerClassTest.class.getMethod("staticGetCallerClass",
StackWalker.class, Class.class, boolean.class);
m.invoke(null, walker, ReflectionTest.class, expectUOE);
} catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
throw new RuntimeException(e);
}
}
void callMethodInvokeRefl() {
try {
Method m = GetCallerClassTest.class.getMethod("reflectiveGetCallerClass",
StackWalker.class, Class.class, boolean.class);
m.invoke(null, walker, ReflectionTest.class, expectUOE);
} catch (UnsupportedOperationException e) {
throw e;
} catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2015, 2023, 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 8020968
* @summary Basic test for hidden frames
* @run main HiddenFrames
*/
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class HiddenFrames {
public static void main(String... args) throws Exception {
new HiddenFrames().test();
new HiddenFrames(Option.SHOW_REFLECT_FRAMES).test();
new HiddenFrames(Option.SHOW_HIDDEN_FRAMES).test();
}
private final Option option;
private final StackWalker walker;
private final List<StackFrame> lambdas = new ArrayList<>();
private final List<StackFrame> reflects = new ArrayList<>();
HiddenFrames() {
this.option = null;
this.walker = StackWalker.getInstance();
}
HiddenFrames(Option option) {
this.option = option;
this.walker = StackWalker.getInstance(option);
}
void test() throws Exception {
walk();
walkFromReflection();
}
void walk() {
Stream.of(0).forEach(i -> walker.walk(s ->
{
s.forEach(this::checkFrame);
return null;
}));
// only check hidden frames but not reflection frames
// walk is not invoked via reflection
if (option == null && !lambdas.isEmpty()) {
throw new RuntimeException("Hidden frames are shown");
}
if (option == Option.SHOW_HIDDEN_FRAMES && lambdas.isEmpty()) {
throw new RuntimeException("No hidden Lambda frame");
}
}
void walkFromReflection() throws Exception {
Method m = HiddenFrames.class.getDeclaredMethod("walk");
m.invoke(this);
if (option == null && !lambdas.isEmpty()) {
throw new RuntimeException("Hidden frames are shown");
}
if (option == Option.SHOW_HIDDEN_FRAMES && lambdas.isEmpty()) {
throw new RuntimeException("No hidden Lambda frame");
}
if (option != null && reflects.isEmpty()) {
throw new RuntimeException("No reflection frame");
}
}
void checkFrame(StackFrame frame) {
String cn = frame.getClassName();
if (cn.startsWith("java.lang.reflect.") || cn.startsWith("jdk.internal.reflect.")) {
reflects.add(frame);
}
if (cn.contains("$$Lambda")) {
lambdas.add(frame);
}
}
}

View file

@ -0,0 +1,543 @@
/*
* Copyright (c) 2015, 2025, 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 8020968 8147039 8156073
* @summary Tests for locals and operands
* @modules java.base/java.lang:open
* @run testng/othervm/timeout=480 -Xint -DtestUnused=true LocalsAndOperands
* @run testng/othervm/timeout=480 -Xcomp LocalsAndOperands
*/
/*
* @test
* @bug 8020968 8147039 8156073
* @modules java.base/java.lang:open
* @requires !vm.graal.enabled
* @run testng/othervm/timeout=480 -Xcomp -XX:-TieredCompilation LocalsAndOperands
*/
import org.testng.annotations.*;
import static org.testng.Assert.*;
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
public class LocalsAndOperands {
static final boolean debug = false;
static final boolean is32bit;
static final boolean testUnused;
static Class<?> liveStackFrameClass;
static Class<?> primitiveSlotClass;
static Class<?> primitiveSlot32Class;
static Class<?> primitiveSlot64Class;
static StackWalker extendedWalker;
static Method getLocals;
static Method getOperands;
static Method getMonitors;
static Method primitiveSize;
static Method primitiveLongValue;
static Method primitiveIntValue;
static Method getExtendedWalker;
private static final long LOWER_LONG_VAL = 4L; // Lower bits
private static final long UPPER_LONG_VAL = 0x123400000000L; // Upper bits
private static final long NEG_LONG_VAL = Long.MIN_VALUE;
private static final double LOWER_DOUBLE_VAL = Double.longBitsToDouble(0xABCDL);
private static final double UPPER_DOUBLE_VAL = Double.longBitsToDouble(0x432100000000L);
static {
try {
liveStackFrameClass = Class.forName("java.lang.LiveStackFrame");
primitiveSlotClass = Class.forName("java.lang.LiveStackFrame$PrimitiveSlot");
primitiveSlot32Class = Class.forName("java.lang.LiveStackFrameInfo$PrimitiveSlot32");
primitiveSlot64Class = Class.forName("java.lang.LiveStackFrameInfo$PrimitiveSlot64");
getLocals = liveStackFrameClass.getDeclaredMethod("getLocals");
getLocals.setAccessible(true);
getOperands = liveStackFrameClass.getDeclaredMethod("getStack");
getOperands.setAccessible(true);
getMonitors = liveStackFrameClass.getDeclaredMethod("getMonitors");
getMonitors.setAccessible(true);
primitiveSize = primitiveSlotClass.getDeclaredMethod("size");
primitiveSize.setAccessible(true);
primitiveLongValue = primitiveSlotClass.getDeclaredMethod("longValue");
primitiveLongValue.setAccessible(true);
primitiveIntValue = primitiveSlotClass.getDeclaredMethod("intValue");
primitiveIntValue.setAccessible(true);
getExtendedWalker = liveStackFrameClass.getMethod("getStackWalker", Set.class);
getExtendedWalker.setAccessible(true);
extendedWalker = (StackWalker) getExtendedWalker.invoke(null,
EnumSet.noneOf(StackWalker.Option.class));
String dataModel = System.getProperty("sun.arch.data.model");
if ("32".equals(dataModel)) {
is32bit = true;
} else if ("64".equals(dataModel)) {
is32bit= false;
} else {
throw new RuntimeException("Weird data model:" + dataModel);
}
System.out.println("VM bits: " + dataModel);
testUnused = System.getProperty("testUnused") != null;
} catch (Throwable t) { throw new RuntimeException(t); }
}
/** Helper method to return a StackFrame's locals */
static Object[] invokeGetLocals(StackFrame arg) {
try {
return (Object[]) getLocals.invoke(arg);
} catch (Exception e) { throw new RuntimeException(e); }
}
/*****************
* DataProviders *
*****************/
/** Calls KnownLocalsTester.testLocals* and provides LiveStackFrames */
@DataProvider
public static StackFrame[][] knownLocalsProvider() {
List<StackFrame[]> list = new ArrayList<>(3);
list.add(new KnownLocalsTester().testLocalsKeepAlive());
list.add(new KnownLocalsTester().testLocalsKeepAliveArgs(0xA, 'z',
"himom", 0x3FF00000000L + 0xFFFF, Math.PI));
if (testUnused) {
list.add(new KnownLocalsTester().testLocalsUnused());
}
return list.toArray(new StackFrame[1][1]);
}
/****************
* Test methods *
****************/
/**
* Check for expected local values in the LiveStackFrame
*/
@Test(dataProvider = "knownLocalsProvider")
public static void checkLocalValues(StackFrame... frames) {
dumpFramesIfDebug(frames);
try {
Stream.of(frames)
.filter(f -> KnownLocalsTester.TEST_METHODS.contains(f.getMethodName()))
.forEach(LocalsAndOperands::checkFrameLocals);
} catch (Exception e) { dumpFramesIfNotDebug(frames); throw e; }
}
/**
* Check the locals in the given StackFrame against the expected values.
*/
private static void checkFrameLocals(StackFrame f) {
Object[] expectedArray = KnownLocalsTester.LOCAL_VALUES;
Object[] locals = invokeGetLocals(f);
for (int i = 0; i < locals.length; i++) {
Object expected = expectedArray[i];
Object observed = locals[i];
if (expected == null) { /* skip nulls in golden values */
continue;
} else if (expected instanceof KnownLocalsTester.TwoSlotValue) {
// confirm integrity of expected values
assertEquals(expectedArray[i+1], null,
"Malformed array of expected values - slot after TwoSlotValue should be null");
assertLongIsInSlots(locals[i], locals[i+1],
((KnownLocalsTester.TwoSlotValue)expected).value);
i++; // skip following slot
} else if (primitiveSlotClass.isInstance(observed)) { // single slot primitive
assertTrue(primitiveValueEquals(observed, expected),
"Local value mismatch: local " + i + " value is " +
observed + ", expected " + expected);
} else if (expected instanceof Class) {
assertTrue(((Class)expected).isInstance(observed),
"Local value mismatch: local " + i + " expected instancof " +
expected + " but got " + observed);
} else if (expected instanceof String) {
assertEquals(expected, observed, "Local value mismatch: local " +
i + " value is " + observed + ", expected " + expected);
} else {
throw new RuntimeException("Unrecognized expected local value " +
i + ": " + expected);
}
}
}
/**
* Sanity check for locals and operands, including testng/jtreg frames
* using all StackWalker options.
*/
@Test
public synchronized void fullStackSanityCheck() throws Throwable {
if (debug) {
System.out.println("Running fullStackSanityCheck");
}
StackWalker sw = (StackWalker) getExtendedWalker.invoke(null,
EnumSet.of(SHOW_HIDDEN_FRAMES, SHOW_REFLECT_FRAMES,
RETAIN_CLASS_REFERENCE));
sw.forEach(f -> {
if (debug) {
printLocals(f);
} else {
try {
System.out.println(" " + f + ": " +
((Object[]) getLocals.invoke(f)).length + " locals, " +
((Object[]) getOperands.invoke(f)).length + " operands, " +
((Object[]) getMonitors.invoke(f)).length + " monitors");
} catch (IllegalAccessException|InvocationTargetException t) {
throw new RuntimeException(t);
}
}
});
}
/**
* Test that LiveStackFrames are not provided with the default StackWalker
* options.
*/
@Test
public static void noLocalsSanityCheck() {
StackWalker sw = StackWalker.getInstance();
sw.forEach(f -> {
assertFalse(liveStackFrameClass.isInstance(f),
"should not be LiveStackFrame");
});
}
/**
* Class stack-walking methods with a known set of methods and local variables.
*/
static class KnownLocalsTester {
private StackWalker walker;
KnownLocalsTester() {
this.walker = extendedWalker;
}
/**
* Perform stackwalk without keeping local variables alive and return an
* array of the collected StackFrames
*/
private synchronized StackFrame[] testLocalsUnused() {
// Unused local variables will become dead
int x = 0xA;
char c = 'z'; // 0x7A
String hi = "himom";
long l = 0x3FF00000000L + 0xFFFFL;
double d = Math.PI;
return walker.walk(s ->
s.filter(f -> TEST_METHODS.contains(f.getMethodName()))
.toArray(StackFrame[]::new)
);
}
/**
* Perform stackwalk, keeping local variables alive, and return a list of
* the collected StackFrames
*/
private synchronized StackFrame[] testLocalsKeepAlive() {
int x = 0xA;
char c = 'z'; // 0x7A
String hi = "himom";
long l = 0x3FF00000000L + 0xFFFFL;
double d = Math.PI;
StackFrame[] frames = walker.walk(s ->
s.filter(f -> TEST_METHODS.contains(f.getMethodName()))
.toArray(StackFrame[]::new)
);
// Use local variables so they stay alive
System.out.println("Stayin' alive: "+this+" "+x+" "+c+" "+hi+" "+l+" "+d);
return frames;
}
/**
* Perform stackwalk, keeping method arguments alive, and return a list of
* the collected StackFrames
*/
private synchronized StackFrame[] testLocalsKeepAliveArgs(int x, char c,
String hi, long l,
double d) {
StackFrame[] frames = walker.walk(s ->
s.filter(f -> TEST_METHODS.contains(f.getMethodName()))
.toArray(StackFrame[]::new)
);
// Use local variables so they stay alive
System.out.println("Stayin' alive: "+this+" "+x+" "+c+" "+hi+" "+l+" "+d);
return frames;
}
// An expected two-slot local (i.e. long or double)
static class TwoSlotValue {
public long value;
public TwoSlotValue(long value) { this.value = value; }
}
// Expected values for locals in KnownLocalsTester.testLocals* methods
private final static Object[] LOCAL_VALUES = new Object[] {
LocalsAndOperands.KnownLocalsTester.class,
Integer.valueOf(0xA),
Integer.valueOf(0x7A),
"himom",
new TwoSlotValue(0x3FF00000000L + 0xFFFFL),
null, // 2nd slot
new TwoSlotValue(Double.doubleToRawLongBits(Math.PI)),
null, // 2nd slot
Integer.valueOf(0)
};
private final static List<String> TEST_METHODS =
List.of("testLocalsUnused",
"testLocalsKeepAlive",
"testLocalsKeepAliveArgs");
}
/* Simpler tests of long & double arguments */
@Test
public static void testUsedLongArg() throws Exception {
usedLong(LOWER_LONG_VAL);
usedLong(UPPER_LONG_VAL);
usedLong(NEG_LONG_VAL);
}
private static void usedLong(long longArg) throws Exception {
StackFrame[] frames = extendedWalker.walk(s ->
s.filter(f -> "usedLong".equals(f.getMethodName()))
.toArray(StackFrame[]::new)
);
try {
dumpFramesIfDebug(frames);
Object[] locals = (Object[]) getLocals.invoke(frames[0]);
assertLongIsInSlots(locals[0], locals[1], longArg);
System.out.println("Stayin' alive: " + longArg);
} catch (Exception t) {
dumpFramesIfNotDebug(frames);
throw t;
}
}
@Test
public static void testUnusedLongArg() throws Exception {
if (testUnused) {
unusedLong(NEG_LONG_VAL);
}
}
private static void unusedLong(long longArg) throws Exception {
StackFrame[] frames = extendedWalker.walk(s ->
s.filter(f -> "unusedLong".equals(f.getMethodName()))
.toArray(StackFrame[]::new)
);
try {
dumpFramesIfDebug(frames);
final Object[] locals = (Object[]) getLocals.invoke(frames[0]);
assertLongIsInSlots(locals[0], locals[1], NEG_LONG_VAL);
} catch (Exception t) {
dumpFramesIfNotDebug(frames);
throw t;
}
}
@Test
public static void testUsedDoubleArg() throws Exception {
usedDouble(LOWER_DOUBLE_VAL);
usedDouble(UPPER_DOUBLE_VAL);
}
private static void usedDouble(double doubleArg) throws Exception {
StackFrame[] frames = extendedWalker.walk(s ->
s.filter(f -> "usedDouble".equals(f.getMethodName()))
.toArray(StackFrame[]::new)
);
try {
dumpFramesIfDebug(frames);
Object[] locals = (Object[]) getLocals.invoke(frames[0]);
assertDoubleIsInSlots(locals[0], locals[1], doubleArg);
System.out.println("Stayin' alive: " + doubleArg);
} catch (Exception t) {
dumpFramesIfNotDebug(frames);
throw t;
}
}
/*******************
* Utility Methods *
*******************/
/**
* Print stack trace with locals
*/
public static void dumpStackWithLocals(StackFrame...frames) {
Stream.of(frames).forEach(LocalsAndOperands::printLocals);
}
public static void dumpFramesIfDebug(StackFrame...frames) {
if (debug) { dumpStackWithLocals(frames); }
}
public static void dumpFramesIfNotDebug(StackFrame...frames) {
if (!debug) { dumpStackWithLocals(frames); }
}
/**
* Print the StackFrame and an indexed list of its locals
*/
public static void printLocals(StackWalker.StackFrame frame) {
try {
System.out.println("Locals for: " + frame);
Object[] locals = (Object[]) getLocals.invoke(frame);
for (int i = 0; i < locals.length; i++) {
String localStr = null;
if (primitiveSlot64Class.isInstance(locals[i])) {
localStr = String.format("0x%X",
(Long)primitiveLongValue.invoke(locals[i]));
} else if (primitiveSlot32Class.isInstance(locals[i])) {
localStr = String.format("0x%X",
(Integer)primitiveIntValue.invoke(locals[i]));
} else if (locals[i] != null) {
localStr = locals[i].toString();
}
System.out.format(" local %d: %s type %s\n", i, localStr, type(locals[i]));
}
Object[] operands = (Object[]) getOperands.invoke(frame);
for (int i = 0; i < operands.length; i++) {
System.out.format(" operand %d: %s type %s%n", i, operands[i],
type(operands[i]));
}
Object[] monitors = (Object[]) getMonitors.invoke(frame);
for (int i = 0; i < monitors.length; i++) {
System.out.format(" monitor %d: %s%n", i, monitors[i]);
}
} catch (Exception e) { throw new RuntimeException(e); }
}
private static String type(Object o) {
try {
if (o == null) {
return "null";
} else if (primitiveSlotClass.isInstance(o)) {
int s = (int)primitiveSize.invoke(o);
return s + "-byte primitive";
} else {
return o.getClass().getName();
}
} catch(Exception e) { throw new RuntimeException(e); }
}
/*
* Check if the PrimitiveValue "primVal" contains the specified value,
* either a Long or an Integer.
*/
static boolean primitiveValueEquals(Object primVal, Object expectedVal) {
try {
if (expectedVal instanceof Long) {
assertFalse(is32bit);
assertTrue(primitiveSlot64Class.isInstance(primVal));
assertTrue(8 == (int)primitiveSize.invoke(primVal));
return Objects.equals(primitiveLongValue.invoke(primVal), expectedVal);
} else if (expectedVal instanceof Integer) {
int expectedInt = (Integer)expectedVal;
if (is32bit) {
assertTrue(primitiveSlot32Class.isInstance(primVal),
"expected a PrimitiveSlot32 on 32-bit VM");
assertTrue(4 == (int)primitiveSize.invoke(primVal));
return expectedInt == (int)primitiveIntValue.invoke(primVal);
} else {
assertTrue(primitiveSlot64Class.isInstance(primVal),
"expected a PrimitiveSlot64 on 64-bit VM");
assertTrue(8 == (int)primitiveSize.invoke(primVal));
// Look for int expectedVal in high- or low-order 32 bits
long primValLong = (long)primitiveLongValue.invoke(primVal);
return (int)(primValLong & 0x00000000FFFFFFFFL) == expectedInt ||
(int)(primValLong >>> 32) == expectedInt;
}
} else {
throw new RuntimeException("Called with non-Integer/Long: " + expectedVal);
}
} catch (IllegalAccessException|InvocationTargetException e) {
throw new RuntimeException(e);
}
}
/*
* Assert that the expected 2-slot long value is stored somewhere in the
* pair of slots.
* Throw exception if long value isn't in the two slots given.
* Accounts for 32 vs 64 bit, but is lax on endianness (accepts either)
*/
static void assertLongIsInSlots(Object primVal0, Object primVal1, long expected) {
try {
if (is32bit) {
int upper = (int)(expected & 0xFFFFFFFFL);
int lower = (int)(expected >> 32);
if (!((primitiveValueEquals(primVal0, upper) &&
primitiveValueEquals(primVal1, lower)) ||
(primitiveValueEquals(primVal0, lower) &&
primitiveValueEquals(primVal1, upper)))) {
throw new RuntimeException(String.format("0x%X and 0x%X of 0x%016X not found in 0x%X and 0x%X",
upper, lower, expected,
(int)primitiveIntValue.invoke(primVal0),
(int)primitiveIntValue.invoke(primVal1)));
}
} else {
if (!(primitiveValueEquals(primVal0, expected) ||
primitiveValueEquals(primVal1, expected))) {
throw new RuntimeException(String.format("0x%016X not found in 0x%016X or 0x%016X",
expected,
(long)primitiveLongValue.invoke(primVal0),
(long)primitiveLongValue.invoke(primVal1)));
}
}
} catch (IllegalAccessException|InvocationTargetException e) {
throw new RuntimeException(e);
}
}
static void assertDoubleIsInSlots(Object primVal0, Object primVal1, double expected) {
assertLongIsInSlots(primVal0, primVal1, Double.doubleToRawLongBits(expected));
}
}

View file

@ -0,0 +1,360 @@
/*
* Copyright (c) 2015, 2023, 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.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
/**
* @test
* @bug 8140450
* @summary This test will walk the stack using different methods, called
* from several threads running concurrently.
* Except in the case of MTSTACKSTREAM - which takes a snapshot
* of the stack before walking, all the methods only allow to
* walk the current thread stack.
* @run main/othervm MultiThreadStackWalk
* @author danielfuchs
*/
public class MultiThreadStackWalk {
static Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
"java.lang.reflect.Method",
"com.sun.javatest.regtest.MainWrapper$MainThread",
"java.lang.Thread"
));
static final List<Class<?>> streamPipelines = Arrays.asList(
classForName("java.util.stream.AbstractPipeline"),
classForName("java.util.stream.TerminalOp")
);
static Class<?> classForName(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
}
private static boolean isStreamPipeline(Class<?> clazz) {
for (Class<?> c : streamPipelines) {
if (c.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
/**
* An object that contains variables pertaining to the execution
* of the test within one thread.
* A small amount of those variable are shared with sub threads when
* the stack walk is executed in parallel - that is when spliterators
* obtained from trySplit are handed over to an instance of SplitThread
* in order to parallelize thread walking.
* @see WalkThread#handOff(MultiThreadStackWalk.Env, java.util.Spliterator, boolean, boolean)
* @see Env#split(MultiThreadStackWalk.Env)
*/
public static class Env {
final AtomicLong frameCounter; // private: the counter for the current thread.
final long checkMarkAt; // constant: the point at which we expect to
// find the marker in consume()
final long max; // constant: the maximum number of recursive
// calls to Call.
final AtomicBoolean debug ; // shared: whether debug is active for the
// instance of Test from which this instance
// of Env was spawned
final AtomicLong markerCalled; // shared: whether the marker was reached
final AtomicLong maxReached; // shared: whether max was reached
final Set<String> unexpected; // shared: list of unexpected infrastructure
// classes encountered after max is reached
public Env(long total, long markAt, AtomicBoolean debug) {
this.debug = debug;
frameCounter = new AtomicLong();
maxReached = new AtomicLong();
unexpected = Collections.synchronizedSet(new TreeSet<>());
this.max = total+2;
this.checkMarkAt = total - markAt + 1;
this.markerCalled = new AtomicLong();
}
// Used when delegating part of the stack walking to a sub thread
// see WalkThread.handOff.
private Env(Env orig, long start) {
debug = orig.debug;
frameCounter = new AtomicLong(start);
maxReached = orig.maxReached;
unexpected = orig.unexpected;
max = orig.max;
checkMarkAt = orig.checkMarkAt;
markerCalled = orig.markerCalled;
}
// The stack walk consumer method, where all the checks are
// performed.
public void consume(StackFrame sfi) {
if (frameCounter.get() == 0 && isStreamPipeline(sfi.getDeclaringClass())) {
return;
}
final long count = frameCounter.getAndIncrement();
final StringBuilder builder = new StringBuilder();
builder.append("Declaring class[")
.append(count)
.append("]: ")
.append(sfi.getDeclaringClass());
builder.append('\n');
builder.append("\t")
.append(sfi.getClassName())
.append(".")
.append(sfi.toStackTraceElement().getMethodName())
.append(sfi.toStackTraceElement().isNativeMethod()
? "(native)"
: "(" + sfi.toStackTraceElement().getFileName()
+":"+sfi.toStackTraceElement().getLineNumber()+")");
builder.append('\n');
if (debug.get()) {
System.out.print("[debug] " + builder.toString());
builder.setLength(0);
}
if (count == max) {
maxReached.incrementAndGet();
}
if (count == checkMarkAt) {
if (sfi.getDeclaringClass() != MultiThreadStackWalk.Marker.class) {
throw new RuntimeException("Expected Marker at " + count
+ ", found " + sfi.getDeclaringClass());
}
} else {
if (count <= 0 && sfi.getDeclaringClass() != MultiThreadStackWalk.Call.class) {
throw new RuntimeException("Expected Call at " + count
+ ", found " + sfi.getDeclaringClass());
} else if (count > 0 && count < max && sfi.getDeclaringClass() != MultiThreadStackWalk.Test.class) {
throw new RuntimeException("Expected Test at " + count
+ ", found " + sfi.getDeclaringClass());
} else if (count == max && sfi.getDeclaringClass() != MultiThreadStackWalk.class) {
throw new RuntimeException("Expected MultiThreadStackWalk at "
+ count + ", found " + sfi.getDeclaringClass());
} else if (count == max && !sfi.toStackTraceElement().getMethodName().equals("runTest")) {
throw new RuntimeException("Expected runTest method at "
+ count + ", found " + sfi.toStackTraceElement().getMethodName());
} else if (count == max+1) {
if (sfi.getDeclaringClass() != MultiThreadStackWalk.WalkThread.class) {
throw new RuntimeException("Expected MultiThreadStackWalk at "
+ count + ", found " + sfi.getDeclaringClass());
}
if (count == max && !sfi.toStackTraceElement().getMethodName().equals("run")) {
throw new RuntimeException("Expected main method at "
+ count + ", found " + sfi.toStackTraceElement().getMethodName());
}
} else if (count > max+1) {
// expect JTreg infrastructure...
if (!infrastructureClasses.contains(sfi.getDeclaringClass().getName())) {
System.err.println("**** WARNING: encountered unexpected infrastructure class at "
+ count +": " + sfi.getDeclaringClass().getName());
unexpected.add(sfi.getDeclaringClass().getName());
}
}
}
if (count == 100) {
// Maybe we should had some kind of checking inside that lambda
// too. For the moment we should be satisfied if it doesn't throw
// any exception and doesn't make the outer walk fail...
StackWalker.getInstance(RETAIN_CLASS_REFERENCE).forEach(x -> {
StackTraceElement st = x.toStackTraceElement();
StringBuilder b = new StringBuilder();
b.append("*** inner walk: ")
.append(x.getClassName())
.append(st == null ? "- no stack trace element -" :
("." + st.getMethodName()
+ (st.isNativeMethod() ? "(native)" :
"(" + st.getFileName()
+ ":" + st.getLineNumber() + ")")))
.append('\n');
if (debug.get()) {
System.out.print(b.toString());
b.setLength(0);
}
});
}
}
}
public interface Call {
enum WalkType {
WALKSTACK, // use Thread.walkStack
}
default WalkType getWalkType() { return WalkType.WALKSTACK;}
default void walk(Env env) {
WalkType walktype = getWalkType();
System.out.println("Thread "+ Thread.currentThread().getName()
+" starting walk with " + walktype);
switch(walktype) {
case WALKSTACK:
StackWalker.getInstance(RETAIN_CLASS_REFERENCE)
.forEach(env::consume);
break;
default:
throw new InternalError("Unknown walk type: " + walktype);
}
}
default void call(Env env, Call next, int total, int current, int markAt) {
if (current < total) {
next.call(env, next, total, current+1, markAt);
}
}
}
public static class Marker implements Call {
final WalkType walkType;
Marker(WalkType walkType) {
this.walkType = walkType;
}
@Override
public WalkType getWalkType() {
return walkType;
}
@Override
public void call(Env env, Call next, int total, int current, int markAt) {
env.markerCalled.incrementAndGet();
if (current < total) {
next.call(env, next, total, current+1, markAt);
} else {
next.walk(env);
}
}
}
public static class Test implements Call {
final Marker marker;
final WalkType walkType;
final AtomicBoolean debug;
Test(WalkType walkType) {
this.walkType = walkType;
this.marker = new Marker(walkType);
this.debug = new AtomicBoolean();
}
@Override
public WalkType getWalkType() {
return walkType;
}
@Override
public void call(Env env, Call next, int total, int current, int markAt) {
if (current < total) {
int nexti = current + 1;
Call nextObj = nexti==markAt ? marker : next;
nextObj.call(env, next, total, nexti, markAt);
} else {
walk(env);
}
}
}
public static Env runTest(Test test, int total, int markAt) {
Env env = new Env(total, markAt, test.debug);
test.call(env, test, total, 0, markAt);
return env;
}
public static void checkTest(Env env, Test test) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + ": Marker called: " + env.markerCalled.get());
System.out.println(threadName + ": Max reached: " + env.maxReached.get());
System.out.println(threadName + ": Frames consumed: " + env.frameCounter.get());
if (env.markerCalled.get() == 0) {
throw new RuntimeException(Thread.currentThread().getName() + ": Marker was not called.");
}
if (env.markerCalled.get() > 1) {
throw new RuntimeException(Thread.currentThread().getName()
+ ": Marker was called more than once: " + env.maxReached.get());
}
if (!env.unexpected.isEmpty()) {
System.out.flush();
System.err.println("Encountered some unexpected infrastructure classes below 'main': "
+ env.unexpected);
}
if (env.maxReached.get() == 0) {
throw new RuntimeException(Thread.currentThread().getName()
+ ": max not reached");
}
if (env.maxReached.get() > 1) {
throw new RuntimeException(Thread.currentThread().getName()
+ ": max was reached more than once: " + env.maxReached.get());
}
}
static class WalkThread extends Thread {
final static AtomicLong walkersCount = new AtomicLong();
Throwable failed = null;
final Test test;
public WalkThread(Test test) {
super("WalkThread[" + walkersCount.incrementAndGet() + ", type="
+ test.getWalkType() + "]");
this.test = test;
}
public void run() {
try {
Env env = runTest(test, 1000, 10);
//waitWalkers(env);
checkTest(env, test);
} catch(Throwable t) {
failed = t;
}
}
}
public static void main(String[] args) throws Throwable {
WalkThread[] threads = new WalkThread[Call.WalkType.values().length*3];
Throwable failed = null;
for (int i=0; i<threads.length; i++) {
Test test = new Test(Call.WalkType.values()[i%Call.WalkType.values().length]);
threads[i] = new WalkThread(test);
}
for (int i=0; i<threads.length; i++) {
threads[i].start();
}
for (int i=0; i<threads.length; i++) {
threads[i].join();
if (failed == null) failed = threads[i].failed;
else if (threads[i].failed == null) {
failed.addSuppressed(threads[i].failed);
}
}
if (failed != null) {
throw failed;
}
}
}

View file

@ -0,0 +1,78 @@
/*
* 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 8157892 8157977
* @summary Verify file name, line number and bci of native methods
* @run main/othervm/native -Xcheck:jni NativeMethod
*/
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.util.List;
import java.util.stream.Collectors;
public class NativeMethod {
public static void main(String... args) throws Exception {
new NativeMethod().test();
}
private final StackWalker walker;
NativeMethod() {
this.walker = StackWalker.getInstance(Option.SHOW_REFLECT_FRAMES);
}
// this native method will invoke NativeMethod::walk method
public native void test();
public void walk() {
List<StackFrame> nativeFrames = walker.walk(s ->
s.filter(StackFrame::isNativeMethod)
.collect(Collectors.toList())
);
assertTrue(nativeFrames.size() > 0, "native frame not found");
// find NativeMethod::test native frame
nativeFrames.stream().filter(f -> f.isNativeMethod()
&& f.getClassName().equals("NativeMethod")
&& f.getMethodName().equals("test"))
.findFirst()
.orElseThrow(() -> new RuntimeException("NativeMethod::test native method not found"));
for (StackFrame f : nativeFrames) {
assertTrue(f.getFileName() != null, "source file expected to be found");
assertTrue(f.getLineNumber() < 0, "line number expected to be unavailable");
assertTrue(f.getByteCodeIndex() < 0, "bci expected to be unavailable");
}
}
private static void assertTrue(boolean value, String msg) {
if (value != true)
throw new AssertionError(msg);
}
static {
System.loadLibrary("nativeMethod");
}
}

View file

@ -0,0 +1,877 @@
/*
* Copyright (c) 2017, 2022, 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 8173898
* @summary Basic test for checking filtering of reflection frames
* @run testng ReflectionFrames
*/
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.StackWalker.Option.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class ReflectionFrames {
final static boolean verbose = false;
final static Class<?> REFLECT_ACCESS = findClass("java.lang.reflect.ReflectAccess");
final static Class<?> REFLECTION_FACTORY = findClass("jdk.internal.reflect.ReflectionFactory");
private static Class<?> findClass(String cn) {
try {
return Class.forName(cn);
} catch (ClassNotFoundException e) {
throw new AssertionError(e);
}
}
/**
* This test invokes new StackInspector() directly from
* the caller StackInspector.Caller.create method.
* It checks that the caller is StackInspector.Caller.
* It also checks the expected frames collected
* by walking the stack from the default StackInspector()
* constructor.
* This is done twice, once using a default StackWalker
* that hides reflection frames, once using a StackWalker
* configured to show reflection frames.
*/
@Test
public static void testNewStackInspector() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
// Calls the StackInspector.create method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The create method invokes new StackInspector() directly.
// No reflection frame should appear.
System.out.println("testNewStackInspector: create");
StackInspector obj = ((StackInspector)StackInspector.Caller.class
.getMethod("create", How.class)
.invoke(null, How.NEW));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
ReflectionFrames.class.getName()
+"::testNewStackInspector"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertEquals(obj.filtered, 0);
// Calls the StackInspector.reflect method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The reflect method invokes the create method through
// reflection.
// The create method invokes new StackInspector() directly.
// No reflection frame should appear.
System.out.println("testNewStackInspector: reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("reflect", How.class)
.invoke(null, How.NEW));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
StackInspector.Caller.class.getName()
+"::reflect",
ReflectionFrames.class.getName()
+"::testNewStackInspector"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertEquals(obj.filtered, 0);
// Calls the StackInspector.handle method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The handle method invokes the create method using
// a MethodHandle.
// The create method invokes new StackInspector() directly.
// No reflection frame should appear.
System.out.println("testNewStackInspector: handle");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("handle", How.class)
.invoke(null, How.NEW));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
StackInspector.Caller.class.getName()
+"::handle",
ReflectionFrames.class.getName()
+"::testNewStackInspector"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertEquals(obj.filtered, 0);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
// Calls the StackInspector.create method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The create method invokes new StackInspector() directly.
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testNewStackInspector: create: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("create", How.class)
.invoke(null, How.NEW));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testNewStackInspector"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertNotEquals(obj.filtered, 0);
// Calls the StackInspector.reflect method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The reflect method invokes the create method through
// reflection.
// The create method invokes new StackInspector() directly.
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testNewStackInspector: reflect: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("reflect", How.class)
.invoke(null, How.NEW));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
Method.class.getName()
+"::invoke",
StackInspector.Caller.class.getName()
+"::reflect",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testNewStackInspector"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertNotEquals(obj.filtered, 0);
// Calls the StackInspector.handle method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The handle method invokes the create method using
// MethodHandle.
// The create method invokes new StackInspector() directly.
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testNewStackInspector: handle: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("handle", How.class)
.invoke(null, How.NEW));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
// MethodHandle::invoke remains hidden
StackInspector.Caller.class.getName()
+"::handle",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testNewStackInspector"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertNotEquals(obj.filtered, 0);
}
/**
* This test invokes Constructor.newInstance() from
* the caller StackInspector.Caller.create method.
* It checks that the caller is StackInspector.Caller.
* It also checks the expected frames collected
* by walking the stack from the default StackInspector()
* constructor.
* This is done twice, once using a default StackWalker
* that hides reflection frames, once using a StackWalker
* configured to show reflection frames.
*/
@Test
public static void testConstructor() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
// Calls the StackInspector.create method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The create method invokes Constructor.newInstance().
// No reflection frame should appear.
System.out.println("testConstructor: create");
StackInspector obj = ((StackInspector)StackInspector.Caller.class
.getMethod("create", How.class)
.invoke(null, How.CONSTRUCTOR));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
ReflectionFrames.class.getName()
+"::testConstructor"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertEquals(obj.filtered, 0);
// Calls the StackInspector.reflect method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The reflect method invokes the create method through
// reflection.
// The create method invokes Constructor.newInstance().
// No reflection frame should appear.
System.out.println("testConstructor: reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("reflect", How.class)
.invoke(null, How.CONSTRUCTOR));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
StackInspector.Caller.class.getName()
+"::reflect",
ReflectionFrames.class.getName()
+"::testConstructor"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertEquals(obj.filtered, 0);
// Calls the StackInspector.handle method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The handle method invokes the create method using
// MethodHandle.
// The create method invokes Constructor.newInstance().
// No reflection frame should appear.
System.out.println("testConstructor: handle");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("handle", How.class)
.invoke(null, How.CONSTRUCTOR));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
StackInspector.Caller.class.getName()
+"::create",
StackInspector.Caller.class.getName()
+"::handle",
ReflectionFrames.class.getName()
+"::testConstructor"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertEquals(obj.filtered, 0);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
// Calls the StackInspector.create method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The create method invokes Constructor.newInstance().
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testConstructor: create: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("create", How.class)
.invoke(null, How.CONSTRUCTOR));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
Constructor.class.getName()
+"::newInstanceWithCaller",
Constructor.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testConstructor"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertNotEquals(obj.filtered, 0);
// Calls the StackInspector.reflect method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The reflect method invokes the create method through
// reflection.
// The create method invokes Constructor.newInstance().
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testConstructor: reflect: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("reflect", How.class)
.invoke(null, How.CONSTRUCTOR));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
Constructor.class.getName()
+"::newInstanceWithCaller",
Constructor.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
Method.class.getName()
+"::invoke",
StackInspector.Caller.class.getName()
+"::reflect",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testConstructor"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertNotEquals(obj.filtered, 0);
// Calls the StackInspector.handle method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The handle method invokes the create method using
// MethodHandle.
// The create method invokes Constructor.newInstance().
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testConstructor: handle: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("handle", How.class)
.invoke(null, How.CONSTRUCTOR));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
Constructor.class.getName()
+"::newInstanceWithCaller",
Constructor.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
// MethodHandle::invoke remains hidden
StackInspector.Caller.class.getName()
+"::handle",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testConstructor"));
assertEquals(obj.cls, StackInspector.Caller.class);
assertNotEquals(obj.filtered, 0);
}
/**
* This test invokes StackInspector.class.newInstance() from
* the caller StackInspector.Caller.create method. Because
* Class.newInstance() is not considered as a
* reflection frame, the caller returned by
* getCallerClass() should appear to be java.lang.Class
* and not StackInspector.Caller.
* It also checks the expected frames collected
* by walking the stack from the default StackInspector()
* constructor.
* This is done twice, once using a default StackWalker
* that hides reflection frames, once using a StackWalker
* configured to show reflection frames.
*/
@Test
public static void testNewInstance() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
// Calls the StackInspector.create method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The create method invokes StackInspector.class.newInstance().
// No reflection frame should appear, except
// Class::newInstance which is not considered as
// a reflection frame.
System.out.println("testNewInstance: create");
StackInspector obj = ((StackInspector)StackInspector.Caller.class
.getMethod("create", How.class)
.invoke(null, How.CLASS));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
REFLECT_ACCESS.getName()
+"::newInstance",
REFLECTION_FACTORY.getName()
+"::newInstance",
Class.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
ReflectionFrames.class.getName()
+"::testNewInstance"));
// Because implementation frames are not filtered, then the
// caller is ReflectAccess.class
assertEquals(obj.cls, REFLECT_ACCESS);
assertEquals(obj.filtered, 0);
// Calls the StackInspector.reflect method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The reflect method invokes the create method through
// reflection.
// The create method invokes StackInspector.class.newInstance().
// No reflection frame should appear, except
// Class::newInstance which is not considered as
// a reflection frame.
System.out.println("testNewInstance: reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("reflect", How.class)
.invoke(null, How.CLASS));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
REFLECT_ACCESS.getName()
+"::newInstance",
REFLECTION_FACTORY.getName()
+"::newInstance",
Class.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
StackInspector.Caller.class.getName()
+"::reflect",
ReflectionFrames.class.getName()
+"::testNewInstance"));
// Because implementation frames are not filtered, then the
// caller is ReflectAccess.class
assertEquals(obj.cls, REFLECT_ACCESS);
assertEquals(obj.filtered, 0);
// Calls the StackInspector.handle method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The handle method invokes the create method using
// reflection.
// The create method invokes StackInspector.class.newInstance().
// No reflection frame should appear, except
// Class::newInstance which is not considered as
// a reflection frame.
System.out.println("testNewInstance: handle");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("handle", How.class)
.invoke(null, How.CLASS));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
REFLECT_ACCESS.getName()
+"::newInstance",
REFLECTION_FACTORY.getName()
+"::newInstance",
Class.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
StackInspector.Caller.class.getName()
+"::handle",
ReflectionFrames.class.getName()
+"::testNewInstance"));
// Because implementation frames are not filtered, then the
// caller is ReflectAccess.class
assertEquals(obj.cls, REFLECT_ACCESS);
assertEquals(obj.filtered, 0);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
// Calls the StackInspector.create method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The create method invokes StackInspector.class.newInstance().
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testNewInstance: create: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("create", How.class)
.invoke(null, How.CLASS));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
Constructor.class.getName()
+"::newInstanceWithCaller",
REFLECT_ACCESS.getName()
+"::newInstance",
Class.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testNewInstance"));
// Because implementation frames are not filtered, then the
// caller is ReflectAccess.class
assertEquals(obj.cls, REFLECT_ACCESS);
assertNotEquals(obj.filtered, 0);
// Calls the StackInspector.reflect method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The reflect method invokes the create method through
// reflection.
// The create method invokes StackInspector.class.newInstance().
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testNewInstance: reflect: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("reflect", How.class)
.invoke(null, How.CLASS));
System.out.println(obj.collectedFrames);
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
Constructor.class.getName()
+"::newInstanceWithCaller",
REFLECT_ACCESS.getName()
+"::newInstance",
Class.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
Method.class.getName()
+"::invoke",
StackInspector.Caller.class.getName()
+"::reflect",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testNewInstance"));
// Because implementation frames are not filtered, then the
// caller is ReflectAccess.class
assertEquals(obj.cls, REFLECT_ACCESS);
assertNotEquals(obj.filtered, 0);
// Calls the StackInspector.handle method through reflection
// and check the frames collected in the StackInspector
// default constructor.
// The handle method invokes the create method using
// MethodHandle.
// The create method invokes StackInspector.class.newInstance().
// We should see all reflection frames, except the
// jdk.internal.reflect frames which we are filtering
// out in StackInspector::filter.
System.out.println("testNewInstance: handle: show reflect");
obj = ((StackInspector)StackInspector.Caller.class
.getMethod("handle", How.class)
.invoke(null, How.CLASS));
assertEquals(obj.collectedFrames,
List.of(StackInspector.class.getName()
+"::<init>",
Constructor.class.getName()
+"::newInstanceWithCaller",
REFLECT_ACCESS.getName()
+"::newInstance",
Class.class.getName()
+"::newInstance",
StackInspector.Caller.class.getName()
+"::create",
// MethodHandle::invoke remains hidden
StackInspector.Caller.class.getName()
+"::handle",
Method.class.getName()
+"::invoke",
ReflectionFrames.class.getName()
+"::testNewInstance"));
// Because implementation frames are not filtered, then the
// caller is ReflectAccess.class
assertEquals(obj.cls, REFLECT_ACCESS);
assertNotEquals(obj.filtered, 0);
}
@Test
public static void testGetCaller() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
assertEquals(StackInspector.getCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("getCaller").invoke(null),
ReflectionFrames.class);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
assertEquals(StackInspector.getCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("getCaller").invoke(null),
ReflectionFrames.class);
}
@Test
public static void testReflectCaller() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
assertEquals(StackInspector.reflectCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("reflectCaller").invoke(null),
ReflectionFrames.class);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
assertEquals(StackInspector.reflectCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("reflectCaller").invoke(null),
ReflectionFrames.class);
}
@Test
public static void testSupplyCaller() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
assertEquals(StackInspector.supplyCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("supplyCaller").invoke(null),
ReflectionFrames.class);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
assertEquals(StackInspector.supplyCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("supplyCaller").invoke(null),
ReflectionFrames.class);
}
@Test
public static void testHandleCaller() throws Exception {
// Sets the default walker which hides reflection
// frames.
StackInspector.walker.set(StackInspector.walkerHide);
assertEquals(StackInspector.handleCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("handleCaller").invoke(null),
ReflectionFrames.class);
// Sets a non-default walker configured to show
// reflection frames
StackInspector.walker.set(StackInspector.walkerShow);
assertEquals(StackInspector.handleCaller(), ReflectionFrames.class);
assertEquals(StackInspector.class.getMethod("handleCaller").invoke(null),
ReflectionFrames.class);
}
static enum How { NEW, CONSTRUCTOR, CLASS};
/**
* An object that collect stack frames by walking the stack
* (and calling getCallerClass()) from within its constructor.
* For the purpose of this test, StackInspector objects are
* always created from the nested StackInspector.Caller class,
* which should therefore appear as the caller of the
* StackInspector constructor.
*/
static class StackInspector {
static final StackWalker walkerHide =
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
static final StackWalker walkerShow =
StackWalker.getInstance(EnumSet.of(
StackWalker.Option.RETAIN_CLASS_REFERENCE,
StackWalker.Option.SHOW_REFLECT_FRAMES));
final static ThreadLocal<StackWalker> walker = new ThreadLocal<>() {
protected StackWalker initialValue() {
return walkerHide;
}
};
List<String> collectedFrames;
Class<?> cls = null;
boolean stop;
int filtered;
final boolean filterImplFrames;
public StackInspector() {
stop = false;
// if reflection frames are not hidden, we want to
// filter implementation frames before collecting
// to avoid depending on internal details.
filterImplFrames = walker.get() == walkerShow;
collectedFrames = walker.get().walk(this::parse);
cls = walker.get().getCallerClass();
}
public List<String> collectedFrames() {
return collectedFrames;
}
// The takeWhile method arrange for stopping frame collection
// as soon as a frame from ReflectionFrames.class is reached.
// The first such frame encountered is still included in the
// collected frames, but collection stops right after.
// This makes it possible to filter out anything above the
// the test method frame, such as frames from the test
// framework.
public boolean takeWhile(StackFrame f) {
if (stop) return false;
if (verbose) System.out.println(" " + f);
stop = stop || f.getDeclaringClass() == ReflectionFrames.class;
return true;
}
// filter out implementation frames to avoid depending
// on implementation details. If present, Class::newInstance,
// Method::invoke and Constructor::newInstance will
// still appear in the collected frames, which is
// sufficient for the purpose of the test.
// In the case where the StackWalker itself is supposed to
// filter the reflection frames, then this filter will always
// return true. This way, if such a reflection frame appears when
// it sjould have been filtered by StackWalker, it will make the
// test fail.
public boolean filter(StackFrame f) {
if (filterImplFrames &&
f.getClassName().startsWith("jdk.internal.reflect.")) {
filtered++;
return false;
}
if (!verbose) System.out.println(" " + f);
return true;
}
public String frame(StackFrame f) {
return f.getClassName() + "::" + f.getMethodName();
}
List<String> parse(Stream<StackFrame> s) {
return s.takeWhile(this::takeWhile)
.filter(this::filter)
.map(this::frame)
.collect(Collectors.toList());
}
/**
* The Caller class is used to create instances of
* StackInspector, either direcltly, or throug reflection.
*/
public static class Caller {
public static StackInspector create(How how) throws Exception {
switch(how) {
case NEW: return new StackInspector();
case CONSTRUCTOR: return StackInspector.class
.getConstructor().newInstance();
case CLASS: return StackInspector.class.newInstance();
default: throw new AssertionError(String.valueOf(how));
}
}
public static StackInspector reflect(How how) throws Exception {
return (StackInspector) Caller.class.getMethod("create", How.class)
.invoke(null, how);
}
public static StackInspector handle(How how) throws Exception {
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findStatic(Caller.class, "create",
MethodType.methodType(StackInspector.class, How.class));
try {
return (StackInspector) mh.invoke(how);
} catch (Error | Exception x) {
throw x;
} catch(Throwable t) {
throw new AssertionError(t);
}
}
}
public static Class<?> getCaller() throws Exception {
return walker.get().getCallerClass();
}
public static Class<?> reflectCaller() throws Exception {
return (Class<?>)StackWalker.class.getMethod("getCallerClass")
.invoke(walker.get());
}
public static Class<?> supplyCaller() throws Exception {
return ((Supplier<Class<?>>)StackInspector.walker.get()::getCallerClass).get();
}
public static Class<?> handleCaller() throws Exception {
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(StackWalker.class, "getCallerClass",
MethodType.methodType(Class.class));
try {
return (Class<?>) mh.invoke(walker.get());
} catch (Error | Exception x) {
throw x;
} catch(Throwable t) {
throw new AssertionError(t);
}
}
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2015, 2023, 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 8140450 8268829
* @summary Sanity test for exception cases
* @run junit SanityTest
*/
import java.util.Collections;
import java.util.Set;
import java.util.stream.Stream;
import static java.lang.StackWalker.Option.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class SanityTest {
@Test
public void testNPE() {
assertThrows(NullPointerException.class, () ->
StackWalker.getInstance((Set<StackWalker.Option>) null));
assertThrows(NullPointerException.class, () ->
StackWalker.getInstance((StackWalker.Option) null));
}
private static Stream<StackWalker> noRetainClassRef() {
return Stream.of(StackWalker.getInstance(), StackWalker.getInstance(DROP_METHOD_INFO));
}
@ParameterizedTest
@MethodSource("noRetainClassRef")
public void testUOE(StackWalker sw) {
assertThrows(UnsupportedOperationException.class, () -> sw.getCallerClass());
}
@Test
public void testInvalidEstimateDepth() {
assertThrows(IllegalArgumentException.class, () ->
StackWalker.getInstance(Collections.emptySet(), 0));
}
@Test
public void testNullFunction() {
assertThrows(NullPointerException.class, () ->
StackWalker.getInstance().walk(null));
}
@Test
public void testNullConsumer() {
assertThrows(NullPointerException.class, () ->
StackWalker.getInstance().forEach(null));
}
@ParameterizedTest
@MethodSource("noRetainClassRef")
public void testUOEFromGetDeclaringClass(StackWalker sw) {
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getDeclaringClass));
}
@ParameterizedTest
@MethodSource("noRetainClassRef")
public void testUOEFromGetMethodType(StackWalker sw) {
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getMethodType));
}
private static Stream<StackWalker> noMethodInfo() {
return Stream.of(StackWalker.getInstance(DROP_METHOD_INFO),
StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE)));
}
@ParameterizedTest
@MethodSource("noMethodInfo")
public void testNoMethodInfo(StackWalker sw) {
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getMethodName));
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getMethodType));
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getDescriptor));
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getByteCodeIndex));
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::getFileName));
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::isNativeMethod));
assertThrows(UnsupportedOperationException.class, () ->
sw.forEach(StackWalker.StackFrame::toStackTraceElement));
}
}

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2015, 2023, 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.
*/
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.util.*;
/**
* Utility class for recording a stack trace for later comparison to
* StackWalker results.
*
* StackTraceElement comparison does not include line number, isNativeMethod
*/
public class StackRecorderUtil implements Iterable<StackRecorderUtil.TestFrame> {
private List<TestFrame> testFrames = new LinkedList();
private boolean compareClasses;
private boolean compareClassNames = true;
private boolean compareMethodNames = true;
private boolean compareSTEs;
public StackRecorderUtil(Set<StackWalker.Option> swOptions) {
compareClasses = swOptions.contains(Option.RETAIN_CLASS_REFERENCE);
compareSTEs = true;
}
/**
* Add a method call to this recorded stack.
*/
public void add(Class declaringClass, String methodName, String fileName) {
testFrames.add(0, new TestFrame(declaringClass, methodName, fileName));
}
public int frameCount() { return testFrames.size(); }
/**
* Compare the given StackFrame returned from the StackWalker to the
* recorded frame at the given index.
*
* Tests for equality, as well as functional correctness with respect to
* the StackWalker's options (e.g. throws or doesn't throw exceptions)
*/
public void compareFrame(int index, StackFrame sf) {
TestFrame tf = testFrames.get(index);
if (compareClasses) {
if (!tf.declaringClass.equals(sf.getDeclaringClass())) {
throw new RuntimeException("Expected class: " +
tf.declaringClass.toString() + ", but got: " +
sf.getDeclaringClass().toString() + ", index: " + index);
}
} else {
boolean caught = false;
try {
sf.getDeclaringClass();
} catch (UnsupportedOperationException e) {
caught = true;
}
if (!caught) {
throw new RuntimeException("StackWalker did not have " +
"RETAIN_CLASS_REFERENCE Option, but did not throw " +
"UnsupportedOperationException");
}
}
if (compareClassNames && !tf.className().equals(sf.getClassName())) {
throw new RuntimeException("Expected class name: " + tf.className() +
", but got: " + sf.getClassName() + ", index: " + index);
}
if (compareMethodNames && !tf.methodName.equals(sf.getMethodName())) {
throw new RuntimeException("Expected method name: " + tf.methodName +
", but got: " + sf.getMethodName() + ", index: " + index);
}
if (compareSTEs) {
StackTraceElement ste = sf.toStackTraceElement();
if (!(ste.getClassName().equals(tf.className()) &&
ste.getMethodName().equals(tf.methodName)) &&
ste.getFileName().equals(tf.fileName)) {
throw new RuntimeException("Expected StackTraceElement info: " +
tf + ", but got: " + ste);
}
if (!Objects.equals(ste.getClassName(), sf.getClassName())
|| !Objects.equals(ste.getMethodName(), sf.getMethodName())
|| !Objects.equals(ste.getFileName(), sf.getFileName())
|| !Objects.equals(ste.getLineNumber(), sf.getLineNumber())
|| !Objects.equals(ste.isNativeMethod(), sf.isNativeMethod())) {
throw new RuntimeException("StackFrame and StackTraceElement differ: " +
"sf=" + sf + ", ste=" + ste);
}
}
}
public Iterator<TestFrame> iterator() {
return testFrames.iterator();
}
/**
* Class used to record stack frame information.
*/
public static class TestFrame {
public Class declaringClass;
public String methodName;
public String fileName = null;
public TestFrame (Class declaringClass, String methodName, String fileName) {
this.declaringClass = declaringClass;
this.methodName = methodName;
this.fileName = fileName;
}
public String className() {
return declaringClass.getName();
}
public String toString() {
return "TestFrame: " + className() + "." + methodName +
(fileName == null ? "" : "(" + fileName + ")");
}
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2015, 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 8020968
* @summary Basic test for Stream<StackFrame> state
* @run main StackStreamState
*/
import java.lang.StackWalker.StackFrame;
import java.util.stream.Stream;
public class StackStreamState {
public static void main(String... args) {
StackStreamState test = new StackStreamState();
test.testStatic();
test.testInstance();
test.testLocal();
}
private static Stream<StackFrame> staticStream;
private Stream<StackFrame> instanceStream;
private final StackWalker walker = StackWalker.getInstance();
void testStatic() {
walker.walk(s -> {
staticStream = s;
return null;
});
checkStreamState(staticStream);
}
void testInstance() {
walker.walk(s -> {
instanceStream = s;
return null;
});
checkStreamState(instanceStream);
}
void testLocal() {
Stream<StackFrame> stream = walker.walk(s -> {
return s;
});
checkStreamState(stream);
}
void checkStreamState(Stream<StackFrame> stream) {
try {
stream.count();
throw new RuntimeException("IllegalStateException not thrown");
} catch (IllegalStateException e) {
System.out.println("Got expected IllegalStateException: " + e.getMessage());
e.printStackTrace(System.out);
}
}
}

View file

@ -0,0 +1,306 @@
/*
* Copyright (c) 2015, 2023, 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.
*/
import java.lang.StackWalker.StackFrame;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import static java.lang.StackWalker.Option.*;
/**
* @test
* @bug 8140450 8210375
* @summary Stack Stream Test
* @modules java.logging
* @run main/othervm StackStreamTest
*/
public class StackStreamTest {
public static void main(String[] argv) throws Exception {
new StackStreamTest().test();
}
private static Logger logger = Logger.getLogger("stackstream");
public StackStreamTest() {
}
public void test() {
A.a();
}
static class A {
public static void a() {
B.b();
}
}
static class B {
public static void b() {
C.c();
}
}
static class C {
public static void c() {
D.d();
}
}
static class D {
public static void d() {
E.e();
}
}
static class E {
public static void e() {
F.f();
}
}
static class F {
public static void f() {
logger.severe("log message");
G.g();
new K().k();
}
}
private static boolean isTestClass(StackFrame f) {
// Filter jtreg frames from the end of the stack
return f.getClassName().startsWith("StackStreamTest");
}
static class G {
static StackWalker STE_WALKER = StackWalker.getInstance();
static StackWalker DEFAULT_WALKER = StackWalker.getInstance();
private static final List<String> GOLDEN_CLASS_NAMES =
Arrays.asList("StackStreamTest$G",
"StackStreamTest$F",
"StackStreamTest$E",
"StackStreamTest$D",
"StackStreamTest$C",
"StackStreamTest$B",
"StackStreamTest$A",
"StackStreamTest",
"StackStreamTest");
private static final List<String> GOLDEN_METHOD_NAMES =
Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main");
public static void g() {
System.out.println("Thread dump");
Thread.dumpStack();
caller();
firstFrame();
// Check class names
System.out.println("check class names");
List<String> sfs = DEFAULT_WALKER.walk(s -> {
return s.filter(StackStreamTest::isTestClass)
.map(StackFrame::getClassName)
.collect(Collectors.toList());
});
equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES);
// Check method names
System.out.println("methodNames()");
sfs = DEFAULT_WALKER.walk(s -> {
return s.filter(StackStreamTest::isTestClass)
.map(StackFrame::getMethodName)
.collect(Collectors.toList());}
);
equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES);
Exception exc = new Exception("G.g stack");
exc.printStackTrace();
System.out.println("Stream of StackTraceElement");
StackWalker.getInstance()
.walk(s ->
{
s.map(StackFrame::toStackTraceElement)
.forEach(ste -> System.out.println("STE: " + ste));
return null;
});
// Do we need this?
System.out.println("Collect StackTraceElement");
List<StackTraceElement> stacktrace = STE_WALKER.walk(s ->
{
// Filter out jtreg frames
return s.filter(StackStreamTest::isTestClass)
.collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList()));
});
int i=0;
for (StackTraceElement s : stacktrace) {
System.out.format(" %d: %s%n", i++, s);
}
// Check STEs for correctness
checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
System.out.println("Collect classes");
List<Class<?>> classes = StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE))
.walk(s -> {
return s.map(StackFrame::getDeclaringClass).collect(Collectors.toList());
});
for (i=0; i < GOLDEN_CLASS_NAMES.size(); i++) {
Class<?> c = classes.get(i);
if (!GOLDEN_CLASS_NAMES.get(i).equals(c.getName())) {
throw new RuntimeException("unexpected class at " + i + " " + c.getName() +
" expected " + GOLDEN_CLASS_NAMES.get(i));
}
}
}
static void checkStackTraceElements(List<String> classNames,
List<String> methodNames,
List<StackTraceElement> stes) {
if (classNames.size() != methodNames.size() ) {
throw new RuntimeException("Test error: classNames and methodNames should be same size");
}
if (classNames.size() != stes.size()) {
dumpSTEInfo(classNames, methodNames, stes);
throw new RuntimeException("wrong number of elements in stes");
}
for (int i = 0; i < classNames.size() ; i++) {
if (!classNames.get(i).equals(stes.get(i).getClassName()) ||
!methodNames.get(i).equals(stes.get(i).getMethodName())) {
dumpSTEInfo(classNames, methodNames, stes);
throw new RuntimeException("class & method names don't match");
}
}
}
static void dumpSTEInfo(List<String> classNames, List<String> methodNames,
List<StackTraceElement> stes) {
System.out.println("Observed class, method names:");
for (StackTraceElement ste : stes) {
System.out.println(" " + ste.getClassName() + ", " + ste.getMethodName());
}
System.out.println("Expected class, method names:");
for (int i = 0; i < classNames.size(); i++) {
System.out.println(" " + classNames.get(i) + ", " + methodNames.get(i));
}
}
static void firstFrame() {
System.out.println("first frame()");
StackWalker sw = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
sw.forEach(e -> {
System.out.println(e.getClassName() + "," + e.getMethodName());
});
System.out.println("\n");
Optional<StackFrame> frame = sw.walk(s ->
{
return s.filter(e -> {
System.err.println(e.getClassName() + " == " +
e.getClassName().equals("StackStreamTest"));
return e.getClassName().equals("StackStreamTest");
}).findFirst();
});
Class<?> c = frame.get().getDeclaringClass();
System.out.println("\nfirst frame: " + c);
if (c != StackStreamTest.class) {
throw new RuntimeException("Unexpected first caller class " + c);
}
}
}
private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) {
System.out.println("List: " + list);
System.out.println("Expected: " + expected);
if (!list.equals(expected)) {
System.err.println("Observed " + label);
for (T s1 : list) {
System.out.println(" " + s1);
}
System.err.println("Expected " + label);
for (T s2 : expected) {
System.out.println(" " + s2);
}
throw new RuntimeException("Error with " + label);
}
}
static class K {
void k() {
k1();
}
void k1() {
k2();
}
void k2() {
k3();
}
void k3() {
k4();
}
void k4() {
k5();
}
void k5() {
k6();
}
void k6() {
k7();
}
void k7() {
k8();
}
void k8() {
k9();
}
void k9() {
k10();
}
void k10() {
k20();
}
void k20() {
new Caller().test();
}
class Caller {
void test() {
Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
System.out.println("\nTesting K class : " + c);
Thread.dumpStack();
if (c != K.class) {
throw new RuntimeException("Unexpected caller class "+ c);
}
}
}
}
static void caller() {
Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
System.out.println("\ncaller class : " + c);
if (c != G.class) {
throw new RuntimeException("Unexpected caller class "+ c);
}
}
}

View file

@ -0,0 +1,352 @@
/*
* Copyright (c) 2015, 2025, 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.
*/
import static java.lang.StackWalker.Option.*;
import java.lang.StackWalker.StackFrame;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import jdk.test.lib.RandomFactory;
/**
* @test
* @bug 8140450
* @summary Stack Walk Test (use -Dseed=X to set PRNG seed)
* @requires test.thread.factory == null
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @compile StackRecorderUtil.java
* @run main/othervm StackWalkTest
* @run main/othervm StackWalkTest -random:50
* @author danielfuchs, bchristi
* @key randomness
*/
public class StackWalkTest {
private static boolean random = false;
private static boolean verbose = false;
private static int randomRuns = 50;
private static final int MAX_RANDOM_DEPTH = 1000;
static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
"jdk.internal.reflect.DirectMethodHandleAccessor",
"java.lang.reflect.Method",
"com.sun.javatest.regtest.MainWrapper$MainThread",
"com.sun.javatest.regtest.agent.MainWrapper$MainThread",
"com.sun.javatest.regtest.agent.MainWrapper$MainTask",
"java.lang.Thread"
));
static final List<Class<?>> streamPipelines = Arrays.asList(
classForName("java.util.stream.AbstractPipeline"),
classForName("java.util.stream.TerminalOp")
);
static Class<?> classForName(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
}
private static boolean isStreamPipeline(Class<?> clazz) {
for (Class<?> c : streamPipelines) {
if (c.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
StackRecorderUtil recorder;
int count = 0;
boolean didWalk = false;
final int estDepth;
final Set<StackWalker.Option> swOptions;
public StackWalkTest() {
this(EnumSet.noneOf(StackWalker.Option.class), -1);
}
public StackWalkTest(Set<StackWalker.Option> swOptions) {
this(swOptions, -1);
}
public StackWalkTest(int estimatedDepth) {
this(EnumSet.noneOf(StackWalker.Option.class), -1);
}
public StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth) {
this.swOptions = swOptions;
this.estDepth = estimatedDepth;
}
private StackWalker createStackWalker() {
// test all StackWalker factory methods
if (this.estDepth < 0) {
if (swOptions.isEmpty()) {
return StackWalker.getInstance();
} else {
return StackWalker.getInstance(swOptions);
}
}
return StackWalker.getInstance(swOptions, estDepth);
}
public void consume(StackFrame sf) {
if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE)
&& isStreamPipeline(sf.getDeclaringClass())) {
return;
}
if (verbose) {
System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName());
}
if (count >= recorder.frameCount()) {
// We've gone past main()...
if (infrastructureClasses.contains(sf.getClassName())) {
// safe to ignore
return;
}
}
try {
recorder.compareFrame(count, sf);
} catch (IndexOutOfBoundsException e) {
// Extra non-infra frame in stream
throw new RuntimeException("extra non-infra stack frame at count "
+ count + ": <" + sf + ">", e);
}
count++;
}
public class Call {
public void walk(int total, int markAt) {
recorder.add(Call.class, "walk", "StackWalkTest.java");
long swFrameCount = createStackWalker().walk(s -> s.count());
if (verbose) {
System.out.println("Call.walk() total=" + total + ", markAt=" + markAt);
System.out.println("recorder frames:");
for (StackRecorderUtil.TestFrame f : recorder) {
System.out.println("\t" + f.declaringClass + "." + f.methodName);
}
System.out.println("\nStackWalker recorded " + swFrameCount + " frames");
System.out.flush();
}
long recFrameCount = (long)recorder.frameCount();
if (swFrameCount < recFrameCount) {
throw new RuntimeException("StackWalker recorded fewer frames ("+
swFrameCount + ") than recorded ("+ recorder.frameCount() +
") - " + "estimatedDepth set to " + estDepth);
}
if (verbose) {
System.out.println("StackWalker frames:");
}
createStackWalker().forEach(StackWalkTest.this::consume);
didWalk = true;
}
public void call(int total, int current, int markAt) {
recorder.add(Call.class, "call", "StackWalkTest.java");
if (current < total) {
testCall.call(total, current+1, markAt);
} else {
walk(total, markAt);
}
}
}
public class Marker extends Call {
@Override
public void call(int total, int current, int markAt) {
recorder.add(Marker.class, "call", "StackWalkTest.java");
if (current < total) {
testCall.call(total, current+1, markAt);
} else {
walk(total, markAt);
}
}
}
private Call markerCall = new Marker();
public class Test extends Call {
@Override
public void call(int total, int current, int markAt) {
recorder.add(Test.class, "call", "StackWalkTest.java");
if (current < total) {
int nexti = current + 1;
if (nexti==markAt) {
markerCall.call(total, nexti, markAt);
} else {
testCall.call2(total, nexti, markAt);
}
} else {
walk(total, markAt);
}
}
public void call2(int total, int current, int markAt) {
recorder.add(Test.class, "call2", "StackWalkTest.java");
if (current < total) {
int nexti = current + 1;
if (nexti==markAt) {
markerCall.call(total, nexti, markAt);
} else {
test2Call.call(total, nexti, markAt);
}
} else {
walk(total, markAt);
}
}
}
private Test testCall = new Test();
/** Inherits call() from Call */
public class Test2 extends Call {}
private Test2 test2Call = new Test2();
public void runTest(Class callerClass, String callerMethod, int stackDepth,
int markAt) {
if (didWalk) {
throw new IllegalStateException("StackWalkTest already used");
}
// Test may run into StackOverflow when running in -Xcomp mode on deep stack
assert stackDepth <= 1000;
assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth("
+ stackDepth + ")";
System.out.print("runTest(" + swOptions
+ "), estimatedDepth=" + estDepth);
recorder = new StackRecorderUtil(swOptions);
recorder.add(callerClass, callerMethod, "StackWalkTest.java");
recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java");
Test test1 = new Test();
test1.call(stackDepth, 0, markAt);
System.out.println(" finished");
if (!didWalk) {
throw new IllegalStateException("Test wasn't actually performed");
}
}
public static void main(String[] args) {
String rand = "-random";
String randItems = "-random:";
for(String arg : args) {
if (arg.startsWith(rand)) {
random = true;
try {
if(arg.startsWith(randItems)) {
randomRuns = Integer.valueOf(arg.substring(randItems.length()));
}
} catch(NumberFormatException e) {}
} else if("-verbose".equals(arg)) {
verbose = true;
}
}
if (random) {
Random rng = RandomFactory.getRandom();
for (int iters = 0; iters < randomRuns; iters++) {
Set<StackWalker.Option> opts = new HashSet<>();
if (rng.nextBoolean()) {
opts.add(RETAIN_CLASS_REFERENCE);
}
int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH);
StackWalkTest swt;
if (rng.nextBoolean() && depth > 1) {
// Test that specifying an estimatedDepth doesn't prevent
// full stack traversal
swt = new StackWalkTest(opts, 1+rng.nextInt(depth-1));
} else {
swt = new StackWalkTest(opts);
}
int markAt = rng.nextInt(depth+1);
System.out.print(depth + "@" + markAt + " ");
System.out.flush();
swt.runTest(StackWalkTest.class, "main", depth, markAt);
}
} else {
// Long stack, default maxDepth
StackWalkTest swt;
swt = new StackWalkTest();
swt.runTest(StackWalkTest.class, "main", 1000, 10);
// Long stack, matching maxDepth
swt = new StackWalkTest(2000);
swt.runTest(StackWalkTest.class, "main", 1000, 10);
// Long stack, maximum maxDepth
swt = new StackWalkTest(Integer.MAX_VALUE);
swt.runTest(StackWalkTest.class, "main", 1000, 10);
//
// Single batch
//
swt = new StackWalkTest(); // default maxDepth
swt.runTest(StackWalkTest.class, "main", 6, 3);
swt = new StackWalkTest(4); // maxDepth < stack
swt.runTest(StackWalkTest.class, "main", 6, 3);
swt = new StackWalkTest(2); // maxDepth < marker
swt.runTest(StackWalkTest.class, "main", 6, 4);
//
// 2 batches
//
swt = new StackWalkTest(); // default maxDepth
swt.runTest(StackWalkTest.class, "main", 24, 10);
swt = new StackWalkTest(18); // maxDepth < stack
swt.runTest(StackWalkTest.class, "main", 24, 10);
swt = new StackWalkTest(8); // maxDepth < marker
swt.runTest(StackWalkTest.class, "main", 24, 10);
//
// 3 batch
//
swt = new StackWalkTest(); // default maxDepth
swt.runTest(StackWalkTest.class, "main", 60, 20);
swt = new StackWalkTest(35); // maxDepth < stack
swt.runTest(StackWalkTest.class, "main", 60, 20);
swt = new StackWalkTest(8); // maxDepth < marker
swt.runTest(StackWalkTest.class, "main", 60, 20);
//
// StackWalker.Options
//
swt = new StackWalkTest();
swt.runTest(StackWalkTest.class, "main", 50, 10);
swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE));
swt.runTest(StackWalkTest.class, "main", 80, 40);
swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE), 50);
swt.runTest(StackWalkTest.class, "main", 1000, 524);
}
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2016, 2024, 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 8140450
* @summary Basic test for the StackWalker::getByteCodeIndex method
* @run main TestBCI
*/
import java.lang.StackWalker.StackFrame;
import java.io.IOException;
import java.io.InputStream;
import java.lang.classfile.Attributes;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.MethodModel;
import java.lang.constant.MethodTypeDesc;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
public class TestBCI {
public static void main(String... args) throws Exception {
TestBCI test = new TestBCI(Walker.class);
System.out.println("Line number table:");
test.methods.values().stream()
.sorted(Comparator.comparing(MethodInfo::name).reversed())
.forEach(System.out::println);
// walk the stack
test.walk();
}
private final Map<String, MethodInfo> methods;
private final Class<?> clazz;
TestBCI(Class<?> c) throws IllegalArgumentException, IOException {
Map<String, MethodInfo> methods;
String filename = c.getName().replace('.', '/') + ".class";
try (InputStream in = c.getResourceAsStream(filename)) {
var cf = ClassFile.of().parse(in.readAllBytes());
methods = cf.methods().stream()
.map(m -> new MethodInfo(cf, m))
.collect(Collectors.toMap(MethodInfo::name, Function.identity()));
}
this.clazz = c;
this.methods = methods;
}
void walk() {
Walker walker = new Walker();
walker.m1();
}
void verify(StackFrame frame) {
if (frame.getDeclaringClass() != clazz)
return;
int bci = frame.getByteCodeIndex();
int lineNumber = frame.getLineNumber();
System.out.format("%s.%s bci %d (%s:%d)%n",
frame.getClassName(), frame.getMethodName(), bci,
frame.getFileName(), lineNumber);
MethodInfo method = methods.get(frame.getMethodName());
SortedSet<Integer> values = method.findLineNumbers(bci).get();
if (!values.contains(lineNumber)) {
throw new RuntimeException("line number for bci: " + bci + " "
+ lineNumber + " not matched line number table: " + values);
}
}
/*
* BCIs in the execution stack when StackWalker::forEach is invoked
* will cover BCI range in the line number table.
*/
class Walker {
final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
void m1() {
int i = (int)Math.random()+2;
m2(i*2);
}
void m2(int i) {
i++;
m3(i);
}
void m3(int i) {
i++; m4(i++);
}
int m4(int i) {
walker.forEach(TestBCI.this::verify);
return i;
}
}
static class MethodInfo {
final MethodModel method;
final String name;
final MethodTypeDesc desc;
final Map<Integer, SortedSet<Integer>> bciToLineNumbers = new HashMap<>();
MethodInfo(ClassModel cf, MethodModel m) {
this.method = m;
this.name = m.methodName().stringValue();
this.desc = m.methodTypeSymbol();
m.code().orElseThrow(() -> new IllegalArgumentException("Missing Code in " + m))
.findAttribute(Attributes.lineNumberTable())
.orElseThrow(() -> new IllegalArgumentException("Missing LineNumberTable in " + m))
.lineNumbers().forEach(entry ->
bciToLineNumbers.computeIfAbsent(entry.startPc(), _ -> new TreeSet<>())
.add(entry.lineNumber()));
}
String name() {
return name;
}
Optional<SortedSet<Integer>> findLineNumbers(int value) {
return bciToLineNumbers.entrySet().stream()
.sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
.filter(e -> e.getKey().intValue() <= value)
.map(Map.Entry::getValue)
.findFirst();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append(desc.displayDescriptor()).append(" ");
bciToLineNumbers.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> sb.append("bci:").append(entry.getKey()).append(" ")
.append(entry.getValue()).append(" "));
return sb.toString();
}
}
}

View file

@ -0,0 +1,288 @@
/*
* Copyright (c) 2015, 2024, 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.
*/
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.EnumSet;
import java.util.concurrent.atomic.AtomicLong;
import java.lang.StackWalker.StackFrame;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Objects;
import static java.lang.StackWalker.Option.*;
/**
* @test
* @bug 8140450 8197901
* @summary Verify stack trace information obtained with respect to StackWalker
* options, when the stack contains lambdas, method handle invoke
* virtual calls, and reflection.
* @run main/othervm VerifyStackTrace
* @author danielfuchs
*/
public class VerifyStackTrace {
static interface TestCase {
StackWalker walker();
String description();
String expected();
}
static final class TestCase1 implements TestCase {
private final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
private final String description = "StackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE)";
// Note: line numbers and lambda hashes will be erased when
// comparing stack traces. However, the stack may change
// if some methods are being renamed in the code base.
// If the JDKcode base changes and the test fails because of that,
// then after validating that the actual stack trace obtained
// is indeed correct (no frames are skipped that shouldn't)
// then you can cut & paste the <-- actual --> stack printed in the
// test output in here:
private final String expected =
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" +
"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" +
"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" +
"4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" +
"5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" +
"6: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
"7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" +
"8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n";
@Override public StackWalker walker() { return walker;}
@Override public String description() { return description;}
@Override public String expected() { return expected;}
}
static final class TestCase2 implements TestCase {
private final StackWalker walker = StackWalker.getInstance(
EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES));
private final String description = "nStackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
"StackWalker.Option.SHOW_REFLECT_FRAMES)";
// Note: line numbers and lambda hashes will be erased when
// comparing stack traces. However, the stack may change
// if some methods are being renamed in the code base.
// If the JDK code base changes and the test fails because of that,
// then after validating that the actual stack trace obtained
// is indeed correct (no frames are skipped that shouldn't)
// then you can cut & paste the <-- actual --> stack printed in the
// test output in here (don't forget the final \n):
private final String expected =
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" +
"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" +
"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" +
"4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" +
"5: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:94)\n" +
"6: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
"7: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
"8: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
"9: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
"10: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
@Override public StackWalker walker() { return walker;}
@Override public String description() { return description;}
@Override public String expected() { return expected;}
}
static class TestCase3 implements TestCase {
private final StackWalker walker = StackWalker.getInstance(
EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));
private final String description = "StackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
"StackWalker.Option.SHOW_HIDDEN_FRAMES)";
// Note: line numbers and lambda hashes will be erased when
// comparing stack traces. However, the stack may change
// if some methods are being renamed in the code base.
// If the JDK code base changes and the test fails because of that,
// then after validating that the actual stack trace obtained
// is indeed correct (no frames are skipped that shouldn't)
// then you can cut & paste the <-- actual --> stack printed in the
// test output in here (don't forget the final \n):
private final String expected =
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:280)\n" +
"2: VerifyStackTrace$$Lambda/0x0000000801001848.run(Unknown Source)\n" +
"3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:206)\n" +
"4: java.base/java.lang.invoke.DirectMethodHandle$Holder.invokeVirtual(DirectMethodHandle$Holder)\n" +
"5: java.base/java.lang.invoke.LambdaForm$MH/0x0000000801004800.invoke_MT(LambdaForm$MH)\n" +
"6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:219)\n" +
"7: VerifyStackTrace.invoke(VerifyStackTrace.java:259)\n" +
"8: java.base/java.lang.invoke.LambdaForm$DMH/0x0000000801002000.invokeStatic(LambdaForm$DMH)\n" +
"9: java.base/java.lang.invoke.LambdaForm$MH/0x0000000801003000.invoke(LambdaForm$MH)\n" +
"10: java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)\n" +
"11: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(DirectMethodHandleAccessor.java:211)\n" +
"12: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:122)\n" +
"13: java.base/java.lang.reflect.Method.invoke(Method.java:573)\n" +
"14: VerifyStackTrace$1.run(VerifyStackTrace.java:292)\n" +
"15: java.base/java.security.AccessController.doPrivileged(AccessController.java:312)\n" +
"16: VerifyStackTrace.test(VerifyStackTrace.java:301)\n" +
"17: VerifyStackTrace.main(VerifyStackTrace.java:254)\n";
@Override public StackWalker walker() { return walker;}
@Override public String description() { return description;}
@Override public String expected() { return expected;}
}
static final class TestCase4 extends TestCase3 {
private final StackWalker walker = StackWalker.getInstance(
EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES, SHOW_REFLECT_FRAMES));
private final String description = "StackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
"StackWalker.Option.SHOW_HIDDEN_FRAMES, " +
"StackWalker.Option.SHOW_REFLECT_FRAMES)";
@Override public StackWalker walker() {return walker;}
@Override public String description() {return description;}
}
public static class Handle implements Runnable {
Runnable impl;
public Handle(Runnable run) {
this.impl = run;
}
public void execute(Runnable run) {
run.run();
}
public void run() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = null;
try {
handle = lookup.findVirtual(Handle.class, "execute",
MethodType.methodType(void.class, Runnable.class));
} catch(NoSuchMethodException | IllegalAccessException x) {
throw new RuntimeException(x);
}
try {
handle.invoke(this, impl);
} catch(Error | RuntimeException x) {
throw x;
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
}
static String prepare(String produced, boolean eraseSensitiveInfo) {
if (eraseSensitiveInfo) {
// Erase sensitive information before comparing:
// comparing line numbers is too fragile, so we just erase them
// out before comparing. We also erase the hash-like names of
// synthetic frames introduced by lambdas & method handles
return produced.replaceAll(":[1-9][0-9]*\\)", ":00)")
.replaceAll("/0x[0-9a-f]+\\.run", "/xxxxxxxx.run")
.replaceAll("/0x[0-9a-f]+\\.invoke", "/xxxxxxxx.invoke")
// LFs may or may not be pre-generated, making frames differ
.replaceAll("DirectMethodHandle\\$Holder", "LambdaForm\\$DMH")
.replaceAll("Invokers\\$Holder", "LambdaForm\\$MH")
.replaceAll("MH\\.invoke", "MH/xxxxxxxx.invoke")
// invoke frames may or may not have basic method type
// information encoded for diagnostic purposes
.replaceAll("xx\\.invoke([A-Za-z]*)_[A-Z_]+", "xx.invoke$1")
.replaceAll("\\$[0-9]+", "\\$??");
} else {
return produced;
}
}
public static void main(String[] args) {
test(new TestCase1());
test(new TestCase2());
test(new TestCase3());
test(new TestCase4());
}
public static void invoke(Runnable run) {
run.run();
}
static final class Recorder {
boolean found; // stop recording after main
public void recordSTE(long counter, StringBuilder s, StackFrame f) {
if (found) return;
found = VerifyStackTrace.class.equals(f.getDeclaringClass()) &&
"main".equals(f.getMethodName());
String line = String.format("%d: %s", counter, f.toStackTraceElement());
s.append(line).append('\n');
System.out.println(line);
}
}
static void test(TestCase test) {
System.out.println("\nTesting: " + test.description());
final AtomicLong counter = new AtomicLong();
final StringBuilder builder = new StringBuilder();
final Recorder recorder = new Recorder();
final Runnable run = () -> test.walker().forEach(
f -> recorder.recordSTE(counter.incrementAndGet(), builder, f));
final Handle handle = new Handle(run);
// We're not using lambda on purpose here. We want the anonymous
// class on the stack.
PrivilegedAction<Object> pa = new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
return VerifyStackTrace.class
.getMethod("invoke", Runnable.class)
.invoke(null, handle);
} catch (NoSuchMethodException
| IllegalAccessException
| InvocationTargetException ex) {
System.out.flush();
throw new RuntimeException(ex);
}
}
};
AccessController.doPrivileged(pa);
System.out.println("Main found: " + recorder.found);
if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) {
System.out.flush();
try {
// sleep to make it less likely that System.out & System.err will
// interleave.
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
System.err.println("\nUnexpected stack trace: "
+ "\n<!-- expected -->\n"
+ prepare(test.expected(), true)
+ "\n<-- actual -->\n"
+ prepare(builder.toString(), false));
throw new RuntimeException("Unexpected stack trace for: " + test.description());
}
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2015, 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 8020968
* @summary Sanity test for Function wildcard signature
* @run main WalkFunction
*/
import java.lang.StackWalker.StackFrame;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
public class WalkFunction {
private static final StackWalker walker = StackWalker.getInstance();
public static void main(String... args) throws Exception {
testFunctions();
testWildcards();
walker.walk(counter());
walker.walk(wildCounter());
}
private static void testFunctions() {
walker.walk(Stream::count);
try {
walker.walk(null);
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
Optional<StackFrame> result = walker.walk(WalkFunction::reduce);
if (!result.get().getClassName().equals(WalkFunction.class.getName())) {
throw new RuntimeException(result.get() + " expected: " + WalkFunction.class.getName());
}
}
static Optional<StackFrame> reduce(Stream<StackFrame> stream) {
return stream.reduce((r,f) -> r.getClassName().compareTo(f.getClassName()) > 0 ? f : r);
}
private static void testWildcards() {
Function<? super Stream<? extends StackFrame>, Void> f1 = WalkFunction::function;
Function<? super Stream<? super StackFrame>, Void> f2 = WalkFunction::function;
Function<? super Stream<StackFrame>, Void> f3 = WalkFunction::function;
Function<Stream<? extends StackFrame>, Void> f4 = WalkFunction::function;
Function<Stream<? super StackFrame>, Void> f5 = WalkFunction::function;
Function<Stream<StackFrame>, Void> f6 = WalkFunction::function;
walker.walk(f1);
walker.walk(f2);
walker.walk(f3);
walker.walk(f4);
walker.walk(f5);
walker.walk(f6);
}
private static Void function(Stream<?> s) {
return null;
}
private static Function<Stream<?>, Long> wildCounter() {
return Stream::count;
}
private static <T> Function<Stream<T>, Long> counter() {
return Stream::count;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2021, 2022, 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include "jni.h"
static jclass test_class;
static jmethodID mid;
static jint current_jni_version = JNI_VERSION_19;
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
jclass cl;
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
cl = (*env)->FindClass(env, "NativeMethod");
test_class = (*env)->NewGlobalRef(env, cl);
mid = (*env)->GetMethodID(env, test_class, "walk", "()V");
return current_jni_version;
}
/*
* Class: NativeMethod
* Method: test
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeMethod_test(JNIEnv *env, jobject obj) {
(*env)->CallVoidMethod(env, obj, mid);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->FatalError(env, "Exception thrown");
}
}