Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/jcl/src/java.base/share/classes/java/lang/StackWalker.java
12513 views
1
/*[INCLUDE-IF JAVA_SPEC_VERSION > 8]*/
2
/*******************************************************************************
3
* Copyright (c) 2016, 2022 IBM Corp. and others
4
*
5
* This program and the accompanying materials are made available under
6
* the terms of the Eclipse Public License 2.0 which accompanies this
7
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
8
* or the Apache License, Version 2.0 which accompanies this distribution and
9
* is available at https://www.apache.org/licenses/LICENSE-2.0.
10
*
11
* This Source Code may also be made available under the following
12
* Secondary Licenses when the conditions for such availability set
13
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
14
* General Public License, version 2 with the GNU Classpath
15
* Exception [1] and GNU General Public License, version 2 with the
16
* OpenJDK Assembly Exception [2].
17
*
18
* [1] https://www.gnu.org/software/classpath/license.html
19
* [2] http://openjdk.java.net/legal/assembly-exception.html
20
*
21
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
22
*******************************************************************************/
23
package java.lang;
24
25
import java.lang.StackWalker.StackFrameImpl;
26
/*[IF JAVA_SPEC_VERSION >= 10]*/
27
import java.lang.invoke.MethodType;
28
/*[ENDIF] JAVA_SPEC_VERSION >= 10 */
29
import java.lang.module.ModuleDescriptor;
30
import java.lang.module.ModuleDescriptor.Version;
31
import java.security.Permission;
32
import java.util.Collections;
33
import java.util.HashSet;
34
import java.util.List;
35
import java.util.Objects;
36
import java.util.Optional;
37
import java.util.Set;
38
import java.util.function.Consumer;
39
import java.util.function.Function;
40
import java.util.stream.Collectors;
41
import java.util.stream.Stream;
42
43
/**
44
* This provides a facility for iterating over the call stack of the current
45
* thread. A StackWalker object may be used multiple times by different threads,
46
* but it will represent the state of the current thread at the time the stack
47
* is walked. A StackWalker may be provided with one or more Option settings to
48
* include information and stack frames such as reflection methods, hidden
49
* frames, and Class objects.
50
*/
51
public final class StackWalker {
52
53
private static final int DEFAULT_BUFFER_SIZE = 1;
54
private final static int J9_RETAIN_CLASS_REFERENCE = 1;
55
private final static int J9_SHOW_REFLECT_FRAMES = 2;
56
private final static int J9_SHOW_HIDDEN_FRAMES = 4;
57
58
final Set<Option> walkerOptions;
59
60
private final int bufferSize;
61
private int flags;
62
63
private StackWalker(Set<Option> options, int estimatedDepth) {
64
super();
65
/* Caller is responsible for copying the client set (if any) */
66
walkerOptions = options;
67
bufferSize = estimatedDepth;
68
if (estimatedDepth <= 0) {
69
/*[MSG "K0641", "estimatedDepth must be greater than 0"]*/
70
throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0641")); //$NON-NLS-1$
71
}
72
if (options.contains(Option.RETAIN_CLASS_REFERENCE)) {
73
@SuppressWarnings("removal")
74
SecurityManager securityMgr = System.getSecurityManager();
75
if (null != securityMgr) {
76
securityMgr.checkPermission(PermissionSingleton.perm);
77
}
78
}
79
flags = 0;
80
if (walkerOptions.contains(Option.RETAIN_CLASS_REFERENCE)) {
81
flags |= J9_RETAIN_CLASS_REFERENCE;
82
}
83
if (walkerOptions.contains(Option.SHOW_REFLECT_FRAMES)) {
84
flags |= J9_SHOW_REFLECT_FRAMES;
85
}
86
if (walkerOptions.contains(Option.SHOW_HIDDEN_FRAMES)) {
87
flags |= J9_SHOW_HIDDEN_FRAMES;
88
}
89
}
90
91
/**
92
* Factory method to create a StackWalker instance with no options set.
93
*
94
* @return StackWalker StackWalker object
95
*/
96
public static StackWalker getInstance() {
97
return getInstance(Collections.emptySet(), DEFAULT_BUFFER_SIZE);
98
}
99
100
/**
101
* Factory method to create a StackWalker with one option. This is provided
102
* for the case where only a single option is required.
103
*
104
* @param option
105
* select the type of information to include
106
* @return StackWalker instance configured with the value of option
107
* @throws SecurityException
108
* if option is RETAIN_CLASS_REFERENCE and the security manager
109
* check fails.
110
*/
111
public static StackWalker getInstance(Option option) {
112
Objects.requireNonNull(option);
113
return getInstance(Collections.singleton(option), DEFAULT_BUFFER_SIZE);
114
}
115
116
/**
117
* Factory method to create a StackWalker with any number of options.
118
*
119
* @param options
120
* select the types of information to include.
121
* @return StackWalker instance configured with the given options
122
* @throws SecurityException
123
* if RETAIN_CLASS_REFERENCE is requested and not permitted by
124
* the security manager
125
*/
126
public static StackWalker getInstance(Set<Option> options) {
127
return getInstance(new HashSet<>(options), DEFAULT_BUFFER_SIZE);
128
}
129
130
/**
131
* Factory method to create a StackWalker.
132
*
133
* @param options
134
* select the types of information to include.
135
* @param estimatedDepth
136
* Hint for the size of buffer to use. Must be 1 or greater.
137
* @return StackWalker instance with the given options specifying the stack
138
* frame information it can access.
139
* @throws SecurityException
140
* if RETAIN_CLASS_REFERENCE is requested and not permitted by
141
* the security manager
142
*/
143
public static StackWalker getInstance(Set<Option> options, int estimatedDepth) {
144
return new StackWalker(new HashSet<>(options), estimatedDepth);
145
}
146
147
/**
148
* @param action
149
* {@link Consumer} object Iterate over the stack from top to
150
* bottom and apply the {@link Consumer} to each
151
* {@link StackFrame}
152
*/
153
public void forEach(Consumer<? super StackFrame> action) {
154
walkWrapperImpl(flags, "forEach", s -> { //$NON-NLS-1$
155
s.forEach(action);
156
return null;
157
});
158
}
159
160
/**
161
* Get the caller of the caller of this function, eliding any reflection or
162
* hidden frames.
163
*
164
* @return Class object for the method calling the current method.
165
* @throws UnsupportedOperationException
166
* if the StackWalker was not created with
167
* {@link Option#RETAIN_CLASS_REFERENCE}
168
* @throws IllegalStateException
169
* if the caller is at the bottom of the stack.
170
*/
171
public Class<?> getCallerClass() {
172
if (!walkerOptions.contains(Option.RETAIN_CLASS_REFERENCE)) {
173
/*[MSG "K0639", "Stack walker not configured with RETAIN_CLASS_REFERENCE"]*/
174
throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0639")); //$NON-NLS-1$
175
}
176
/*
177
* Get the top two stack frames: the client calling getCallerClass and
178
* the client's caller. Ignore reflection and special frames.
179
*/
180
List<StackFrame> result = StackWalker.walkWrapperImpl(J9_RETAIN_CLASS_REFERENCE, "getCallerClass", //$NON-NLS-1$
181
s -> s.limit(2).collect(Collectors.toList()));
182
if (result.size() < 2) {
183
/*[MSG "K0640", "getCallerClass() called from method with no caller"]*/
184
throw new IllegalCallerException(com.ibm.oti.util.Msg.getString("K0640")); //$NON-NLS-1$
185
}
186
if (((StackFrameImpl)result.get(0)).callerSensitive) {
187
/*[MSG "K0644", "Caller-sensitive method called StackWalker.getCallerClass()"]*/
188
throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0644")); //$NON-NLS-1$
189
}
190
StackFrame clientsCaller = result.get(1);
191
192
return clientsCaller.getDeclaringClass();
193
}
194
195
private native static <T> T walkWrapperImpl(int flags, String walkerMethod,
196
Function<? super Stream<StackFrame>, ? extends T> function);
197
198
/**
199
* Traverse the calling thread's stack at the time this method is called and
200
* apply {@code function} to each stack frame.
201
*
202
* @param <T> the type of the return value from applying function to the stream
203
* @param function operation to apply to the stream
204
* @param walkState Pointer to a J9StackWalkState struct
205
* @return the value returned by {@code function}.
206
*/
207
private static <T> T walkImpl(Function<? super Stream<StackFrame>, ? extends T> function, long walkState) {
208
T result;
209
try (Stream<StackFrame> frameStream = Stream.iterate(getImpl(walkState), x -> (null != x), x -> getImpl(walkState))) {
210
result = function.apply(frameStream);
211
}
212
return result;
213
}
214
215
private static native StackFrameImpl getImpl(long walkState);
216
217
/**
218
* Traverse the calling thread's stack at the time this method is called and
219
* apply {@code function} to each stack frame.
220
*
221
* @param <T> the type of the return value from applying function to the stream
222
* @param function operation to apply to the stream
223
* @return the value returned by {@code function}.
224
*/
225
public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function) {
226
return walkWrapperImpl(flags, "walk", function); //$NON-NLS-1$
227
}
228
229
/**
230
* Selects what type of stack and method information is provided by the
231
* StackWalker
232
*
233
*/
234
public static enum Option {
235
/**
236
* Allow clients to obtain a method's Class object.
237
*/
238
RETAIN_CLASS_REFERENCE,
239
/**
240
* Include stack frames for reflection methods.
241
*/
242
SHOW_REFLECT_FRAMES,
243
/**
244
* Include stack frames for reflection methods, as well as JVM special
245
* stack frames, such as frames for anonymous classes.
246
*/
247
SHOW_HIDDEN_FRAMES;
248
}
249
250
/**
251
* Contains information about the StackWalker's current stack frame.
252
*/
253
public static interface StackFrame {
254
255
/**
256
* @return the offset of the current bytecode in the method represented
257
* by this frame.
258
*/
259
int getByteCodeIndex();
260
261
/**
262
* @return the binary name of the declaring class of this frame's
263
* method.
264
*/
265
String getClassName();
266
267
/**
268
* @return the Class object of the declaring class of this frame's
269
* method.
270
* @throws UnsupportedOperationException
271
* if the StackWalker was not created with
272
* Option.RETAIN_CLASS_REFERENCE
273
*/
274
Class<?> getDeclaringClass();
275
276
/**
277
* @return File name of the class containing the current method. May be
278
* null.
279
*/
280
String getFileName();
281
282
/**
283
* @return Location of the current point of execution in the source
284
* file, or a negative number if this information is unavailable
285
* or the method is native.
286
*/
287
int getLineNumber();
288
289
/**
290
* @return the name of this StackFrame's method
291
*/
292
String getMethodName();
293
294
/**
295
* @return true if the method represented by this StackFrame is a native
296
* method
297
*/
298
boolean isNativeMethod();
299
300
/**
301
* Converts this StackFrame into a StackTraceElement.
302
*
303
* @return StackTraceElement
304
*/
305
StackTraceElement toStackTraceElement();
306
307
/*[IF JAVA_SPEC_VERSION >= 10]*/
308
/**
309
* @throws UnsupportedOperationException if this method is not overridden
310
* @return MethodType containing the parameter and return types for the associated method.
311
* @since 10
312
*/
313
default MethodType getMethodType() {
314
throw new UnsupportedOperationException();
315
}
316
317
/**
318
* @throws UnsupportedOperationException if this method is not overridden or the StackWalker
319
* instance is not configured with RETAIN_CLASS_REFERENCE.
320
* @return method descriptor string representing the type of this frame's method.
321
* @since 10
322
*/
323
default String getDescriptor() {
324
throw new UnsupportedOperationException();
325
}
326
327
/*[ENDIF] JAVA_SPEC_VERSION >= 10 */
328
}
329
330
final static class StackFrameImpl implements StackFrame {
331
332
private Class<?> declaringClass;
333
private String fileName;
334
private int bytecodeIndex;
335
private String classLoaderName;
336
private String className;
337
private int lineNumber;
338
private Module frameModule;
339
private String methodName;
340
private String methodSignature;
341
boolean callerSensitive;
342
343
@Override
344
public int getByteCodeIndex() {
345
return bytecodeIndex;
346
}
347
348
@Override
349
public String getClassName() {
350
return className;
351
}
352
353
@Override
354
public Class<?> getDeclaringClass() {
355
if (null == declaringClass) {
356
/*[MSG "K0639","Stack walker not configured with RETAIN_CLASS_REFERENCE"]*/
357
throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0639")); //$NON-NLS-1$
358
} else {
359
return declaringClass;
360
}
361
}
362
363
@Override
364
public String getFileName() {
365
return fileName;
366
}
367
368
@Override
369
public int getLineNumber() {
370
return lineNumber;
371
}
372
373
@Override
374
public String getMethodName() {
375
return methodName;
376
}
377
378
@Override
379
public boolean isNativeMethod() {
380
return -2 == lineNumber;
381
}
382
383
@Override
384
public StackTraceElement toStackTraceElement() {
385
String moduleName = null;
386
String moduleVersion = null;
387
if (null != frameModule && frameModule.isNamed()) {
388
ModuleDescriptor desc = frameModule.getDescriptor();
389
moduleName = desc.name();
390
Optional<Version> versionInfo = desc.version();
391
if (versionInfo.isPresent()) {
392
moduleVersion = versionInfo.get().toString();
393
}
394
}
395
396
StackTraceElement element = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName,
397
lineNumber);
398
399
/**
400
* Disable including classloader name and module version in stack trace output
401
* until StackWalker StackTraceElement include info flags can be set properly.
402
*
403
* See: https://github.com/eclipse-openj9/openj9/issues/11774
404
*/
405
element.disableIncludeInfoFlags();
406
407
return element;
408
}
409
410
@Override
411
public String toString() {
412
StackTraceElement stackTraceElement = toStackTraceElement();
413
return stackTraceElement.toString();
414
}
415
416
/*[IF JAVA_SPEC_VERSION >= 10]*/
417
/**
418
* Creates a MethodType object for the method associated with this frame.
419
* @throws UnsupportedOperationException if the StackWalker object is not configured with RETAIN_CLASS_REFERENCE
420
* @return MethodType object
421
* @since 10
422
*/
423
@Override
424
public MethodType getMethodType() {
425
if (null == declaringClass) {
426
/*[MSG "K0639","Stack walker not configured with RETAIN_CLASS_REFERENCE"]*/
427
throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0639")); //$NON-NLS-1$
428
}
429
return MethodType.fromMethodDescriptorString(methodSignature, declaringClass.internalGetClassLoader());
430
}
431
432
/**
433
* Creates a string containing the signature of the method associated with this frame.
434
* @return String signature
435
* @since 10
436
*/
437
@Override
438
public java.lang.String getDescriptor() {
439
return methodSignature;
440
}
441
/*[ENDIF] JAVA_SPEC_VERSION >= 10 */
442
443
}
444
445
static class PermissionSingleton {
446
static final Permission perm =
447
new RuntimePermission("getStackWalkerWithClassReference"); //$NON-NLS-1$
448
}
449
}
450
451