mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8268829: Provide an optimized way to walk the stack with Class object only
8210375: StackWalker::getCallerClass throws UnsupportedOperationException Reviewed-by: coleenp, dfuchs, bchristi
This commit is contained in:
parent
716201c77d
commit
111ecdbaf5
34 changed files with 982 additions and 622 deletions
135
src/java.base/share/classes/java/lang/ClassFrameInfo.java
Normal file
135
src/java.base/share/classes/java/lang/ClassFrameInfo.java
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.lang;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
|
||||
/**
|
||||
* ClassFrameInfo is an implementation of StackFrame that contains only
|
||||
* the class name and declaring class.
|
||||
*
|
||||
* Methods that access the method information such as method name,
|
||||
* will throw UnsupportedOperationException.
|
||||
*
|
||||
* @see StackWalker.Option#DROP_METHOD_INFO
|
||||
*/
|
||||
class ClassFrameInfo implements StackFrame {
|
||||
static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
Object classOrMemberName; // Class or ResolvedMemberName initialized by VM
|
||||
int flags; // updated by VM to set hidden and caller-sensitive bits
|
||||
|
||||
/*
|
||||
* Construct an empty ClassFrameInfo object that will be filled by the VM
|
||||
* during stack walking.
|
||||
*
|
||||
* @see StackStreamFactory.AbstractStackWalker#callStackWalk
|
||||
* @see StackStreamFactory.AbstractStackWalker#fetchStackFrames
|
||||
*/
|
||||
ClassFrameInfo(StackWalker walker) {
|
||||
this.flags = walker.retainClassRef ? RETAIN_CLASS_REF_BIT : 0;
|
||||
}
|
||||
|
||||
// package-private called by StackStreamFactory to skip
|
||||
// the capability check
|
||||
Class<?> declaringClass() {
|
||||
return (Class<?>) classOrMemberName;
|
||||
}
|
||||
|
||||
boolean isCallerSensitive() {
|
||||
return JLIA.isCallerSensitive(flags & MEMBER_INFO_FLAGS);
|
||||
}
|
||||
|
||||
boolean isHidden() {
|
||||
return JLIA.isHiddenMember(flags & MEMBER_INFO_FLAGS);
|
||||
}
|
||||
|
||||
// ----- implementation of StackFrame methods
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return declaringClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
ensureRetainClassRefEnabled();
|
||||
return declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getByteCodeIndex() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLineNumber() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativeMethod() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackTraceElement toStackTraceElement() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String tags = isHidden() ? " hidden" : "";
|
||||
if (isCallerSensitive()) {
|
||||
tags += " caller sensitive";
|
||||
}
|
||||
return declaringClass().getName() + " " + tags;
|
||||
}
|
||||
|
||||
private static final int MEMBER_INFO_FLAGS = 0x00FFFFFF;
|
||||
private static final int RETAIN_CLASS_REF_BIT = 0x08000000; // retainClassRef
|
||||
|
||||
boolean retainClassRef() {
|
||||
return (flags & RETAIN_CLASS_REF_BIT) == RETAIN_CLASS_REF_BIT;
|
||||
}
|
||||
|
||||
void ensureRetainClassRefEnabled() {
|
||||
if (!retainClassRef()) {
|
||||
throw new UnsupportedOperationException("No access to RETAIN_CLASS_REFERENCE");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
|
@ -24,20 +24,20 @@
|
|||
*/
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.vm.ContinuationScope;
|
||||
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class StackFrameInfo implements StackFrame {
|
||||
private static final JavaLangInvokeAccess JLIA =
|
||||
SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
private final boolean retainClassRef;
|
||||
private Object memberName; // MemberName initialized by VM
|
||||
private int bci; // initialized by VM to >= 0
|
||||
/**
|
||||
* StackFrameInfo is an implementation of StackFrame that contains the
|
||||
* class and method information. This is used by stack walker configured
|
||||
* without StackWalker.Option#DROP_METHOD_INFO.
|
||||
*/
|
||||
class StackFrameInfo extends ClassFrameInfo {
|
||||
private String name;
|
||||
private Object type; // String or MethodType
|
||||
private int bci; // set by VM to >= 0
|
||||
private ContinuationScope contScope;
|
||||
private volatile StackTraceElement ste;
|
||||
|
||||
|
@ -49,14 +49,13 @@ class StackFrameInfo implements StackFrame {
|
|||
* @see StackStreamFactory.AbstractStackWalker#fetchStackFrames
|
||||
*/
|
||||
StackFrameInfo(StackWalker walker) {
|
||||
this.retainClassRef = walker.retainClassRef;
|
||||
this.memberName = JLIA.newMemberName();
|
||||
super(walker);
|
||||
}
|
||||
|
||||
// package-private called by StackStreamFactory to skip
|
||||
// the capability check
|
||||
Class<?> declaringClass() {
|
||||
return JLIA.getDeclaringClass(memberName);
|
||||
return JLIA.getDeclaringClass(classOrMemberName);
|
||||
}
|
||||
|
||||
// ----- implementation of StackFrame methods
|
||||
|
@ -66,26 +65,43 @@ class StackFrameInfo implements StackFrame {
|
|||
return declaringClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
ensureRetainClassRefEnabled();
|
||||
return declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName() {
|
||||
return JLIA.getName(memberName);
|
||||
if (name == null) {
|
||||
expandStackFrameInfo();
|
||||
assert name != null;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodType getMethodType() {
|
||||
ensureRetainClassRefEnabled();
|
||||
return JLIA.getMethodType(memberName);
|
||||
|
||||
if (type == null) {
|
||||
expandStackFrameInfo();
|
||||
assert type != null;
|
||||
}
|
||||
|
||||
if (type instanceof MethodType mt) {
|
||||
return mt;
|
||||
}
|
||||
|
||||
// type is not a MethodType yet. Convert it thread-safely.
|
||||
synchronized (this) {
|
||||
if (type instanceof String sig) {
|
||||
type = JLIA.getMethodType(sig, declaringClass().getClassLoader());
|
||||
}
|
||||
}
|
||||
return (MethodType)type;
|
||||
}
|
||||
|
||||
// expand the name and type field of StackFrameInfo
|
||||
private native void expandStackFrameInfo();
|
||||
|
||||
@Override
|
||||
public String getDescriptor() {
|
||||
return JLIA.getMethodDescriptor(memberName);
|
||||
return getMethodType().descriptorString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,7 +130,7 @@ class StackFrameInfo implements StackFrame {
|
|||
|
||||
@Override
|
||||
public boolean isNativeMethod() {
|
||||
return JLIA.isNative(memberName);
|
||||
return Modifier.isNative(flags);
|
||||
}
|
||||
|
||||
private String getContinuationScopeName() {
|
||||
|
@ -139,10 +155,4 @@ class StackFrameInfo implements StackFrame {
|
|||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private void ensureRetainClassRefEnabled() {
|
||||
if (!retainClassRef) {
|
||||
throw new UnsupportedOperationException("No access to RETAIN_CLASS_REFERENCE");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
|
@ -30,8 +30,10 @@ import java.lang.StackWalker.Option;
|
|||
import java.lang.StackWalker.StackFrame;
|
||||
|
||||
import java.lang.annotation.Native;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
@ -74,10 +76,10 @@ final class StackStreamFactory {
|
|||
|
||||
// These flags must match the values maintained in the VM
|
||||
@Native private static final int DEFAULT_MODE = 0x0;
|
||||
@Native private static final int FILL_CLASS_REFS_ONLY = 0x2;
|
||||
@Native private static final int GET_CALLER_CLASS = 0x4;
|
||||
@Native private static final int CLASS_INFO_ONLY = 0x2;
|
||||
@Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM
|
||||
@Native private static final int FILL_LIVE_STACK_FRAMES = 0x100;
|
||||
|
||||
/*
|
||||
* For Throwable to use StackWalker, set useNewThrowable to true.
|
||||
* Performance work and extensive testing is needed to replace the
|
||||
|
@ -89,10 +91,11 @@ final class StackStreamFactory {
|
|||
static <T> StackFrameTraverser<T>
|
||||
makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
|
||||
{
|
||||
if (walker.hasLocalsOperandsOption())
|
||||
if (walker.hasLocalsOperandsOption()) {
|
||||
return new LiveStackInfoTraverser<>(walker, function);
|
||||
else
|
||||
} else {
|
||||
return new StackFrameTraverser<>(walker, function);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,6 +111,17 @@ final class StackStreamFactory {
|
|||
CLOSED; // the stream is closed when the stack walking is done
|
||||
}
|
||||
|
||||
private static int toStackWalkMode(StackWalker walker, int mode) {
|
||||
int newMode = mode;
|
||||
if (walker.hasOption(Option.DROP_METHOD_INFO))
|
||||
newMode |= CLASS_INFO_ONLY;
|
||||
if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES))
|
||||
newMode |= SHOW_HIDDEN_FRAMES;
|
||||
if (walker.hasLocalsOperandsOption())
|
||||
newMode |= FILL_LIVE_STACK_FRAMES;
|
||||
return newMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of AbstractStackWalker implements a specific stack walking logic.
|
||||
* It needs to set up the frame buffer and stack walking mode.
|
||||
|
@ -124,7 +138,7 @@ final class StackStreamFactory {
|
|||
protected final StackWalker walker;
|
||||
protected final Thread thread;
|
||||
protected final int maxDepth;
|
||||
protected final long mode;
|
||||
protected final int mode;
|
||||
protected int depth; // traversed stack depth
|
||||
protected FrameBuffer<? extends T> frameBuffer;
|
||||
protected long anchor;
|
||||
|
@ -137,7 +151,7 @@ final class StackStreamFactory {
|
|||
}
|
||||
protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) {
|
||||
this.thread = Thread.currentThread();
|
||||
this.mode = toStackWalkMode(walker, mode);
|
||||
this.mode = mode;
|
||||
this.walker = walker;
|
||||
this.maxDepth = maxDepth;
|
||||
this.depth = 0;
|
||||
|
@ -151,16 +165,6 @@ final class StackStreamFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private int toStackWalkMode(StackWalker walker, int mode) {
|
||||
int newMode = mode;
|
||||
if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES) &&
|
||||
(mode & FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY)
|
||||
newMode |= SHOW_HIDDEN_FRAMES;
|
||||
if (walker.hasLocalsOperandsOption())
|
||||
newMode |= FILL_LIVE_STACK_FRAMES;
|
||||
return newMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback method to consume the stack frames. This method is invoked
|
||||
* once stack walking begins (i.e. it is only invoked when walkFrames is called).
|
||||
|
@ -450,11 +454,11 @@ final class StackStreamFactory {
|
|||
* @param continuation the continuation to walk, or {@code null} if walking a thread.
|
||||
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
||||
* @param startIndex start index of the frame buffers to be filled.
|
||||
* @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
|
||||
* @param frames Either a {@link ClassFrameInfo} array, if mode is {@link #CLASS_INFO_ONLY}
|
||||
* or a {@link StackFrameInfo} (or derivative) array otherwise.
|
||||
* @return Result of AbstractStackWalker::doStackWalk
|
||||
*/
|
||||
private native R callStackWalk(long mode, int skipframes,
|
||||
private native R callStackWalk(int mode, int skipframes,
|
||||
ContinuationScope contScope, Continuation continuation,
|
||||
int batchSize, int startIndex,
|
||||
T[] frames);
|
||||
|
@ -466,12 +470,12 @@ final class StackStreamFactory {
|
|||
* @param anchor
|
||||
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
||||
* @param startIndex start index of the frame buffers to be filled.
|
||||
* @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
|
||||
* @param frames Either a {@link ClassFrameInfo} array, if mode is {@link #CLASS_INFO_ONLY}
|
||||
* or a {@link StackFrameInfo} (or derivative) array otherwise.
|
||||
*
|
||||
* @return the end index to the frame buffers
|
||||
*/
|
||||
private native int fetchStackFrames(long mode, long anchor,
|
||||
private native int fetchStackFrames(int mode, long anchor,
|
||||
int batchSize, int startIndex,
|
||||
T[] frames);
|
||||
|
||||
|
@ -483,7 +487,7 @@ final class StackStreamFactory {
|
|||
*
|
||||
* This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
|
||||
*/
|
||||
static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrameInfo>
|
||||
static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrame>
|
||||
implements Spliterator<StackFrame>
|
||||
{
|
||||
static {
|
||||
|
@ -491,70 +495,11 @@ final class StackStreamFactory {
|
|||
}
|
||||
private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
|
||||
|
||||
final class StackFrameBuffer extends FrameBuffer<StackFrameInfo> {
|
||||
private StackFrameInfo[] stackFrames;
|
||||
StackFrameBuffer(int initialBatchSize) {
|
||||
super(initialBatchSize);
|
||||
|
||||
this.stackFrames = new StackFrameInfo[initialBatchSize];
|
||||
for (int i = START_POS; i < initialBatchSize; i++) {
|
||||
stackFrames[i] = new StackFrameInfo(walker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
StackFrameInfo[] frames() {
|
||||
return stackFrames;
|
||||
}
|
||||
|
||||
@Override
|
||||
void resize(int startIndex, int elements) {
|
||||
if (!isActive())
|
||||
throw new IllegalStateException("inactive frame buffer can't be resized");
|
||||
|
||||
assert startIndex == START_POS :
|
||||
"bad start index " + startIndex + " expected " + START_POS;
|
||||
|
||||
int size = startIndex+elements;
|
||||
if (stackFrames.length < size) {
|
||||
StackFrameInfo[] newFrames = new StackFrameInfo[size];
|
||||
// copy initial magic...
|
||||
System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
|
||||
stackFrames = newFrames;
|
||||
}
|
||||
for (int i = startIndex; i < size; i++) {
|
||||
stackFrames[i] = new StackFrameInfo(walker);
|
||||
}
|
||||
currentBatchSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
StackFrameInfo nextStackFrame() {
|
||||
if (isEmpty()) {
|
||||
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
|
||||
}
|
||||
|
||||
StackFrameInfo frame = stackFrames[origin];
|
||||
origin++;
|
||||
return frame;
|
||||
}
|
||||
|
||||
@Override
|
||||
final Class<?> at(int index) {
|
||||
return stackFrames[index].declaringClass();
|
||||
}
|
||||
}
|
||||
|
||||
final Function<? super Stream<StackFrame>, ? extends T> function; // callback
|
||||
|
||||
StackFrameTraverser(StackWalker walker,
|
||||
Function<? super Stream<StackFrame>, ? extends T> function) {
|
||||
this(walker, function, DEFAULT_MODE);
|
||||
}
|
||||
StackFrameTraverser(StackWalker walker,
|
||||
Function<? super Stream<StackFrame>, ? extends T> function,
|
||||
int mode) {
|
||||
super(walker, mode);
|
||||
super(walker, toStackWalkMode(walker, DEFAULT_MODE));
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
|
@ -567,7 +512,7 @@ final class StackStreamFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
StackFrameInfo frame = frameBuffer.nextStackFrame();
|
||||
StackFrame frame = frameBuffer.nextStackFrame();
|
||||
depth++;
|
||||
return frame;
|
||||
}
|
||||
|
@ -584,7 +529,9 @@ final class StackStreamFactory {
|
|||
|
||||
@Override
|
||||
protected void initFrameBuffer() {
|
||||
this.frameBuffer = new StackFrameBuffer(getNextBatchSize());
|
||||
this.frameBuffer = walker.hasOption(Option.DROP_METHOD_INFO)
|
||||
? new ClassFrameBuffer(walker, getNextBatchSize())
|
||||
: new StackFrameBuffer<>(StackFrameInfo.class, walker, getNextBatchSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -651,11 +598,108 @@ final class StackStreamFactory {
|
|||
}
|
||||
}
|
||||
|
||||
static class StackFrameBuffer<T extends ClassFrameInfo> extends FrameBuffer<T> {
|
||||
final StackWalker walker;
|
||||
private final Class<T> type;
|
||||
private final Constructor<T> ctor;
|
||||
private T[] stackFrames;
|
||||
StackFrameBuffer(Class<T> type, StackWalker walker, int initialBatchSize) {
|
||||
super(initialBatchSize);
|
||||
this.walker = walker;
|
||||
this.type = type;
|
||||
try {
|
||||
this.ctor = type.getDeclaredConstructor(StackWalker.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
this.stackFrames = fill(allocateArray(initialBatchSize), START_POS, initialBatchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
T[] frames() {
|
||||
return stackFrames;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] allocateArray(int size) {
|
||||
return (T[])Array.newInstance(type, size);
|
||||
}
|
||||
|
||||
T[] fill(T[] array, int startIndex, int size) {
|
||||
try {
|
||||
for (int i = startIndex; i < size; i++) {
|
||||
array[i] = ctor.newInstance(walker);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
void resize(int startIndex, int elements) {
|
||||
if (!isActive())
|
||||
throw new IllegalStateException("inactive frame buffer can't be resized");
|
||||
|
||||
assert startIndex == START_POS :
|
||||
"bad start index " + startIndex + " expected " + START_POS;
|
||||
|
||||
int size = startIndex+elements;
|
||||
if (stackFrames.length < size) {
|
||||
T[] newFrames = allocateArray(size);
|
||||
// copy initial magic...
|
||||
System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
|
||||
stackFrames = newFrames;
|
||||
}
|
||||
fill(stackFrames, startIndex, size);
|
||||
currentBatchSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
T nextStackFrame() {
|
||||
if (isEmpty()) {
|
||||
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
|
||||
}
|
||||
|
||||
T frame = stackFrames[origin];
|
||||
origin++;
|
||||
return frame;
|
||||
}
|
||||
|
||||
@Override
|
||||
final Class<?> at(int index) {
|
||||
return stackFrames[index].declaringClass();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer for ClassFrameInfo. It allocates ClassFrameInfo via bytecode
|
||||
* invocation instead of via core reflection to minimize the overhead.
|
||||
*/
|
||||
static class ClassFrameBuffer extends StackFrameBuffer<ClassFrameInfo> {
|
||||
ClassFrameBuffer(StackWalker walker, int initialBatchSize) {
|
||||
super(ClassFrameInfo.class, walker, initialBatchSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
ClassFrameInfo[] allocateArray(int size) {
|
||||
return new ClassFrameInfo[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
ClassFrameInfo[] fill(ClassFrameInfo[] array, int startIndex, int size) {
|
||||
for (int i = startIndex; i < size; i++) {
|
||||
array[i] = new ClassFrameInfo(walker);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CallerClassFinder is specialized to return Class<?> for each stack frame.
|
||||
* StackFrame is not requested.
|
||||
*/
|
||||
static final class CallerClassFinder extends AbstractStackWalker<Integer, Class<?>> {
|
||||
static final class CallerClassFinder extends AbstractStackWalker<Integer, ClassFrameInfo> {
|
||||
static {
|
||||
stackWalkImplClasses.add(CallerClassFinder.class);
|
||||
}
|
||||
|
@ -663,52 +707,7 @@ final class StackStreamFactory {
|
|||
private Class<?> caller;
|
||||
|
||||
CallerClassFinder(StackWalker walker) {
|
||||
super(walker, FILL_CLASS_REFS_ONLY|GET_CALLER_CLASS);
|
||||
}
|
||||
|
||||
static final class ClassBuffer extends FrameBuffer<Class<?>> {
|
||||
Class<?>[] classes; // caller class for fast path
|
||||
ClassBuffer(int batchSize) {
|
||||
super(batchSize);
|
||||
classes = new Class<?>[batchSize];
|
||||
}
|
||||
|
||||
@Override
|
||||
Class<?>[] frames() { return classes;}
|
||||
|
||||
@Override
|
||||
final Class<?> at(int index) { return classes[index];}
|
||||
|
||||
// ------ subclass may override the following methods -------
|
||||
/**
|
||||
* Resizes the buffers for VM to fill in the next batch of stack frames.
|
||||
* The next batch will start at the given startIndex with the maximum number
|
||||
* of elements.
|
||||
*
|
||||
* <p> Subclass may override this method to manage the allocated buffers.
|
||||
*
|
||||
* @param startIndex the start index for the first frame of the next batch to fill in.
|
||||
* @param elements the number of elements for the next batch to fill in.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
void resize(int startIndex, int elements) {
|
||||
if (!isActive())
|
||||
throw new IllegalStateException("inactive frame buffer can't be resized");
|
||||
|
||||
assert startIndex == START_POS :
|
||||
"bad start index " + startIndex + " expected " + START_POS;
|
||||
|
||||
int size = startIndex+elements;
|
||||
if (classes.length < size) {
|
||||
// copy the elements in classes array to the newly allocated one.
|
||||
// classes[0] is a Thread object
|
||||
Class<?>[] prev = classes;
|
||||
classes = new Class<?>[size];
|
||||
System.arraycopy(prev, 0, classes, 0, startIndex);
|
||||
}
|
||||
currentBatchSize = size;
|
||||
}
|
||||
super(walker, toStackWalkMode(walker, CLASS_INFO_ONLY));
|
||||
}
|
||||
|
||||
Class<?> findCaller() {
|
||||
|
@ -720,25 +719,37 @@ final class StackStreamFactory {
|
|||
protected Integer consumeFrames() {
|
||||
checkState(OPEN);
|
||||
int n = 0;
|
||||
Class<?>[] frames = new Class<?>[2];
|
||||
// skip the API calling this getCallerClass method
|
||||
// 0: StackWalker::getCallerClass
|
||||
// 1: caller-sensitive method
|
||||
// 2: caller class
|
||||
while (n < 2 && (caller = nextFrame()) != null) {
|
||||
if (isMethodHandleFrame(caller)) { continue; }
|
||||
if (isReflectionFrame(caller)) { continue; }
|
||||
frames[n++] = caller;
|
||||
ClassFrameInfo curFrame = null;
|
||||
// StackWalker::getCallerClass method
|
||||
// 0: caller-sensitive method
|
||||
// 1: caller class
|
||||
ClassFrameInfo[] frames = new ClassFrameInfo[2];
|
||||
while (n < 2 && hasNext() && (curFrame = frameBuffer.nextStackFrame()) != null) {
|
||||
caller = curFrame.declaringClass();
|
||||
if (curFrame.isHidden() || isReflectionFrame(caller) || isMethodHandleFrame(caller)) {
|
||||
if (isDebug)
|
||||
System.err.println(" skip: frame " + frameBuffer.getIndex() + " " + curFrame);
|
||||
continue;
|
||||
}
|
||||
frames[n++] = curFrame;
|
||||
}
|
||||
if (isDebug) {
|
||||
System.err.println("0: " + frames[0]);
|
||||
System.err.println("1: " + frames[1]);
|
||||
}
|
||||
if (frames[1] == null) {
|
||||
throw new IllegalCallerException("no caller frame");
|
||||
throw new IllegalCallerException("no caller frame: " + Arrays.toString(frames));
|
||||
}
|
||||
if (frames[0].isCallerSensitive()) {
|
||||
throw new UnsupportedOperationException("StackWalker::getCallerClass called from @CallerSensitive "
|
||||
+ Arrays.toString(frames));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFrameBuffer() {
|
||||
this.frameBuffer = new ClassBuffer(getNextBatchSize());
|
||||
this.frameBuffer = new ClassFrameBuffer(walker, getNextBatchSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -756,70 +767,15 @@ final class StackStreamFactory {
|
|||
static {
|
||||
stackWalkImplClasses.add(LiveStackInfoTraverser.class);
|
||||
}
|
||||
// VM will fill in all method info and live stack info directly in StackFrameInfo
|
||||
final class LiveStackFrameBuffer extends FrameBuffer<LiveStackFrameInfo> {
|
||||
private LiveStackFrameInfo[] stackFrames;
|
||||
LiveStackFrameBuffer(int initialBatchSize) {
|
||||
super(initialBatchSize);
|
||||
this.stackFrames = new LiveStackFrameInfo[initialBatchSize];
|
||||
for (int i = START_POS; i < initialBatchSize; i++) {
|
||||
stackFrames[i] = new LiveStackFrameInfo(walker);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
LiveStackFrameInfo[] frames() {
|
||||
return stackFrames;
|
||||
}
|
||||
|
||||
@Override
|
||||
void resize(int startIndex, int elements) {
|
||||
if (!isActive()) {
|
||||
throw new IllegalStateException("inactive frame buffer can't be resized");
|
||||
}
|
||||
assert startIndex == START_POS :
|
||||
"bad start index " + startIndex + " expected " + START_POS;
|
||||
|
||||
int size = startIndex + elements;
|
||||
if (stackFrames.length < size) {
|
||||
LiveStackFrameInfo[] newFrames = new LiveStackFrameInfo[size];
|
||||
// copy initial magic...
|
||||
System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
|
||||
stackFrames = newFrames;
|
||||
}
|
||||
|
||||
for (int i = startIndex(); i < size; i++) {
|
||||
stackFrames[i] = new LiveStackFrameInfo(walker);
|
||||
}
|
||||
|
||||
currentBatchSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
LiveStackFrameInfo nextStackFrame() {
|
||||
if (isEmpty()) {
|
||||
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
|
||||
}
|
||||
|
||||
LiveStackFrameInfo frame = stackFrames[origin];
|
||||
origin++;
|
||||
return frame;
|
||||
}
|
||||
|
||||
@Override
|
||||
final Class<?> at(int index) {
|
||||
return stackFrames[index].declaringClass();
|
||||
}
|
||||
}
|
||||
|
||||
LiveStackInfoTraverser(StackWalker walker,
|
||||
Function<? super Stream<StackFrame>, ? extends T> function) {
|
||||
super(walker, function, DEFAULT_MODE);
|
||||
super(walker, function);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFrameBuffer() {
|
||||
this.frameBuffer = new LiveStackFrameBuffer(getNextBatchSize());
|
||||
this.frameBuffer = new StackFrameBuffer<>(LiveStackFrameInfo.class, walker, getNextBatchSize());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -851,7 +807,7 @@ final class StackStreamFactory {
|
|||
* when walking the stack.
|
||||
*
|
||||
* May be an array of {@code Class<?>} if the {@code AbstractStackWalker}
|
||||
* mode is {@link #FILL_CLASS_REFS_ONLY}, or an array of
|
||||
* mode is {@link #CLASS_INFO_ONLY}, or an array of
|
||||
* {@link StackFrameInfo} (or derivative) array otherwise.
|
||||
*
|
||||
* @return An array of frames that may be used to store frame objects
|
||||
|
@ -1021,22 +977,20 @@ final class StackStreamFactory {
|
|||
|
||||
private static boolean filterStackWalkImpl(Class<?> c) {
|
||||
return stackWalkImplClasses.contains(c) ||
|
||||
c.getName().startsWith("java.util.stream.");
|
||||
c.getPackageName().equals("java.util.stream");
|
||||
}
|
||||
|
||||
// MethodHandle frames are not hidden and CallerClassFinder has
|
||||
// to filter them out
|
||||
private static boolean isMethodHandleFrame(Class<?> c) {
|
||||
return c.getName().startsWith("java.lang.invoke.");
|
||||
return c.getPackageName().equals("java.lang.invoke");
|
||||
}
|
||||
|
||||
private static boolean isReflectionFrame(Class<?> c) {
|
||||
// ## should filter all @Hidden frames?
|
||||
return c == Method.class ||
|
||||
c == Constructor.class ||
|
||||
MethodAccessor.class.isAssignableFrom(c) ||
|
||||
ConstructorAccessor.class.isAssignableFrom(c) ||
|
||||
c.getName().startsWith("java.lang.invoke.LambdaForm");
|
||||
ConstructorAccessor.class.isAssignableFrom(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
|
@ -49,13 +49,15 @@ import jdk.internal.vm.ContinuationScope;
|
|||
* If an attempt is made to reuse the closed stream,
|
||||
* {@code IllegalStateException} will be thrown.
|
||||
*
|
||||
* <p> The {@linkplain Option <em>stack walking options</em>} of a
|
||||
* {@code StackWalker} determines the information of
|
||||
* {@link StackFrame StackFrame} objects to be returned.
|
||||
* By default, stack frames of the reflection API and implementation
|
||||
* classes are {@linkplain Option#SHOW_HIDDEN_FRAMES hidden}
|
||||
* and {@code StackFrame}s have the class name and method name
|
||||
* available but not the {@link StackFrame#getDeclaringClass() Class reference}.
|
||||
* <p> {@linkplain Option <em>Stack walker options</em>} configure the stack frame
|
||||
* information obtained by a {@code StackWalker}.
|
||||
* By default, the class name and method information are collected but
|
||||
* not the {@link StackFrame#getDeclaringClass() Class reference}.
|
||||
* The method information can be dropped via the {@link Option#DROP_METHOD_INFO
|
||||
* DROP_METHOD_INFO} option. The {@code Class} object can be retained for
|
||||
* access via the {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option.
|
||||
* Stack frames of the reflection API and implementation classes are
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden} by default.
|
||||
*
|
||||
* <p> {@code StackWalker} is thread-safe. Multiple threads can share
|
||||
* a single {@code StackWalker} object to traverse its own stack.
|
||||
|
@ -66,20 +68,19 @@ import jdk.internal.vm.ContinuationScope;
|
|||
* @apiNote
|
||||
* Examples
|
||||
*
|
||||
* <p>1. To find the first caller filtering a known list of implementation class:
|
||||
* <pre>{@code
|
||||
* StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
|
||||
* <p>1. To find the first caller filtering out a known list of implementation class:
|
||||
* {@snippet lang="java" :
|
||||
* StackWalker walker = StackWalker.getInstance(Set.of(Option.DROP_METHOD_INFO, Option.RETAIN_CLASS_REFERENCE));
|
||||
* Optional<Class<?>> callerClass = walker.walk(s ->
|
||||
* s.map(StackFrame::getDeclaringClass)
|
||||
* .filter(interestingClasses::contains)
|
||||
* .findFirst());
|
||||
* }</pre>
|
||||
* s.map(StackFrame::getDeclaringClass)
|
||||
* .filter(Predicate.not(implClasses::contains))
|
||||
* .findFirst());
|
||||
* }
|
||||
*
|
||||
* <p>2. To snapshot the top 10 stack frames of the current thread,
|
||||
* <pre>{@code
|
||||
* List<StackFrame> stack = StackWalker.getInstance().walk(s ->
|
||||
* s.limit(10).collect(Collectors.toList()));
|
||||
* }</pre>
|
||||
* {@snippet lang="java" :
|
||||
* List<StackFrame> stack = StackWalker.getInstance().walk(s -> s.limit(10).toList());
|
||||
* }
|
||||
*
|
||||
* Unless otherwise noted, passing a {@code null} argument to a
|
||||
* constructor or method in this {@code StackWalker} class
|
||||
|
@ -93,41 +94,44 @@ public final class StackWalker {
|
|||
* A {@code StackFrame} object represents a method invocation returned by
|
||||
* {@link StackWalker}.
|
||||
*
|
||||
* <p> The {@link #getDeclaringClass()} method may be unsupported as determined
|
||||
* by the {@linkplain Option stack walking options} of a {@linkplain
|
||||
* StackWalker stack walker}.
|
||||
* <p> {@linkplain Option <em>Stack walker options</em>} configure the stack
|
||||
* frame information obtained by a {@code StackWalker}.
|
||||
* If the stack walker is configured with {@link Option#DROP_METHOD_INFO
|
||||
* DROP_METHOD_INFO} option, method information such as
|
||||
* the {@linkplain StackFrame#getMethodName() method name},
|
||||
* the {@linkplain StackFrame#getLineNumber() line number},
|
||||
* the {@linkplain StackFrame#getByteCodeIndex() bytecode index}, etc
|
||||
* will be dropped.
|
||||
* If the stack walker is configured with {@link Option#RETAIN_CLASS_REFERENCE
|
||||
* RETAIN_CLASS_REFERENCE} option, the {@link #getDeclaringClass() Class} object
|
||||
* will be retained for access.
|
||||
*
|
||||
* @since 9
|
||||
* @jvms 2.6
|
||||
* @jvms 2.6 Frames
|
||||
*/
|
||||
public interface StackFrame {
|
||||
/**
|
||||
* Gets the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* of the declaring class of the method represented by this stack frame.
|
||||
*
|
||||
* @return the binary name of the declaring class of the method
|
||||
* represented by this stack frame
|
||||
* {@return the {@linkplain ClassLoader##binary-name binary name}
|
||||
* of the declaring class of the method represented by this stack frame}
|
||||
*
|
||||
* @jls 13.1 The Form of a Binary
|
||||
*/
|
||||
public String getClassName();
|
||||
|
||||
/**
|
||||
* Gets the name of the method represented by this stack frame.
|
||||
* @return the name of the method represented by this stack frame
|
||||
* {@return the name of the method represented by this stack frame}
|
||||
*
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*/
|
||||
public String getMethodName();
|
||||
|
||||
/**
|
||||
* Gets the declaring {@code Class} for the method represented by
|
||||
* this stack frame.
|
||||
* {@return the declaring {@code Class} for the method represented by
|
||||
* this stack frame}
|
||||
*
|
||||
* @return the declaring {@code Class} of the method represented by
|
||||
* this stack frame
|
||||
*
|
||||
* @throws UnsupportedOperationException if this {@code StackWalker}
|
||||
* is not configured with {@link Option#RETAIN_CLASS_REFERENCE
|
||||
* Option.RETAIN_CLASS_REFERENCE}.
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* without {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option
|
||||
*/
|
||||
public Class<?> getDeclaringClass();
|
||||
|
||||
|
@ -138,11 +142,11 @@ public final class StackWalker {
|
|||
* @implSpec
|
||||
* The default implementation throws {@code UnsupportedOperationException}.
|
||||
*
|
||||
* @return the {@code MethodType} for this stack frame
|
||||
* @return the {@code MethodType} of the method represented by this stack frame
|
||||
*
|
||||
* @throws UnsupportedOperationException if this {@code StackWalker}
|
||||
* is not configured with {@link Option#RETAIN_CLASS_REFERENCE
|
||||
* Option.RETAIN_CLASS_REFERENCE}.
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option or
|
||||
* without {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option
|
||||
*
|
||||
* @since 10
|
||||
*/
|
||||
|
@ -161,6 +165,9 @@ public final class StackWalker {
|
|||
* @return the descriptor of the method represented by
|
||||
* this stack frame
|
||||
*
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*
|
||||
* @see MethodType#fromMethodDescriptorString(String, ClassLoader)
|
||||
* @see MethodType#toMethodDescriptorString()
|
||||
* @jvms 4.3.3 Method Descriptor
|
||||
|
@ -182,6 +189,9 @@ public final class StackWalker {
|
|||
* containing the execution point represented by this stack frame,
|
||||
* or a negative number if the method is native.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*
|
||||
* @jvms 4.7.3 The {@code Code} Attribute
|
||||
*/
|
||||
public int getByteCodeIndex();
|
||||
|
@ -198,6 +208,9 @@ public final class StackWalker {
|
|||
* represented by this stack frame, or {@code null} if
|
||||
* this information is unavailable.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*
|
||||
* @jvms 4.7.10 The {@code SourceFile} Attribute
|
||||
*/
|
||||
public String getFileName();
|
||||
|
@ -213,23 +226,27 @@ public final class StackWalker {
|
|||
* point represented by this stack frame, or a negative number if
|
||||
* this information is unavailable.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*
|
||||
* @jvms 4.7.12 The {@code LineNumberTable} Attribute
|
||||
*/
|
||||
public int getLineNumber();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the method containing the execution point
|
||||
* represented by this stack frame is a native method.
|
||||
* {@return {@code true} if the method containing the execution point
|
||||
* represented by this stack frame is a native method}
|
||||
*
|
||||
* @return {@code true} if the method containing the execution point
|
||||
* represented by this stack frame is a native method.
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*/
|
||||
public boolean isNativeMethod();
|
||||
|
||||
/**
|
||||
* Gets a {@code StackTraceElement} for this stack frame.
|
||||
* {@return {@code StackTraceElement} for this stack frame}
|
||||
*
|
||||
* @return {@code StackTraceElement} for this stack frame.
|
||||
* @throws UnsupportedOperationException if the {@code StackWalker} is configured
|
||||
* with {@link Option#DROP_METHOD_INFO DROP_METHOD_INFO} option
|
||||
*/
|
||||
public StackTraceElement toStackTraceElement();
|
||||
}
|
||||
|
@ -250,6 +267,21 @@ public final class StackWalker {
|
|||
* {@link StackFrame#getDeclaringClass() StackFrame.getDeclaringClass()}.
|
||||
*/
|
||||
RETAIN_CLASS_REFERENCE,
|
||||
/**
|
||||
* Drops the method information from {@code StackFrame}s
|
||||
* walked by this {@code StackWalker}.
|
||||
*
|
||||
* <p> A {@code StackWalker} configured with this option will drop
|
||||
* the {@linkplain StackFrame#getMethodName() method name},
|
||||
* the {@linkplain StackFrame#getMethodType() method type},
|
||||
* the {@linkplain StackFrame#getLineNumber() line number},
|
||||
* the {@linkplain StackFrame#getByteCodeIndex() bytecode index},
|
||||
* the {@linkplain StackFrame#getFileName() source file name} and
|
||||
* {@linkplain StackFrame#isNativeMethod() native method or not}.
|
||||
*
|
||||
* @since 22
|
||||
*/
|
||||
DROP_METHOD_INFO,
|
||||
/**
|
||||
* Shows all reflection frames.
|
||||
*
|
||||
|
@ -319,28 +351,9 @@ public final class StackWalker {
|
|||
return DEFAULT_WALKER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance.
|
||||
*
|
||||
* <p> This {@code StackWalker} is configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
|
||||
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @return a {@code StackWalker} configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
|
||||
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
*/
|
||||
static StackWalker getInstance(ContinuationScope contScope) {
|
||||
return getInstance(EnumSet.noneOf(Option.class), contScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given option specifying
|
||||
* the stack frame information it can access.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code option} is
|
||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
||||
|
@ -359,33 +372,13 @@ public final class StackWalker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given option specifying
|
||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||
* the stack frame information it can access.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code option} is
|
||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*
|
||||
* @param option {@link Option stack walking option}
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @return a {@code StackWalker} configured with the given option
|
||||
*
|
||||
* @throws SecurityException if a security manager exists and its
|
||||
* {@code checkPermission} method denies access.
|
||||
*/
|
||||
static StackWalker getInstance(Option option, ContinuationScope contScope) {
|
||||
return getInstance(EnumSet.of(Objects.requireNonNull(option)), contScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||
* the stack frame information it can access. If the given {@code options}
|
||||
* is empty, this {@code StackWalker} is configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
* If the given {@code options} is empty, this {@code StackWalker} is
|
||||
* configured to skip all {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames}
|
||||
* and no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code options} contains
|
||||
|
@ -393,7 +386,7 @@ public final class StackWalker {
|
|||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*
|
||||
* @param options {@link Option stack walking option}
|
||||
* @param options {@link Option stack walking options}
|
||||
*
|
||||
* @return a {@code StackWalker} configured with the given options
|
||||
*
|
||||
|
@ -401,46 +394,23 @@ public final class StackWalker {
|
|||
* {@code checkPermission} method denies access.
|
||||
*/
|
||||
public static StackWalker getInstance(Set<Option> options) {
|
||||
return getInstance(options, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||
* the stack frame information it can access. If the given {@code options}
|
||||
* is empty, this {@code StackWalker} is configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code options} contains
|
||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*
|
||||
* @param options {@link Option stack walking option}
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @return a {@code StackWalker} configured with the given options
|
||||
*
|
||||
* @throws SecurityException if a security manager exists and its
|
||||
* {@code checkPermission} method denies access.
|
||||
*/
|
||||
static StackWalker getInstance(Set<Option> options, ContinuationScope contScope) {
|
||||
if (options.isEmpty() && contScope == null) {
|
||||
if (options.isEmpty()) {
|
||||
return DEFAULT_WALKER;
|
||||
}
|
||||
|
||||
EnumSet<Option> optionSet = toEnumSet(options);
|
||||
checkPermission(optionSet);
|
||||
return new StackWalker(optionSet, contScope);
|
||||
return new StackWalker(optionSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||
* the stack frame information it can access. If the given {@code options}
|
||||
* is empty, this {@code StackWalker} is configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
* the stack frame information it can access.
|
||||
*
|
||||
* <p>
|
||||
* If the given {@code options} is empty, this {@code StackWalker} is
|
||||
* configured to skip all {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames}
|
||||
* and no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code options} contains
|
||||
|
@ -475,24 +445,9 @@ public final class StackWalker {
|
|||
private StackWalker(EnumSet<Option> options) {
|
||||
this(options, 0, null, null, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, ContinuationScope contScope) {
|
||||
this(options, 0, null, contScope, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, ContinuationScope contScope, Continuation continuation) {
|
||||
this(options, 0, null, contScope, continuation);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, int estimateDepth) {
|
||||
this(options, estimateDepth, null, null, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, int estimateDepth, ContinuationScope contScope) {
|
||||
this(options, estimateDepth, null, contScope, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options,
|
||||
int estimateDepth,
|
||||
ContinuationScope contScope,
|
||||
Continuation continuation) {
|
||||
this(options, estimateDepth, null, contScope, continuation);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options,
|
||||
int estimateDepth,
|
||||
ExtendedOption extendedOption,
|
||||
|
@ -541,13 +496,12 @@ public final class StackWalker {
|
|||
* @apiNote
|
||||
* For example, to find the first 10 calling frames, first skipping those frames
|
||||
* whose declaring class is in package {@code com.foo}:
|
||||
* <blockquote>
|
||||
* <pre>{@code
|
||||
* {@snippet lang="java" :
|
||||
* List<StackFrame> frames = StackWalker.getInstance().walk(s ->
|
||||
* s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
|
||||
* .limit(10)
|
||||
* .collect(Collectors.toList()));
|
||||
* }</pre></blockquote>
|
||||
* s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
|
||||
* .limit(10)
|
||||
* .toList());
|
||||
* }
|
||||
*
|
||||
* <p>This method takes a {@code Function} accepting a {@code Stream<StackFrame>},
|
||||
* rather than returning a {@code Stream<StackFrame>} and allowing the
|
||||
|
@ -638,9 +592,10 @@ public final class StackWalker {
|
|||
* the class loader to load the resource bundle. The caller class
|
||||
* in this example is {@code MyTool}.
|
||||
*
|
||||
* <pre>{@code
|
||||
* {@snippet lang="java" :
|
||||
* class Util {
|
||||
* private final StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
|
||||
* private final StackWalker walker =
|
||||
* StackWalker.getInstance(Set.of(Option.DROP_METHOD_INFO, Option.RETAIN_CLASS_REFERENCE));
|
||||
* public ResourceBundle getResourceBundle(String bundleName) {
|
||||
* Class<?> caller = walker.getCallerClass();
|
||||
* return ResourceBundle.getBundle(bundleName, Locale.getDefault(), caller.getClassLoader());
|
||||
|
@ -653,18 +608,18 @@ public final class StackWalker {
|
|||
* ResourceBundle rb = util.getResourceBundle("mybundle");
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* }
|
||||
*
|
||||
* An equivalent way to find the caller class using the
|
||||
* {@link StackWalker#walk walk} method is as follows
|
||||
* (filtering the reflection frames, {@code MethodHandle} and hidden frames
|
||||
* not shown below):
|
||||
* <pre>{@code
|
||||
* {@snippet lang="java" :
|
||||
* Optional<Class<?>> caller = walker.walk(s ->
|
||||
* s.map(StackFrame::getDeclaringClass)
|
||||
* .skip(2)
|
||||
* .findFirst());
|
||||
* }</pre>
|
||||
* }
|
||||
*
|
||||
* When the {@code getCallerClass} method is called from a method that
|
||||
* is the bottom most frame on the stack,
|
||||
|
@ -700,10 +655,12 @@ public final class StackWalker {
|
|||
return newInstance(options, extendedOption, null);
|
||||
}
|
||||
|
||||
static StackWalker newInstance(Set<Option> options, ContinuationScope contScope) {
|
||||
return newInstance(options, null, contScope);
|
||||
}
|
||||
|
||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope) {
|
||||
EnumSet<Option> optionSet = toEnumSet(options);
|
||||
checkPermission(optionSet);
|
||||
return new StackWalker(optionSet, 0, extendedOption, contScope, null);
|
||||
return newInstance(options, extendedOption, contScope, null);
|
||||
}
|
||||
|
||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope, Continuation continuation) {
|
||||
|
|
|
@ -65,11 +65,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
|
|||
* and those seven fields omit much of the information in Method.
|
||||
* @author jrose
|
||||
*/
|
||||
/*non-public*/
|
||||
final class ResolvedMethodName {
|
||||
//@Injected JVM_Method* vmtarget;
|
||||
//@Injected Class<?> vmholder;
|
||||
}
|
||||
|
||||
/*non-public*/
|
||||
final class MemberName implements Member, Cloneable {
|
||||
|
|
|
@ -57,6 +57,8 @@ import java.util.function.Function;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.MN_CALLER_SENSITIVE;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.MN_HIDDEN_MEMBER;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||
|
@ -1543,37 +1545,22 @@ abstract class MethodHandleImpl {
|
|||
static {
|
||||
SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
|
||||
@Override
|
||||
public Object newMemberName() {
|
||||
return new MemberName();
|
||||
public Class<?> getDeclaringClass(Object rmname) {
|
||||
ResolvedMethodName method = (ResolvedMethodName)rmname;
|
||||
return method.declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(Object mname) {
|
||||
MemberName memberName = (MemberName)mname;
|
||||
return memberName.getName();
|
||||
}
|
||||
@Override
|
||||
public Class<?> getDeclaringClass(Object mname) {
|
||||
MemberName memberName = (MemberName)mname;
|
||||
return memberName.getDeclaringClass();
|
||||
public MethodType getMethodType(String descriptor, ClassLoader loader) {
|
||||
return MethodType.fromDescriptor(descriptor, loader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodType getMethodType(Object mname) {
|
||||
MemberName memberName = (MemberName)mname;
|
||||
return memberName.getMethodType();
|
||||
public boolean isCallerSensitive(int flags) {
|
||||
return (flags & MN_CALLER_SENSITIVE) == MN_CALLER_SENSITIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodDescriptor(Object mname) {
|
||||
MemberName memberName = (MemberName)mname;
|
||||
return memberName.getMethodDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNative(Object mname) {
|
||||
MemberName memberName = (MemberName)mname;
|
||||
return memberName.isNative();
|
||||
public boolean isHiddenMember(int flags) {
|
||||
return (flags & MN_HIDDEN_MEMBER) == MN_HIDDEN_MEMBER;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -116,6 +116,7 @@ class MethodHandleNatives {
|
|||
MN_IS_TYPE = 0x00080000, // nested type
|
||||
MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
|
||||
MN_TRUSTED_FINAL = 0x00200000, // trusted final field
|
||||
MN_HIDDEN_MEMBER = 0x00400000, // members defined in a hidden class or with @Hidden
|
||||
MN_REFERENCE_KIND_SHIFT = 24, // refKind
|
||||
MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT;
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.lang.invoke;
|
||||
|
||||
final class ResolvedMethodName {
|
||||
//@Injected JVM_Method* vmtarget;
|
||||
private Class<?> vmholder;
|
||||
|
||||
Class<?> declaringClass() {
|
||||
return vmholder;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue