/** Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************/package java.lang;import jdk.internal.reflect.MethodAccessor;import jdk.internal.reflect.ConstructorAccessor;import java.lang.StackWalker.Option;import java.lang.StackWalker.StackFrame;import java.lang.annotation.Native;import java.lang.reflect.Method;import java.lang.reflect.Constructor;import java.util.HashSet;import java.util.NoSuchElementException;import java.util.Objects;import java.util.Set;import java.util.Spliterator;import java.util.function.Consumer;import java.util.function.Function;import java.util.stream.Stream;import java.util.stream.StreamSupport;import sun.security.action.GetPropertyAction;import static java.lang.StackStreamFactory.WalkerState.*;/*** StackStreamFactory class provides static factory methods* to get different kinds of stack walker/traverser.** AbstractStackWalker provides the basic stack walking support* fetching stack frames from VM in batches.** AbstractStackWalker subclass is specialized for a specific kind of stack traversal* to avoid overhead of Stream/Lambda* 1. Support traversing Stream<StackFrame>* 2. StackWalker::getCallerClass* 3. AccessControlContext getting ProtectionDomain*/final class StackStreamFactory {private StackStreamFactory() {}// Stack walk implementation classes to be excluded during stack walking// lazily add subclasses when they are loaded.private final static Set<Class<?>> stackWalkImplClasses = init();private static final int SMALL_BATCH = 8;private static final int BATCH_SIZE = 32;private static final int LARGE_BATCH_SIZE = 256;private static final int MIN_BATCH_SIZE = SMALL_BATCH;// These flags must match the values maintained in the VM@Native private static final int DEFAULT_MODE = 0x0;@Native private static final int FILL_CLASS_REFS_ONLY = 0x2;@Native private static final int GET_CALLER_CLASS = 0x4;@Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM@Native private static final int FILL_LIVE_STACK_FRAMES = 0x100;/** For Throwable to use StackWalker, set useNewThrowable to true.* Performance work and extensive testing is needed to replace the* VM built-in backtrace filled in Throwable with the StackWalker.*/final static boolean isDebug ="true".equals(GetPropertyAction.privilegedGetProperty("stackwalk.debug"));static <T> StackFrameTraverser<T>makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function){if (walker.hasLocalsOperandsOption())return new LiveStackInfoTraverser<>(walker, function);elsereturn new StackFrameTraverser<>(walker, function);}/*** Gets a stack stream to find caller class.*/static CallerClassFinder makeCallerFinder(StackWalker walker) {return new CallerClassFinder(walker);}enum WalkerState {NEW, // the stream is new and stack walking has not startedOPEN, // the stream is open when it is being traversed.CLOSED; // the stream is closed when the stack walking is done}/*** Subclass of AbstractStackWalker implements a specific stack walking logic.* It needs to set up the frame buffer and stack walking mode.** It initiates the VM stack walking via the callStackWalk method that serves* as the anchored frame and VM will call up to AbstractStackWalker::doStackWalk.** @param <R> the type of the result returned from stack walking* @param <T> the type of the data gathered for each frame.* For example, StackFrameInfo for StackWalker::walk or* Class<?> for StackWalker::getCallerClass*/static abstract class AbstractStackWalker<R, T> {protected final StackWalker walker;protected final Thread thread;protected final int maxDepth;protected final long mode;protected int depth; // traversed stack depthprotected FrameBuffer<? extends T> frameBuffer;protected long anchor;// buffers to fill in stack frame informationprotected AbstractStackWalker(StackWalker walker, int mode) {this(walker, mode, Integer.MAX_VALUE);}protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) {this.thread = Thread.currentThread();this.mode = toStackWalkMode(walker, mode);this.walker = walker;this.maxDepth = maxDepth;this.depth = 0;}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).** Each specialized AbstractStackWalker subclass implements the consumeFrames method* to control the following:* 1. fetch the subsequent batches of stack frames* 2. reuse or expand the allocated buffers* 3. create specialized StackFrame objects** @return the number of consumed frames*/protected abstract R consumeFrames();/*** Initialize FrameBuffer. Subclass should implement this method to* create its custom frame buffers.*/protected abstract void initFrameBuffer();/*** Returns the suggested next batch size.** Subclass should override this method to change the batch size** @param lastBatchFrameCount number of frames in the last batch; or zero* @return suggested batch size*/protected abstract int batchSize(int lastBatchFrameCount);/** Returns the next batch size, always >= minimum batch size (32)** Subclass may override this method if the minimum batch size is different.*/protected int getNextBatchSize() {int lastBatchSize = depth == 0 ? 0 : frameBuffer.curBatchFrameCount();int nextBatchSize = batchSize(lastBatchSize);if (isDebug) {System.err.println("last batch size = " + lastBatchSize +" next batch size = " + nextBatchSize);}return nextBatchSize >= MIN_BATCH_SIZE ? nextBatchSize : MIN_BATCH_SIZE;}/** Checks if this stream is in the given state. Otherwise, throws IllegalStateException.** VM also validates this stream if it's anchored for stack walking* when stack frames are fetched for each batch.*/final void checkState(WalkerState state) {if (thread != Thread.currentThread()) {throw new IllegalStateException("Invalid thread walking this stack stream: " +Thread.currentThread().getName() + " " + thread.getName());}switch (state) {case NEW:if (anchor != 0) {throw new IllegalStateException("This stack stream is being reused.");}break;case OPEN:if (anchor == 0 || anchor == -1L) {throw new IllegalStateException("This stack stream is not valid for walking.");}break;case CLOSED:if (anchor != -1L) {throw new IllegalStateException("This stack stream is not closed.");}}}/** Close this stream. This stream becomes invalid to walk.*/private void close() {this.anchor = -1L;}/** Walks stack frames until {@link #consumeFrames} is done consuming* the frames it is interested in.*/final R walk() {checkState(NEW);try {// VM will need to stablize the stack before walking. It will invoke// the AbstractStackWalker::doStackWalk method once it fetches the first batch.// the callback will be invoked within the scope of the callStackWalk frame.return beginStackWalk();} finally {close(); // done traversal; close the stream}}private boolean skipReflectionFrames() {return !walker.hasOption(Option.SHOW_REFLECT_FRAMES) &&!walker.hasOption(Option.SHOW_HIDDEN_FRAMES);}/** Returns {@code Class} object at the current frame;* or {@code null} if no more frame. If advanceToNextBatch is true,* it will only fetch the next batch.*/final Class<?> peekFrame() {while (frameBuffer.isActive() && depth < maxDepth) {if (frameBuffer.isEmpty()) {// fetch another batch of stack framesgetNextBatch();} else {Class<?> c = frameBuffer.get();if (skipReflectionFrames() && isReflectionFrame(c)) {if (isDebug)System.err.println(" skip: frame " + frameBuffer.getIndex() + " " + c);frameBuffer.next();depth++;continue;} else {return c;}}}return null;}/** This method is only invoked by VM.** It will invoke the consumeFrames method to start the stack walking* with the first batch of stack frames. Each specialized AbstractStackWalker* subclass implements the consumeFrames method to control the following:* 1. fetch the subsequent batches of stack frames* 2. reuse or expand the allocated buffers* 3. create specialized StackFrame objects*/private Object doStackWalk(long anchor, int skipFrames, int batchSize,int bufStartIndex, int bufEndIndex) {checkState(NEW);frameBuffer.check(skipFrames);if (isDebug) {System.err.format("doStackWalk: skip %d start %d end %d%n",skipFrames, bufStartIndex, bufEndIndex);}this.anchor = anchor; // set anchor for this bulk stack frame traversalframeBuffer.setBatch(depth, bufStartIndex, bufEndIndex);// traverse all frames and perform the action on the stack frames, if specifiedreturn consumeFrames();}/** Get next batch of stack frames.*/private int getNextBatch() {int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize());if (!frameBuffer.isActive() || nextBatchSize <= 0) {if (isDebug) {System.out.format(" more stack walk done%n");}frameBuffer.freeze(); // stack walk donereturn 0;}return fetchStackFrames(nextBatchSize);}/** This method traverses the next stack frame and returns the Class* invoking that stack frame.** This method can only be called during the walk method. This is intended* to be used to walk the stack frames in one single invocation and* this stack stream will be invalidated once walk is done.** @see #tryNextFrame*/final Class<?> nextFrame() {if (!hasNext()) {return null;}Class<?> c = frameBuffer.next();depth++;return c;}/** Returns true if there is next frame to be traversed.* This skips hidden frames unless this StackWalker has* {@link Option#SHOW_REFLECT_FRAMES}*/final boolean hasNext() {return peekFrame() != null;}/*** Begin stack walking - pass the allocated arrays to the VM to fill in* stack frame information.** VM first anchors the frame of the current thread. A traversable stream* on this thread's stack will be opened. The VM will fetch the first batch* of stack frames and call AbstractStackWalker::doStackWalk to invoke the* stack walking function on each stack frame.** If all fetched stack frames are traversed, AbstractStackWalker::fetchStackFrames will* fetch the next batch of stack frames to continue.*/private R beginStackWalk() {// initialize buffers for VM to fill the stack frame infoinitFrameBuffer();return callStackWalk(mode, 0,frameBuffer.curBatchFrameCount(),frameBuffer.startIndex(),frameBuffer.frames());}/** Fetches stack frames.** @params batchSize number of elements of the frame buffers for this batch* @returns number of frames fetched in this batch*/private int fetchStackFrames(int batchSize) {int startIndex = frameBuffer.startIndex();frameBuffer.resize(startIndex, batchSize);int endIndex = fetchStackFrames(mode, anchor, batchSize,startIndex,frameBuffer.frames());if (isDebug) {System.out.format(" more stack walk requesting %d got %d to %d frames%n",batchSize, frameBuffer.startIndex(), endIndex);}int numFrames = endIndex - startIndex;if (numFrames == 0) {frameBuffer.freeze(); // done stack walking} else {frameBuffer.setBatch(depth, startIndex, endIndex);}return numFrames;}/*** Begins stack walking. This method anchors this frame and invokes* AbstractStackWalker::doStackWalk after fetching the first batch of stack frames.** @param mode mode of stack walking* @param skipframes number of frames to be skipped before filling the frame buffer.* @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}* or a {@link StackFrameInfo} (or derivative) array otherwise.* @return Result of AbstractStackWalker::doStackWalk*/private native R callStackWalk(long mode, int skipframes,int batchSize, int startIndex,T[] frames);/*** Fetch the next batch of stack frames.** @param mode mode of stack walking* @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}* or a {@link StackFrameInfo} (or derivative) array otherwise.** @return the end index to the frame buffers*/private native int fetchStackFrames(long mode, long anchor,int batchSize, int startIndex,T[] frames);}/** This StackFrameTraverser supports {@link Stream} traversal.** This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.*/static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrameInfo>implements Spliterator<StackFrame>{static {stackWalkImplClasses.add(StackFrameTraverser.class);}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);}}@OverrideStackFrameInfo[] frames() {return stackFrames;}@Overridevoid 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;}@OverrideStackFrameInfo nextStackFrame() {if (isEmpty()) {throw new NoSuchElementException("origin=" + origin + " fence=" + fence);}StackFrameInfo frame = stackFrames[origin];origin++;return frame;}@Overridefinal Class<?> at(int index) {return stackFrames[index].declaringClass();}}final Function<? super Stream<StackFrame>, ? extends T> function; // callbackStackFrameTraverser(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);this.function = function;}/*** Returns next StackFrame object in the current batch of stack frames;* or null if no more stack frame.*/StackFrame nextStackFrame() {if (!hasNext()) {return null;}StackFrameInfo frame = frameBuffer.nextStackFrame();depth++;return frame;}@Overrideprotected T consumeFrames() {checkState(OPEN);Stream<StackFrame> stream = StreamSupport.stream(this, false);if (function != null) {return function.apply(stream);} elsethrow new UnsupportedOperationException();}@Overrideprotected void initFrameBuffer() {this.frameBuffer = new StackFrameBuffer(getNextBatchSize());}@Overrideprotected int batchSize(int lastBatchFrameCount) {if (lastBatchFrameCount == 0) {// First batch, use estimateDepth if not exceed the large batch size// and not too smallint initialBatchSize = Math.max(walker.estimateDepth(), SMALL_BATCH);return Math.min(initialBatchSize, LARGE_BATCH_SIZE);} else {if (lastBatchFrameCount > BATCH_SIZE) {return lastBatchFrameCount;} else {return Math.min(lastBatchFrameCount*2, BATCH_SIZE);}}}// ------- Implementation of Spliterator@Overridepublic Spliterator<StackFrame> trySplit() {return null; // ordered stream and do not allow to split}@Overridepublic long estimateSize() {return maxDepth;}@Overridepublic int characteristics() {return CHARACTERISTICS;}@Overridepublic void forEachRemaining(Consumer<? super StackFrame> action) {checkState(OPEN);for (int n = 0; n < maxDepth; n++) {StackFrame frame = nextStackFrame();if (frame == null) break;action.accept(frame);}}@Overridepublic boolean tryAdvance(Consumer<? super StackFrame> action) {checkState(OPEN);int index = frameBuffer.getIndex();if (hasNext()) {StackFrame frame = nextStackFrame();action.accept(frame);if (isDebug) {System.err.println("tryAdvance: " + index + " " + frame);}return true;}if (isDebug) {System.err.println("tryAdvance: " + index + " NO element");}return false;}}/** CallerClassFinder is specialized to return Class<?> for each stack frame.* StackFrame is not requested.*/static final class CallerClassFinder extends AbstractStackWalker<Integer, Class<?>> {static {stackWalkImplClasses.add(CallerClassFinder.class);}private Class<?> caller;CallerClassFinder(StackWalker walker) {super(walker, FILL_CLASS_REFS_ONLY|GET_CALLER_CLASS);}final class ClassBuffer extends FrameBuffer<Class<?>> {Class<?>[] classes; // caller class for fast pathClassBuffer(int batchSize) {super(batchSize);classes = new Class<?>[batchSize];}@OverrideClass<?>[] frames() { return classes;}@Overridefinal 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.**/@Overridevoid 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 objectClass<?>[] prev = classes;classes = new Class<?>[size];System.arraycopy(prev, 0, classes, 0, startIndex);}currentBatchSize = size;}}Class<?> findCaller() {walk();return caller;}@Overrideprotected 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 classwhile (n < 2 && (caller = nextFrame()) != null) {if (isMethodHandleFrame(caller)) { continue; }if (isReflectionFrame(caller)) { continue; }frames[n++] = caller;}if (frames[1] == null) {throw new IllegalCallerException("no caller frame");}return n;}@Overrideprotected void initFrameBuffer() {this.frameBuffer = new ClassBuffer(getNextBatchSize());}@Overrideprotected int batchSize(int lastBatchFrameCount) {return MIN_BATCH_SIZE;}@Overrideprotected int getNextBatchSize() {return MIN_BATCH_SIZE;}}static final class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {static {stackWalkImplClasses.add(LiveStackInfoTraverser.class);}// VM will fill in all method info and live stack info directly in StackFrameInfofinal 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);}}@OverrideLiveStackFrameInfo[] frames() {return stackFrames;}@Overridevoid 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;}@OverrideLiveStackFrameInfo nextStackFrame() {if (isEmpty()) {throw new NoSuchElementException("origin=" + origin + " fence=" + fence);}LiveStackFrameInfo frame = stackFrames[origin];origin++;return frame;}@Overridefinal Class<?> at(int index) {return stackFrames[index].declaringClass();}}LiveStackInfoTraverser(StackWalker walker,Function<? super Stream<StackFrame>, ? extends T> function) {super(walker, function, DEFAULT_MODE);}@Overrideprotected void initFrameBuffer() {this.frameBuffer = new LiveStackFrameBuffer(getNextBatchSize());}}/** Frame buffer** Each specialized AbstractStackWalker subclass may subclass the FrameBuffer.*/static abstract class FrameBuffer<F> {static final int START_POS = 2; // 0th and 1st elements are reserved// buffers for VM to fill stack frame infoint currentBatchSize; // current batch sizeint origin; // index to the current traversed stack frameint fence; // index to the last frame in the current batchFrameBuffer(int initialBatchSize) {if (initialBatchSize < MIN_BATCH_SIZE) {throw new IllegalArgumentException(initialBatchSize +" < minimum batch size: " + MIN_BATCH_SIZE);}this.origin = START_POS;this.fence = 0;this.currentBatchSize = initialBatchSize;}/*** Returns an array of frames that may be used to store frame objects* 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* {@link StackFrameInfo} (or derivative) array otherwise.** @return An array of frames that may be used to store frame objects* when walking the stack. Must not be null.*/abstract F[] frames(); // must not return null/*** 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.**/abstract void resize(int startIndex, int elements);/*** Return the class at the given position in the current batch.* @param index the position of the frame.* @return the class at the given position in the current batch.*/abstract Class<?> at(int index);// ------ subclass may override the following methods -------/** Returns the start index for this frame buffer is refilled.** This implementation reuses the allocated buffer for the next batch* of stack frames. For subclass to retain the fetched stack frames,* it should override this method to return the index at which the frame* should be filled in for the next batch.*/int startIndex() {return START_POS;}/*** Returns next StackFrame object in the current batch of stack frames*/F nextStackFrame() {throw new InternalError("should not reach here");}// ------ FrameBuffer implementation ------final int curBatchFrameCount() {return currentBatchSize-START_POS;}/** Tests if this frame buffer is empty. All frames are fetched.*/final boolean isEmpty() {return origin >= fence || (origin == START_POS && fence == 0);}/** Freezes this frame buffer. The stack stream source is done fetching.*/final void freeze() {origin = 0;fence = 0;}/** Tests if this frame buffer is active. It is inactive when* it is done for traversal. All stack frames have been traversed.*/final boolean isActive() {return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);}/*** Gets the class at the current frame and move to the next frame.*/final Class<?> next() {if (isEmpty()) {throw new NoSuchElementException("origin=" + origin + " fence=" + fence);}Class<?> c = at(origin);origin++;if (isDebug) {int index = origin-1;System.out.format(" next frame at %d: %s (origin %d fence %d)%n", index,Objects.toString(c), index, fence);}return c;}/*** Gets the class at the current frame.*/final Class<?> get() {if (isEmpty()) {throw new NoSuchElementException("origin=" + origin + " fence=" + fence);}return at(origin);}/** Returns the index of the current frame.*/final int getIndex() {return origin;}/** Set the start and end index of a new batch of stack frames that have* been filled in this frame buffer.*/final void setBatch(int depth, int startIndex, int endIndex) {if (startIndex <= 0 || endIndex <= 0)throw new IllegalArgumentException("startIndex=" + startIndex+ " endIndex=" + endIndex);this.origin = startIndex;this.fence = endIndex;if (depth == 0 && fence > 0) {// filter the frames due to the stack stream implementationfor (int i = START_POS; i < fence; i++) {Class<?> c = at(i);if (isDebug) System.err.format(" frame %d: %s%n", i, c);if (filterStackWalkImpl(c)) {origin++;} else {break;}}}}/** Checks if the origin is the expected start index.*/final void check(int skipFrames) {int index = skipFrames + START_POS;if (origin != index) {// stack walk must continue with the previous frame depththrow new IllegalStateException("origin " + origin + " != " + index);}}}private static native boolean checkStackWalkModes();// avoid loading other subclasses as they may not be usedprivate static Set<Class<?>> init() {if (!checkStackWalkModes()) {throw new InternalError("StackWalker mode values do not match with JVM");}Set<Class<?>> classes = new HashSet<>();classes.add(StackWalker.class);classes.add(StackStreamFactory.class);classes.add(AbstractStackWalker.class);return classes;}private static boolean filterStackWalkImpl(Class<?> c) {return stackWalkImplClasses.contains(c) ||c.getName().startsWith("java.util.stream.");}// MethodHandle frames are not hidden and CallerClassFinder has// to filter them outprivate static boolean isMethodHandleFrame(Class<?> c) {return c.getName().startsWith("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");}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。