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:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
227
test/jdk/java/lang/StackWalker/AcrossThreads.java
Normal file
227
test/jdk/java/lang/StackWalker/AcrossThreads.java
Normal 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);
|
||||
}
|
||||
}
|
||||
235
test/jdk/java/lang/StackWalker/Basic.java
Normal file
235
test/jdk/java/lang/StackWalker/Basic.java
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
137
test/jdk/java/lang/StackWalker/CallerFromMain.java
Normal file
137
test/jdk/java/lang/StackWalker/CallerFromMain.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
222
test/jdk/java/lang/StackWalker/DumpStackTest.java
Normal file
222
test/jdk/java/lang/StackWalker/DumpStackTest.java
Normal 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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
158
test/jdk/java/lang/StackWalker/EmbeddedStackWalkTest.java
Normal file
158
test/jdk/java/lang/StackWalker/EmbeddedStackWalkTest.java
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
265
test/jdk/java/lang/StackWalker/GetCallerClassTest.java
Normal file
265
test/jdk/java/lang/StackWalker/GetCallerClassTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
108
test/jdk/java/lang/StackWalker/HiddenFrames.java
Normal file
108
test/jdk/java/lang/StackWalker/HiddenFrames.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
543
test/jdk/java/lang/StackWalker/LocalsAndOperands.java
Normal file
543
test/jdk/java/lang/StackWalker/LocalsAndOperands.java
Normal 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));
|
||||
}
|
||||
}
|
||||
360
test/jdk/java/lang/StackWalker/MultiThreadStackWalk.java
Normal file
360
test/jdk/java/lang/StackWalker/MultiThreadStackWalk.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
78
test/jdk/java/lang/StackWalker/NativeMethod.java
Normal file
78
test/jdk/java/lang/StackWalker/NativeMethod.java
Normal 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");
|
||||
}
|
||||
}
|
||||
877
test/jdk/java/lang/StackWalker/ReflectionFrames.java
Normal file
877
test/jdk/java/lang/StackWalker/ReflectionFrames.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
test/jdk/java/lang/StackWalker/SanityTest.java
Normal file
115
test/jdk/java/lang/StackWalker/SanityTest.java
Normal 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));
|
||||
}
|
||||
}
|
||||
137
test/jdk/java/lang/StackWalker/StackRecorderUtil.java
Normal file
137
test/jdk/java/lang/StackWalker/StackRecorderUtil.java
Normal 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 + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
74
test/jdk/java/lang/StackWalker/StackStreamState.java
Normal file
74
test/jdk/java/lang/StackWalker/StackStreamState.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
306
test/jdk/java/lang/StackWalker/StackStreamTest.java
Normal file
306
test/jdk/java/lang/StackWalker/StackStreamTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
352
test/jdk/java/lang/StackWalker/StackWalkTest.java
Normal file
352
test/jdk/java/lang/StackWalker/StackWalkTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
168
test/jdk/java/lang/StackWalker/TestBCI.java
Normal file
168
test/jdk/java/lang/StackWalker/TestBCI.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
288
test/jdk/java/lang/StackWalker/VerifyStackTrace.java
Normal file
288
test/jdk/java/lang/StackWalker/VerifyStackTrace.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
89
test/jdk/java/lang/StackWalker/WalkFunction.java
Normal file
89
test/jdk/java/lang/StackWalker/WalkFunction.java
Normal 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;
|
||||
}
|
||||
}
|
||||
59
test/jdk/java/lang/StackWalker/libnativeMethod.c
Normal file
59
test/jdk/java/lang/StackWalker/libnativeMethod.c
Normal 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");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue