8143911: Reintegrate JEP 259: Stack-Walking API
Reviewed-by: coleenp, dfuchs, bchristi, psandoz, sspitsyn
Contributed-by: Mandy Chung <mandy.chung@oracle.com>, Brent Christian <brent.christian@oracle.com>, Daniel Fuchs <daniel.fuchs@oracle.com>, Hamlin Li <huaming.li@oracle.com>
--- a/jdk/make/mapfiles/libjava/mapfile-vers Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Tue Nov 24 15:05:58 2015 -0800
@@ -138,9 +138,14 @@
Java_java_lang_Double_longBitsToDouble;
Java_java_lang_Double_doubleToRawLongBits;
Java_java_lang_reflect_Proxy_defineClass0;
- Java_java_lang_Shutdown_runAllFinalizers;
Java_java_lang_Float_intBitsToFloat;
Java_java_lang_Float_floatToRawIntBits;
+ Java_java_lang_StackFrameInfo_fillInStackFrames;
+ Java_java_lang_StackFrameInfo_setMethodInfo;
+ Java_java_lang_StackStreamFactory_checkStackWalkModes;
+ Java_java_lang_StackStreamFactory_00024AbstractStackWalker_callStackWalk;
+ Java_java_lang_StackStreamFactory_00024AbstractStackWalker_fetchStackFrames;
+ Java_java_lang_Shutdown_runAllFinalizers;
Java_java_lang_StrictMath_IEEEremainder;
Java_java_lang_StrictMath_acos;
Java_java_lang_StrictMath_asin;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/LiveStackFrame.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 java.lang.StackWalker.StackFrame;
+import java.util.EnumSet;
+import java.util.Set;
+
+import static java.lang.StackWalker.ExtendedOption.LOCALS_AND_OPERANDS;
+
+/**
+ * <em>UNSUPPORTED</em> This interface is intended to be package-private
+ * or move to an internal package.<p>
+ *
+ * {@code LiveStackFrame} represents a frame storing data and partial results.
+ * Each frame has its own array of local variables (JVMS section 2.6.1),
+ * its own operand stack (JVMS section 2.6.2) for a method invocation.
+ *
+ * @jvms 2.6 Frames
+ */
+/* package-private */
+interface LiveStackFrame extends StackFrame {
+ /**
+ * Return the monitors held by this stack frame. This method returns
+ * an empty array if no monitor is held by this stack frame.
+ *
+ * @return the monitors held by this stack frames
+ */
+ public Object[] getMonitors();
+
+ /**
+ * Gets the local variable array of this stack frame.
+ *
+ * <p>A single local variable can hold a value of type boolean, byte, char,
+ * short, int, float, reference or returnAddress. A pair of local variables
+ * can hold a value of type long or double. In other words,
+ * a value of type long or type double occupies two consecutive local
+ * variables. For a value of primitive type, the element in the
+ * local variable array is an {@link PrimitiveValue} object;
+ * otherwise, the element is an {@code Object}.
+ *
+ * @return the local variable array of this stack frame.
+ */
+ public Object[] getLocals();
+
+ /**
+ * Gets the operand stack of this stack frame.
+ *
+ * <p>
+ * The 0-th element of the returned array represents the top of the operand stack.
+ * This method returns an empty array if the operand stack is empty.
+ *
+ * <p>Each entry on the operand stack can hold a value of any Java Virtual
+ * Machine Type.
+ * For a value of primitive type, the element in the returned array is
+ * an {@link PrimitiveValue} object; otherwise, the element is the {@code Object}
+ * on the operand stack.
+ *
+ * @return the operand stack of this stack frame.
+ */
+ public Object[] getStack();
+
+ /**
+ * <em>UNSUPPORTED</em> This interface is intended to be package-private
+ * or move to an internal package.<p>
+ *
+ * Represents a local variable or an entry on the operand whose value is
+ * of primitive type.
+ */
+ public abstract class PrimitiveValue {
+ /**
+ * Returns the base type of this primitive value, one of
+ * {@code B, D, C, F, I, J, S, Z}.
+ *
+ * @return Name of a base type
+ * @jvms table 4.3-A
+ */
+ abstract char type();
+
+ /**
+ * Returns the boolean value if this primitive value is of type boolean.
+ * @return the boolean value if this primitive value is of type boolean.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type boolean.
+ */
+ public boolean booleanValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the int value if this primitive value is of type int.
+ * @return the int value if this primitive value is of type int.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type int.
+ */
+ public int intValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the long value if this primitive value is of type long.
+ * @return the long value if this primitive value is of type long.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type long.
+ */
+ public long longValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the char value if this primitive value is of type char.
+ * @return the char value if this primitive value is of type char.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type char.
+ */
+ public char charValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the byte value if this primitive value is of type byte.
+ * @return the byte value if this primitive value is of type byte.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type byte.
+ */
+ public byte byteValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the short value if this primitive value is of type short.
+ * @return the short value if this primitive value is of type short.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type short.
+ */
+ public short shortValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the float value if this primitive value is of type float.
+ * @return the float value if this primitive value is of type float.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type float.
+ */
+ public float floatValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+
+ /**
+ * Returns the double value if this primitive value is of type double.
+ * @return the double value if this primitive value is of type double.
+ *
+ * @throws UnsupportedOperationException if this primitive value is not
+ * of type double.
+ */
+ public double doubleValue() {
+ throw new UnsupportedOperationException("this primitive of type " + type());
+ }
+ }
+
+
+ /**
+ * Gets {@code StackWalker} that can get locals and operands.
+ *
+ * @throws SecurityException if the security manager is present and
+ * denies access to {@code RuntimePermission("liveStackFrames")}
+ */
+ public static StackWalker getStackWalker() {
+ return getStackWalker(EnumSet.noneOf(StackWalker.Option.class));
+ }
+
+ /**
+ * Gets a {@code StackWalker} instance with the given options specifying
+ * the stack frame information it can access, and which will traverse at most
+ * the given {@code maxDepth} number of stack frames. If no option is
+ * specified, this {@code StackWalker} obtains the method name and
+ * the class name with all
+ * {@linkplain StackWalker.Option#SHOW_HIDDEN_FRAMES hidden frames} skipped.
+ * The returned {@code StackWalker} can get locals and operands.
+ *
+ * @param options stack walk {@link StackWalker.Option options}
+ *
+ * @throws SecurityException if the security manager is present and
+ * it denies access to {@code RuntimePermission("liveStackFrames")}; or
+ * or if the given {@code options} contains
+ * {@link StackWalker.Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE}
+ * and it denies access to {@code StackFramePermission("retainClassReference")}.
+ */
+ public static StackWalker getStackWalker(Set<StackWalker.Option> options) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new RuntimePermission("liveStackFrames"));
+ }
+ return StackWalker.newInstance(options, LOCALS_AND_OPERANDS);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/LiveStackFrameInfo.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 java.lang.StackWalker.Option;
+import java.util.EnumSet;
+import java.util.Set;
+
+import static java.lang.StackWalker.ExtendedOption.*;
+
+final class LiveStackFrameInfo extends StackFrameInfo implements LiveStackFrame {
+ private static Object[] EMPTY_ARRAY = new Object[0];
+
+ LiveStackFrameInfo(StackWalker walker) {
+ super(walker);
+ }
+
+ // These fields are initialized by the VM if ExtendedOption.LOCALS_AND_OPERANDS is set
+ private Object[] monitors = EMPTY_ARRAY;
+ private Object[] locals = EMPTY_ARRAY;
+ private Object[] operands = EMPTY_ARRAY;
+
+ @Override
+ public Object[] getMonitors() {
+ return monitors;
+ }
+
+ @Override
+ public Object[] getLocals() {
+ return locals;
+ }
+
+ @Override
+ public Object[] getStack() {
+ return operands;
+ }
+
+ /*
+ * Convert primitive value to {@code Primitive} object to represent
+ * a local variable or an element on the operand stack of primitive type.
+ */
+ static PrimitiveValue asPrimitive(boolean value) {
+ return new BooleanPrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(int value) {
+ return new IntPrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(short value) {
+ return new ShortPrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(char value) {
+ return new CharPrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(byte value) {
+ return new BytePrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(long value) {
+ return new LongPrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(float value) {
+ return new FloatPrimitive(value);
+ }
+
+ static PrimitiveValue asPrimitive(double value) {
+ return new DoublePrimitive(value);
+ }
+
+ private static class IntPrimitive extends PrimitiveValue {
+ final int value;
+ IntPrimitive(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'I';
+ }
+
+ @Override
+ public int intValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class ShortPrimitive extends PrimitiveValue {
+ final short value;
+ ShortPrimitive(short value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'S';
+ }
+
+ @Override
+ public short shortValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class BooleanPrimitive extends PrimitiveValue {
+ final boolean value;
+ BooleanPrimitive(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'Z';
+ }
+
+ @Override
+ public boolean booleanValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class CharPrimitive extends PrimitiveValue {
+ final char value;
+ CharPrimitive(char value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'C';
+ }
+
+ @Override
+ public char charValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class BytePrimitive extends PrimitiveValue {
+ final byte value;
+ BytePrimitive(byte value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'B';
+ }
+
+ @Override
+ public byte byteValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class LongPrimitive extends PrimitiveValue {
+ final long value;
+ LongPrimitive(long value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'J';
+ }
+
+ @Override
+ public long longValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class FloatPrimitive extends PrimitiveValue {
+ final float value;
+ FloatPrimitive(float value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'F';
+ }
+
+ @Override
+ public float floatValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+
+ private static class DoublePrimitive extends PrimitiveValue {
+ final double value;
+ DoublePrimitive(double value) {
+ this.value = value;
+ }
+
+ @Override
+ public char type() {
+ return 'D';
+ }
+
+ @Override
+ public double doubleValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StackFrameInfo.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.misc.JavaLangInvokeAccess;
+import jdk.internal.misc.SharedSecrets;
+
+import static java.lang.StackWalker.Option.*;
+import java.lang.StackWalker.StackFrame;
+import java.util.Optional;
+import java.util.OptionalInt;
+
+class StackFrameInfo implements StackFrame {
+ private final static JavaLangInvokeAccess jlInvokeAccess =
+ SharedSecrets.getJavaLangInvokeAccess();
+
+ // -XX:+MemberNameInStackFrame will initialize MemberName and all other fields;
+ // otherwise, VM will set the hidden fields (injected by the VM).
+ // -XX:+MemberNameInStackFrame is temporary to enable performance measurement
+ //
+ // Footprint improvement: MemberName::clazz and MemberName::name
+ // can replace StackFrameInfo::declaringClass and StackFrameInfo::methodName
+ // Currently VM sets StackFrameInfo::methodName instead of expanding MemberName::name
+
+ final StackWalker walker;
+ final Class<?> declaringClass;
+ final Object memberName;
+ final int bci;
+
+ // methodName, fileName, and lineNumber will be lazily set by the VM
+ // when first requested.
+ private String methodName;
+ private String fileName = null; // default for unavailable filename
+ private int lineNumber = -1; // default for unavailable lineNumber
+
+ /*
+ * Create StackFrameInfo for StackFrameTraverser and LiveStackFrameTraverser
+ * to use
+ */
+ StackFrameInfo(StackWalker walker) {
+ this.walker = walker;
+ this.declaringClass = null;
+ this.bci = -1;
+ this.memberName = jlInvokeAccess.newMemberName();
+ }
+
+ @Override
+ public String getClassName() {
+ return declaringClass.getName();
+ }
+
+ @Override
+ public Class<?> getDeclaringClass() {
+ walker.ensureAccessEnabled(RETAIN_CLASS_REFERENCE);
+ return declaringClass;
+ }
+
+ // Call the VM to set methodName, lineNumber, and fileName
+ private synchronized void ensureMethodInfoInitialized() {
+ if (methodName == null) {
+ setMethodInfo();
+ }
+ }
+
+ @Override
+ public String getMethodName() {
+ ensureMethodInfoInitialized();
+ return methodName;
+ }
+
+ @Override
+ public Optional<String> getFileName() {
+ ensureMethodInfoInitialized();
+ return fileName != null ? Optional.of(fileName) : Optional.empty();
+ }
+
+ @Override
+ public OptionalInt getLineNumber() {
+ ensureMethodInfoInitialized();
+ return lineNumber > 0 ? OptionalInt.of(lineNumber) : OptionalInt.empty();
+ }
+
+ @Override
+ public boolean isNativeMethod() {
+ ensureMethodInfoInitialized();
+ return lineNumber == -2;
+ }
+
+ @Override
+ public String toString() {
+ ensureMethodInfoInitialized();
+ // similar format as StackTraceElement::toString
+ if (isNativeMethod()) {
+ return getClassName() + "." + getMethodName() + "(Native Method)";
+ } else {
+ // avoid allocating Optional objects
+ return getClassName() + "." + getMethodName() +
+ "(" + (fileName != null ? fileName : "Unknown Source") +
+ (lineNumber > 0 ? ":" + lineNumber : " bci:" + bci) + ")";
+ }
+ }
+
+ /**
+ * Lazily initialize method name, file name, line number
+ */
+ private native void setMethodInfo();
+
+ /**
+ * Fill in source file name and line number of the given StackFrame array.
+ */
+ static native void fillInStackFrames(int startIndex,
+ Object[] stackframes,
+ int fromIndex, int toIndex);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StackFramePermission.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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;
+
+/**
+ * Permission to access {@link StackWalker.StackFrame}.
+ *
+ * @see java.lang.StackWalker.Option#RETAIN_CLASS_REFERENCE
+ * @see StackWalker.StackFrame#getDeclaringClass()
+ */
+public class StackFramePermission extends java.security.BasicPermission {
+ private static final long serialVersionUID = 2841894854386706014L;
+
+ /**
+ * Creates a new {@code StackFramePermission} object.
+ *
+ * @param name Permission name. Must be "retainClassReference".
+ *
+ * @throws IllegalArgumentException if {@code name} is invalid.
+ * @throws NullPointerException if {@code name} is {@code null}.
+ */
+ public StackFramePermission(String name) {
+ super(name);
+ if (!name.equals("retainClassReference")) {
+ throw new IllegalArgumentException("name: " + name);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.misc.VM;
+
+import java.io.PrintStream;
+import java.lang.StackWalker.Option;
+import java.lang.StackWalker.StackFrame;
+
+import java.lang.annotation.Native;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Optional;
+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 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. Throwable::init and Throwable::getStackTrace
+ * 4. 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 FILTER_FILL_IN_STACKTRACE = 0x10;
+ @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 useNewThrowable = getProperty("stackwalk.newThrowable", false);
+ final static boolean isDebug = getProperty("stackwalk.debug", false);
+
+ static <T> StackFrameTraverser<T>
+ makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
+ {
+ if (walker.hasLocalsOperandsOption())
+ return new LiveStackInfoTraverser<T>(walker, function);
+ else
+ return new StackFrameTraverser<T>(walker, function);
+ }
+
+ /**
+ * Gets a stack stream to find caller class.
+ */
+ static CallerClassFinder makeCallerFinder(StackWalker walker) {
+ return new CallerClassFinder(walker);
+ }
+
+ static boolean useStackTrace(Throwable t) {
+ if (t instanceof VirtualMachineError)
+ return false;
+
+ return VM.isBooted() && StackStreamFactory.useNewThrowable;
+ }
+
+ /*
+ * This should only be used by Throwable::<init>.
+ */
+ static StackTrace makeStackTrace(Throwable ex) {
+ return StackTrace.dump(ex);
+ }
+
+ /*
+ * This creates StackTrace for Thread::dumpThread to use.
+ */
+ static StackTrace makeStackTrace() {
+ return StackTrace.dump();
+ }
+
+ enum WalkerState {
+ NEW, // the stream is new and stack walking has not started
+ OPEN, // the stream is open when it is being traversed.
+ CLOSED; // the stream is closed when the stack walking is done
+ }
+
+ static abstract class AbstractStackWalker<T> {
+ protected final StackWalker walker;
+ protected final Thread thread;
+ protected final int maxDepth;
+ protected final long mode;
+ protected int depth; // traversed stack depth
+ protected FrameBuffer frameBuffer; // buffer for VM to fill in
+ protected long anchor;
+
+ // buffers to fill in stack frame information
+ protected 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) &&
+ !fillCallerClassOnly(newMode) /* don't show hidden frames for getCallerClass */)
+ newMode |= SHOW_HIDDEN_FRAMES;
+ if (walker.hasLocalsOperandsOption())
+ newMode |= FILL_LIVE_STACK_FRAMES;
+ return newMode;
+ }
+
+ private boolean fillCallerClassOnly(int mode) {
+ return (mode|FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY;
+ }
+ /**
+ * 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 T 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 (this.anchor != 0) {
+ throw new IllegalStateException("This stack stream is being reused.");
+ }
+ break;
+ case OPEN:
+ if (this.anchor <= 0) {
+ throw new IllegalStateException("This stack stream is not valid for walking");
+ }
+ break;
+ case CLOSED:
+ if (this.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 T 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 frames
+ getNextBatch();
+ } 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 traversal
+ frameBuffer.setBatch(bufStartIndex, bufEndIndex);
+
+ // traverse all frames and perform the action on the stack frames, if specified
+ return 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 done
+ return 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 T beginStackWalk() {
+ // initialize buffers for VM to fill the stack frame info
+ initFrameBuffer();
+
+ return callStackWalk(mode, 0,
+ frameBuffer.curBatchFrameCount(),
+ frameBuffer.startIndex(),
+ frameBuffer.classes,
+ frameBuffer.stackFrames);
+ }
+
+ /*
+ * 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.classes,
+ frameBuffer.stackFrames);
+ 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(startIndex, endIndex);
+ }
+ return numFrames;
+ }
+
+ /**
+ * Begins stack walking. This method anchors this frame and invokes
+ * AbstractStackWalker::doStackWalk after fetching the firt 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 classes Classes buffer of the stack frames
+ * @param frames StackFrame buffer, or null
+ * @return Result of AbstractStackWalker::doStackWalk
+ */
+ private native T callStackWalk(long mode, int skipframes,
+ int batchSize, int startIndex,
+ Class<?>[] classes,
+ StackFrame[] 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 classes Classes buffer of the stack frames
+ * @param frames StackFrame buffer, or null
+ *
+ * @return the end index to the frame buffers
+ */
+ private native int fetchStackFrames(long mode, long anchor,
+ int batchSize, int startIndex,
+ Class<?>[] classes,
+ StackFrame[] frames);
+
+
+ /*
+ * Frame buffer
+ *
+ * Each specialized AbstractStackWalker subclass may subclass the FrameBuffer.
+ */
+ class FrameBuffer {
+ static final int START_POS = 2; // 0th and 1st elements are reserved
+
+ // buffers for VM to fill stack frame info
+ int currentBatchSize; // current batch size
+ Class<?>[] classes; // caller class for fast path
+
+ StackFrame[] stackFrames;
+
+ int origin; // index to the current traversed stack frame
+ int fence; // index to the last frame in the current batch
+
+ FrameBuffer(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;
+ this.classes = new Class<?>[currentBatchSize];
+ }
+
+ 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 = classes[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 classes[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 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 implementation
+ for (int i = START_POS; i < fence; i++) {
+ Class<?> c = classes[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 depth
+ throw new IllegalStateException("origin " + origin + " != " + 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.
+ *
+ */
+ void resize(int startIndex, int elements) {
+ if (!isActive())
+ throw new IllegalStateException("inactive frame buffer can't be resized");
+
+ 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, START_POS);
+ }
+ currentBatchSize = size;
+ }
+
+ /*
+ * 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
+ */
+ StackFrame nextStackFrame() {
+ throw new InternalError("should not reach here");
+ }
+ }
+ }
+
+ /*
+ * This StackFrameTraverser supports {@link Stream} traversal.
+ *
+ * This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
+ */
+ static class StackFrameTraverser<T> extends AbstractStackWalker<T>
+ implements Spliterator<StackFrame>
+ {
+ static {
+ stackWalkImplClasses.add(StackFrameTraverser.class);
+ }
+ private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
+ class Buffer extends FrameBuffer {
+ Buffer(int initialBatchSize) {
+ super(initialBatchSize);
+
+ this.stackFrames = new StackFrame[initialBatchSize];
+ for (int i = START_POS; i < initialBatchSize; i++) {
+ stackFrames[i] = new StackFrameInfo(walker);
+ }
+ }
+
+ @Override
+ void resize(int startIndex, int elements) {
+ super.resize(startIndex, elements);
+
+ int size = startIndex+elements;
+ if (stackFrames.length < size) {
+ stackFrames = new StackFrame[size];
+ }
+ for (int i = startIndex(); i < size; i++) {
+ stackFrames[i] = new StackFrameInfo(walker);
+ }
+ }
+
+ @Override
+ StackFrame nextStackFrame() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
+ }
+
+ StackFrame frame = stackFrames[origin];
+ origin++;
+ return frame;
+ }
+ }
+
+ 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);
+ 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;
+ }
+
+ StackFrame frame = frameBuffer.nextStackFrame();
+ depth++;
+ return frame;
+ }
+
+ @Override
+ protected T consumeFrames() {
+ checkState(OPEN);
+ Stream<StackFrame> stream = StreamSupport.stream(this, false);
+ if (function != null) {
+ return function.apply(stream);
+ } else
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void initFrameBuffer() {
+ this.frameBuffer = new Buffer(getNextBatchSize());
+ }
+
+ @Override
+ protected int batchSize(int lastBatchFrameCount) {
+ if (lastBatchFrameCount == 0) {
+ // First batch, use estimateDepth if not exceed the large batch size
+ // and not too small
+ int 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
+
+ @Override
+ public Spliterator<StackFrame> trySplit() {
+ return null; // ordered stream and do not allow to split
+ }
+
+ @Override
+ public long estimateSize() {
+ return maxDepth;
+ }
+
+ @Override
+ public int characteristics() {
+ return CHARACTERISTICS;
+ }
+
+ @Override
+ public 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);
+ }
+ }
+
+ @Override
+ public 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 class CallerClassFinder extends AbstractStackWalker<Integer> {
+ static {
+ stackWalkImplClasses.add(CallerClassFinder.class);
+ }
+
+ private Class<?> caller;
+
+ CallerClassFinder(StackWalker walker) {
+ super(walker, FILL_CLASS_REFS_ONLY);
+ }
+
+ Class<?> findCaller() {
+ walk();
+ return caller;
+ }
+
+ @Override
+ 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;
+ frames[n++] = caller;
+ }
+
+ if (frames[1] == null)
+ throw new IllegalStateException("no caller frame");
+ return n;
+ }
+
+ @Override
+ protected void initFrameBuffer() {
+ this.frameBuffer = new FrameBuffer(getNextBatchSize());
+ }
+
+ @Override
+ protected int batchSize(int lastBatchFrameCount) {
+ return MIN_BATCH_SIZE;
+ }
+
+ @Override
+ protected int getNextBatchSize() {
+ return MIN_BATCH_SIZE;
+ }
+ }
+
+ /*
+ * StackTrace caches all frames in the buffer. StackTraceElements are
+ * created lazily when Throwable::getStackTrace is called.
+ */
+ static class StackTrace extends AbstractStackWalker<Integer> {
+ static {
+ stackWalkImplClasses.add(StackTrace.class);
+ }
+
+ class GrowableBuffer extends FrameBuffer {
+ GrowableBuffer(int initialBatchSize) {
+ super(initialBatchSize);
+
+ this.stackFrames = new StackFrame[initialBatchSize];
+ for (int i = START_POS; i < initialBatchSize; i++) {
+ stackFrames[i] = new StackFrameInfo(walker);
+ }
+ }
+
+ /*
+ * Returns the next index to fill
+ */
+ @Override
+ int startIndex() {
+ return origin;
+ }
+
+ /**
+ * Initialize the buffers for VM to fill in the stack frame information.
+ * The next batch will start at the given startIndex to
+ * the length of the buffer.
+ */
+ @Override
+ void resize(int startIndex, int elements) {
+ // Expand the frame buffer.
+ // Do not call super.resize that will reuse the filled elements
+ // in this frame buffer
+ int size = startIndex+elements;
+ if (classes.length < size) {
+ // resize the frame buffer
+ classes = Arrays.copyOf(classes, size);
+ stackFrames = Arrays.copyOf(stackFrames, size);
+ }
+ for (int i = startIndex; i < size; i++) {
+ stackFrames[i] = new StackFrameInfo(walker);
+ }
+ currentBatchSize = size;
+ }
+
+ StackTraceElement get(int index) {
+ return new StackTraceElement(classes[index].getName(), "unknown", null, -1);
+ }
+
+ /**
+ * Returns an array of StackTraceElement for all stack frames cached in
+ * this StackTrace object.
+ * <p>
+ * This method is intended for Throwable::getOurStackTrace use only.
+ */
+ StackTraceElement[] toStackTraceElements() {
+ int startIndex = START_POS;
+ for (int i = startIndex; i < classes.length; i++) {
+ if (classes[i] != null && filterStackWalkImpl(classes[i])) {
+ startIndex++;
+ } else {
+ break;
+ }
+ }
+
+ // VM fills in the method name, filename, line number info
+ StackFrameInfo.fillInStackFrames(0, stackFrames, startIndex, startIndex + depth);
+
+ StackTraceElement[] stes = new StackTraceElement[depth];
+ for (int i = startIndex, j = 0; i < classes.length && j < depth; i++, j++) {
+ if (isDebug) {
+ System.err.println("StackFrame: " + i + " " + stackFrames[i]);
+ }
+ stes[j] = stackFrames[i].toStackTraceElement();
+ }
+ return stes;
+ }
+ }
+
+ private static final int MAX_STACK_FRAMES = 1024;
+ private static final StackWalker STACKTRACE_WALKER =
+ StackWalker.newInstanceNoCheck(EnumSet.of(Option.SHOW_REFLECT_FRAMES));
+
+ private StackTraceElement[] stes;
+ static StackTrace dump() {
+ return new StackTrace();
+ }
+
+ static StackTrace dump(Throwable ex) {
+ return new StackTrace(ex);
+ }
+
+ private StackTrace() {
+ this(STACKTRACE_WALKER, DEFAULT_MODE);
+ }
+
+ /*
+ * Throwable::fillInStackTrace and <init> of Throwable and subclasses
+ * are filtered in the VM.
+ */
+ private StackTrace(Throwable ex) {
+ this(STACKTRACE_WALKER, FILTER_FILL_IN_STACKTRACE); // skip Throwable::init frames
+ if (isDebug) {
+ System.err.println("dump stack for " + ex.getClass().getName());
+ }
+ }
+
+ StackTrace(StackWalker walker, int mode) {
+ super(walker, mode, MAX_STACK_FRAMES);
+
+ // snapshot the stack trace
+ walk();
+ }
+
+ @Override
+ protected Integer consumeFrames() {
+ // traverse all frames and perform the action on the stack frames, if specified
+ int n = 0;
+ while (n < maxDepth && nextFrame() != null) {
+ n++;
+ }
+ return n;
+ }
+
+ @Override
+ protected void initFrameBuffer() {
+ this.frameBuffer = new GrowableBuffer(getNextBatchSize());
+ }
+
+ // TODO: implement better heuristic
+ @Override
+ protected int batchSize(int lastBatchFrameCount) {
+ // chunk size of VM backtrace is 32
+ return lastBatchFrameCount == 0 ? 32 : 32;
+ }
+
+ /**
+ * Returns an array of StackTraceElement for all stack frames cached in
+ * this StackTrace object.
+ * <p>
+ * This method is intended for Throwable::getOurStackTrace use only.
+ */
+ synchronized StackTraceElement[] getStackTraceElements() {
+ if (stes == null) {
+ stes = ((GrowableBuffer) frameBuffer).toStackTraceElements();
+ // release the frameBuffer memory
+ frameBuffer = null;
+ }
+ return stes;
+ }
+
+ /*
+ * Prints stack trace to the given PrintStream.
+ *
+ * Further implementation could skip creating StackTraceElement objects
+ * print directly to the PrintStream.
+ */
+ void printStackTrace(PrintStream s) {
+ StackTraceElement[] stes = getStackTraceElements();
+ synchronized (s) {
+ s.println("Stack trace");
+ for (StackTraceElement traceElement : stes)
+ s.println("\tat " + traceElement);
+ }
+ }
+ }
+
+ static class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {
+ static {
+ stackWalkImplClasses.add(LiveStackInfoTraverser.class);
+ }
+ // VM will fill in all method info and live stack info directly in StackFrameInfo
+ class Buffer extends FrameBuffer {
+ Buffer(int initialBatchSize) {
+ super(initialBatchSize);
+ this.stackFrames = new StackFrame[initialBatchSize];
+ for (int i = START_POS; i < initialBatchSize; i++) {
+ stackFrames[i] = new LiveStackFrameInfo(walker);
+ }
+ }
+
+ @Override
+ void resize(int startIndex, int elements) {
+ super.resize(startIndex, elements);
+ int size = startIndex + elements;
+
+ if (stackFrames.length < size) {
+ this.stackFrames = new StackFrame[size];
+ }
+
+ for (int i = startIndex(); i < size; i++) {
+ stackFrames[i] = new LiveStackFrameInfo(walker);
+ }
+ }
+
+ @Override
+ StackFrame nextStackFrame() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
+ }
+
+ StackFrame frame = stackFrames[origin];
+ origin++;
+ return frame;
+ }
+ }
+
+ LiveStackInfoTraverser(StackWalker walker,
+ Function<? super Stream<StackFrame>, ? extends T> function) {
+ super(walker, function, DEFAULT_MODE);
+ }
+
+ @Override
+ protected void initFrameBuffer() {
+ this.frameBuffer = new Buffer(getNextBatchSize());
+ }
+ }
+
+ private static native boolean checkStackWalkModes();
+
+ // avoid loading other subclasses as they may not be used
+ private 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 out
+ private static boolean isMethodHandleFrame(Class<?> c) {
+ return c.getName().startsWith("java.lang.invoke.");
+ }
+
+ private static boolean isReflectionFrame(Class<?> c) {
+ if (c.getName().startsWith("sun.reflect") &&
+ !sun.reflect.MethodAccessor.class.isAssignableFrom(c)) {
+ throw new InternalError("Not sun.reflect.MethodAccessor: " + c.toString());
+ }
+ // ## should filter all @Hidden frames?
+ return c == Method.class ||
+ sun.reflect.MethodAccessor.class.isAssignableFrom(c) ||
+ c.getName().startsWith("java.lang.invoke.LambdaForm");
+ }
+
+ private static boolean getProperty(String key, boolean value) {
+ String s = AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public String run() {
+ return System.getProperty(key);
+ }
+ });
+ if (s != null) {
+ return Boolean.valueOf(s);
+ }
+ return value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/StackWalker.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 sun.reflect.CallerSensitive;
+
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/**
+ * A stack walker.
+ *
+ * <p> The {@link StackWalker#walk walk} method opens a sequential stream
+ * of {@link StackFrame StackFrame}s for the current thread and then applies
+ * the given function to walk the {@code StackFrame} stream.
+ * The stream reports stack frame elements in order, from the top most frame
+ * that represents the execution point at which the stack was generated to
+ * the bottom most frame.
+ * The {@code StackFrame} stream is closed when the {@code walk} method returns.
+ * 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> {@code StackWalker} is thread-safe. Multiple threads can share
+ * a single {@code StackWalker} object to traverse its own stack.
+ * A permission check is performed when a {@code StackWalker} is created,
+ * according to the options it requests.
+ * No further permission check is done at stack walking time.
+ *
+ * @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);
+ * Optional<Class<?>> callerClass = walker.walk(s ->
+ * s.map(StackFrame::getDeclaringClass)
+ * .filter(interestingClasses::contains)
+ * .findFirst());
+ * }</pre>
+ *
+ * <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>
+ *
+ * Unless otherwise noted, passing a {@code null} argument to a
+ * constructor or method in this {@code StackWalker} class
+ * will cause a {@link NullPointerException NullPointerException}
+ * to be thrown.
+ *
+ * @since 1.9
+ */
+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}.
+ *
+ * @since 1.9
+ * @jvms 2.6
+ */
+ public static interface StackFrame {
+ /**
+ * Gets the <a href="ClassLoader.html#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
+ *
+ * @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
+ */
+ public String getMethodName();
+
+ /**
+ * Gets 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}.
+ */
+ public Class<?> getDeclaringClass();
+
+ /**
+ * Returns the name of the source file containing the execution point
+ * represented by this stack frame. Generally, this corresponds
+ * to the {@code SourceFile} attribute of the relevant {@code class}
+ * file as defined by <cite>The Java Virtual Machine Specification</cite>.
+ * In some systems, the name may refer to some source code unit
+ * other than a file, such as an entry in a source repository.
+ *
+ * @return the name of the file containing the execution point
+ * represented by this stack frame, or empty {@code Optional}
+ * is unavailable.
+ *
+ * @jvms 4.7.10 The {@code SourceFile} Attribute
+ */
+ public Optional<String> getFileName();
+
+ /**
+ * Returns the line number of the source line containing the execution
+ * point represented by this stack frame. Generally, this is
+ * derived from the {@code LineNumberTable} attribute of the relevant
+ * {@code class} file as defined by <cite>The Java Virtual Machine
+ * Specification</cite>.
+ *
+ * @return the line number of the source line containing the execution
+ * point represented by this stack frame, or empty
+ * {@code Optional} if this information is unavailable.
+ *
+ * @jvms 4.7.12 The {@code LineNumberTable} Attribute
+ */
+ public OptionalInt 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.
+ */
+ public boolean isNativeMethod();
+
+ /**
+ * Gets a {@code StackTraceElement} for this stack frame.
+ *
+ * @return {@code StackTraceElement} for this stack frame.
+ *
+ * */
+ public default StackTraceElement toStackTraceElement() {
+ int lineNumber = isNativeMethod() ? -2
+ : getLineNumber().orElse(-1);
+ return new StackTraceElement(getClassName(), getMethodName(),
+ getFileName().orElse(null),
+ lineNumber);
+ }
+ }
+
+ /**
+ * Stack walker option to configure the {@linkplain StackFrame stack frame}
+ * information obtained by a {@code StackWalker}.
+ *
+ * @since 1.9
+ */
+ public enum Option {
+ /**
+ * Retains {@code Class} object in {@code StackFrame}s
+ * walked by this {@code StackWalker}.
+ *
+ * <p> A {@code StackWalker} configured with this option will support
+ * {@link StackWalker#getCallerClass()} and
+ * {@link StackFrame#getDeclaringClass() StackFrame.getDeclaringClass()}.
+ */
+ RETAIN_CLASS_REFERENCE,
+ /**
+ * Shows all reflection frames.
+ *
+ * <p>By default, reflection frames are hidden. This includes the
+ * {@link java.lang.reflect.Method#invoke} method
+ * and the reflection implementation classes. A {@code StackWalker} with
+ * this {@code SHOW_REFLECT_FRAMES} option will show all reflection frames.
+ * The {@link #SHOW_HIDDEN_FRAMES} option can also be used to show all
+ * reflection frames and it will also show other hidden frames that
+ * are implementation-specific.
+ */
+ SHOW_REFLECT_FRAMES,
+ /**
+ * Shows all hidden frames.
+ *
+ * <p>A Java Virtual Machine implementation may hide implementation
+ * specific frames in addition to {@linkplain #SHOW_REFLECT_FRAMES
+ * reflection frames}. A {@code StackWalker} with this {@code SHOW_HIDDEN_FRAMES}
+ * option will show all hidden frames (including reflection frames).
+ */
+ SHOW_HIDDEN_FRAMES;
+ }
+
+ enum ExtendedOption {
+ /**
+ * Obtain monitors, locals and operands.
+ */
+ LOCALS_AND_OPERANDS
+ };
+
+ static final EnumSet<Option> DEFAULT_EMPTY_OPTION = EnumSet.noneOf(Option.class);
+
+ private final static StackWalker DEFAULT_WALKER =
+ new StackWalker(DEFAULT_EMPTY_OPTION);
+
+ private final Set<Option> options;
+ private final ExtendedOption extendedOption;
+ private final int estimateDepth;
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ */
+ public static StackWalker getInstance() {
+ // no permission check needed
+ return DEFAULT_WALKER;
+ }
+
+ /**
+ * 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},
+ * it calls its {@link SecurityManager#checkPermission checkPermission}
+ * method for {@code StackFramePermission("retainClassReference")}.
+ *
+ * @param option {@link Option stack walking option}
+ *
+ * @return a {@code StackWalker} configured with the given option
+ *
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkPermission} method denies access.
+ */
+ public static StackWalker getInstance(Option option) {
+ return getInstance(EnumSet.of(Objects.requireNonNull(option)));
+ }
+
+ /**
+ * 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 StackFramePermission("retainClassReference")}.
+ *
+ * @param options {@link Option stack walking option}
+ *
+ * @return a {@code StackWalker} configured with the given options
+ *
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkPermission} method denies access.
+ */
+ public static StackWalker getInstance(Set<Option> options) {
+ if (options.isEmpty()) {
+ return DEFAULT_WALKER;
+ }
+
+ checkPermission(options);
+ return new StackWalker(toEnumSet(options));
+ }
+
+ /**
+ * Returns a {@code StackWalker} instance with the given {@ocde options} specifying
+ * the stack frame information it can access. If the given {@ocde 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 StackFramePermission("retainClassReference")}.
+ *
+ * <p>
+ * The {@code estimateDepth} specifies the estimate number of stack frames
+ * this {@code StackWalker} will traverse that the {@code StackWalker} could
+ * use as a hint for the buffer size.
+ *
+ * @param options {@link Option stack walking options}
+ * @param estimateDepth Estimate number of stack frames to be traversed.
+ *
+ * @return a {@code StackWalker} configured with the given options
+ *
+ * @throws IllegalArgumentException if {@code estimateDepth <= 0}
+ * @throws SecurityException if a security manager exists and its
+ * {@code checkPermission} method denies access.
+ */
+ public static StackWalker getInstance(Set<Option> options, int estimateDepth) {
+ if (estimateDepth <= 0) {
+ throw new IllegalArgumentException("estimateDepth must be > 0");
+ }
+ checkPermission(options);
+ return new StackWalker(toEnumSet(options), estimateDepth);
+ }
+
+ // ----- private constructors ------
+ private StackWalker(EnumSet<Option> options) {
+ this(options, 0, null);
+ }
+ private StackWalker(EnumSet<Option> options, int estimateDepth) {
+ this(options, estimateDepth, null);
+ }
+ private StackWalker(EnumSet<Option> options, int estimateDepth, ExtendedOption extendedOption) {
+ this.options = options;
+ this.estimateDepth = estimateDepth;
+ this.extendedOption = extendedOption;
+ }
+
+ private static void checkPermission(Set<Option> options) {
+ Objects.requireNonNull(options);
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ if (options.contains(Option.RETAIN_CLASS_REFERENCE)) {
+ sm.checkPermission(new StackFramePermission("retainClassReference"));
+ }
+ }
+ }
+
+ /*
+ * Returns a defensive copy
+ */
+ private static EnumSet<Option> toEnumSet(Set<Option> options) {
+ Objects.requireNonNull(options);
+ if (options.isEmpty()) {
+ return DEFAULT_EMPTY_OPTION;
+ } else {
+ return EnumSet.copyOf(options);
+ }
+ }
+
+ /**
+ * Applies the given function to the stream of {@code StackFrame}s
+ * for the current thread, traversing from the top frame of the stack,
+ * which is the method calling this {@code walk} method.
+ *
+ * <p>The {@code StackFrame} stream will be closed when
+ * this method returns. When a closed {@code Stream<StackFrame>} object
+ * is reused, {@code IllegalStateException} will be thrown.
+ *
+ * @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
+ * List<StackFrame> frames = StackWalker.getInstance().walk(s ->
+ * s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
+ * .limit(10)
+ * .collect(Collectors.toList()));
+ * }</pre></blockquote>
+ *
+ * <p>This method takes a {@code Function} accepting a {@code Stream<StackFrame>},
+ * rather than returning a {@code Stream<StackFrame>} and allowing the
+ * caller to directly manipulate the stream. The Java virtual machine is
+ * free to reorganize a thread's control stack, for example, via
+ * deoptimization. By taking a {@code Function} parameter, this method
+ * allows access to stack frames through a stable view of a thread's control
+ * stack.
+ *
+ * <p>Parallel execution is effectively disabled and stream pipeline
+ * execution will only occur on the current thread.
+ *
+ * @implNote The implementation stabilizes the stack by anchoring a frame
+ * specific to the stack walking and ensures that the stack walking is
+ * performed above the anchored frame. When the stream object is closed or
+ * being reused, {@code IllegalStateException} will be thrown.
+ *
+ * @param function a function that takes a stream of
+ * {@linkplain StackFrame stack frames} and returns a result.
+ * @param <T> The type of the result of applying the function to the
+ * stream of {@linkplain StackFrame stack frame}.
+ *
+ * @return the result of applying the function to the stream of
+ * {@linkplain StackFrame stack frame}.
+ */
+ @CallerSensitive
+ public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function) {
+ // Returning a Stream<StackFrame> would be unsafe, as the stream could
+ // be used to access the stack frames in an uncontrolled manner. For
+ // example, a caller might pass a Spliterator of stack frames after one
+ // or more frames had been traversed. There is no robust way to detect
+ // whether the execution point when
+ // Spliterator.tryAdvance(java.util.function.Consumer<? super T>) is
+ // invoked is the exact same execution point where the stack frame
+ // traversal is expected to resume.
+
+ Objects.requireNonNull(function);
+ return StackStreamFactory.makeStackTraverser(this, function)
+ .walk();
+ }
+
+ /**
+ * Performs the given action on each element of {@code StackFrame} stream
+ * of the current thread, traversing from the top frame of the stack,
+ * which is the method calling this {@code forEach} method.
+ *
+ * <p> This method is equivalent to calling
+ * <blockquote>
+ * {@code walk(s -> { s.forEach(action); return null; });}
+ * </blockquote>
+ *
+ * @param action an action to be performed on each {@code StackFrame}
+ * of the stack of the current thread
+ */
+ @CallerSensitive
+ public void forEach(Consumer<? super StackFrame> action) {
+ Objects.requireNonNull(action);
+ StackStreamFactory.makeStackTraverser(this, s -> {
+ s.forEach(action);
+ return null;
+ }).walk();
+ }
+
+ /**
+ * Gets the {@code Class} object of the caller invoking the method
+ * that calls this {@code getCallerClass} method.
+ *
+ * <p> Reflection frames, {@link java.lang.invoke.MethodHandle} and
+ * hidden frames are filtered regardless of the
+ * {@link Option#SHOW_REFLECT_FRAMES SHOW_REFLECT_FRAMES}
+ * and {@link Option#SHOW_HIDDEN_FRAMES SHOW_HIDDEN_FRAMES} options
+ * this {@code StackWalker} has been configured.
+ *
+ * <p> This method throws {@code UnsupportedOperationException}
+ * if this {@code StackWalker} is not configured with
+ * {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option,
+ * This method should be called when a caller frame is present. If
+ * it is called from the last frame on the stack;
+ * {@code IllegalStateException} will be thrown.
+ *
+ * @apiNote
+ * For example, {@code Util::getResourceBundle} loads a resource bundle
+ * on behalf of the caller. It calls this {@code getCallerClass} method
+ * to find the method calling {@code Util::getResourceBundle} and use the caller's
+ * class loader to load the resource bundle. The caller class in this example
+ * is the {@code MyTool} class.
+ *
+ * <pre>{@code
+ * class Util {
+ * private final StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
+ * public ResourceBundle getResourceBundle(String bundleName) {
+ * Class<?> caller = walker.getCallerClass();
+ * return ResourceBundle.getBundle(bundleName, Locale.getDefault(), caller.getClassLoader());
+ * }
+ * }
+ *
+ * class MyTool {
+ * private final Util util = new Util();
+ * private void init() {
+ * 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
+ * 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 last frame on the stack,
+ * for example, {@code static public void main} method launched by the
+ * {@code java} launcher or a method invoked from a JNI attached thread.
+ * {@code IllegalStateException} is thrown.
+ *
+ * @return {@code Class} object of the caller's caller invoking this method.
+ *
+ * @throws UnsupportedOperationException if this {@code StackWalker}
+ * is not configured with {@link Option#RETAIN_CLASS_REFERENCE
+ * Option.RETAIN_CLASS_REFERENCE}.
+ * @throws IllegalStateException if there is no caller frame, i.e.
+ * when this {@code getCallerClass} method is called from a method
+ * which is the last frame on the stack.
+ */
+ @CallerSensitive
+ public Class<?> getCallerClass() {
+ if (!options.contains(Option.RETAIN_CLASS_REFERENCE)) {
+ throw new UnsupportedOperationException("This stack walker " +
+ "does not have RETAIN_CLASS_REFERENCE access");
+ }
+
+ return StackStreamFactory.makeCallerFinder(this).findCaller();
+ }
+
+ // ---- package access ----
+ static StackWalker newInstanceNoCheck(EnumSet<Option> options) {
+ return new StackWalker(options, 0, null);
+ }
+
+ static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption) {
+ checkPermission(options);
+ return new StackWalker(toEnumSet(options), 0, extendedOption);
+ }
+
+ int estimateDepth() {
+ return estimateDepth;
+ }
+
+ boolean hasOption(Option option) {
+ return options.contains(option);
+ }
+
+ boolean hasLocalsOperandsOption() {
+ return extendedOption == ExtendedOption.LOCALS_AND_OPERANDS;
+ }
+
+ void ensureAccessEnabled(Option access) {
+ if (!hasOption(access)) {
+ throw new UnsupportedOperationException("No access to " + access +
+ ": " + options.toString());
+ }
+ }
+}
--- a/jdk/src/java.base/share/classes/java/lang/System.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/System.java Tue Nov 24 15:05:58 2015 -0800
@@ -1896,12 +1896,6 @@
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
- public int getStackTraceDepth(Throwable t) {
- return t.getStackTraceDepth();
- }
- public StackTraceElement getStackTraceElement(Throwable t, int i) {
- return t.getStackTraceElement(i);
- }
public String newStringUnsafe(char[] chars) {
return new String(chars, true);
}
--- a/jdk/src/java.base/share/classes/java/lang/Thread.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Thread.java Tue Nov 24 15:05:58 2015 -0800
@@ -1329,11 +1329,9 @@
/**
* Prints a stack trace of the current thread to the standard error stream.
* This method is used only for debugging.
- *
- * @see Throwable#printStackTrace()
*/
public static void dumpStack() {
- new Exception("Stack trace").printStackTrace();
+ StackStreamFactory.makeStackTrace().printStackTrace(System.err);
}
/**
@@ -1556,7 +1554,7 @@
return stackTrace;
} else {
// Don't need JVM help for current thread
- return (new Exception()).getStackTrace();
+ return StackStreamFactory.makeStackTrace().getStackTraceElements();
}
}
--- a/jdk/src/java.base/share/classes/java/lang/Throwable.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Throwable.java Tue Nov 24 15:05:58 2015 -0800
@@ -24,6 +24,8 @@
*/
package java.lang;
+import sun.misc.VM;
+
import java.io.*;
import java.util.*;
@@ -778,7 +780,11 @@
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
- fillInStackTrace(0);
+ if (backtrace == null && StackStreamFactory.useStackTrace(this)) {
+ backtrace = StackStreamFactory.makeStackTrace(this);
+ } else {
+ fillInStackTrace(0);
+ }
stackTrace = UNASSIGNED_STACK;
}
return this;
@@ -819,10 +825,14 @@
// backtrace if this is the first call to this method
if (stackTrace == UNASSIGNED_STACK ||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
- int depth = getStackTraceDepth();
- stackTrace = new StackTraceElement[depth];
- for (int i=0; i < depth; i++)
- stackTrace[i] = getStackTraceElement(i);
+ if (backtrace instanceof StackStreamFactory.StackTrace) {
+ stackTrace = ((StackStreamFactory.StackTrace)backtrace).getStackTraceElements();
+ } else {
+ int depth = getStackTraceDepth();
+ stackTrace = new StackTraceElement[depth];
+ for (int i = 0; i < depth; i++)
+ stackTrace[i] = getStackTraceElement(i);
+ }
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Tue Nov 24 15:05:58 2015 -0800
@@ -1077,4 +1077,13 @@
// System.out.println("Hello world! My methods are:");
// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
// }
+
+ static {
+ // Allow privileged classes outside of java.lang
+ jdk.internal.misc.SharedSecrets.setJavaLangInvokeAccess(new jdk.internal.misc.JavaLangInvokeAccess() {
+ public Object newMemberName() {
+ return new MemberName();
+ }
+ });
+ }
}
--- a/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java Tue Nov 24 15:05:58 2015 -0800
@@ -31,13 +31,12 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.ZonedDateTime;
+import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
+import java.util.function.Predicate;
import java.util.function.Supplier;
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
import sun.util.logging.PlatformLogger;
import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
@@ -169,42 +168,55 @@
// Returns the caller's class and method's name; best effort
// if cannot infer, return the logger's name.
private String getCallerInfo() {
- String sourceClassName = null;
- String sourceMethodName = null;
+ Optional<StackWalker.StackFrame> frame = new CallerFinder().get();
+ if (frame.isPresent()) {
+ return frame.get().getClassName() + " " + frame.get().getMethodName();
+ } else {
+ return name;
+ }
+ }
- JavaLangAccess access = SharedSecrets.getJavaLangAccess();
- Throwable throwable = new Throwable();
- int depth = access.getStackTraceDepth(throwable);
+ /*
+ * CallerFinder is a stateful predicate.
+ */
+ static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
+ static final StackWalker WALKER = StackWalker.getInstance();
- String logClassName = "sun.util.logging.PlatformLogger";
- String simpleLoggerClassName = "jdk.internal.logger.SimpleConsoleLogger";
- boolean lookingForLogger = true;
- for (int ix = 0; ix < depth; ix++) {
- // Calling getStackTraceElement directly prevents the VM
- // from paying the cost of building the entire stack frame.
- final StackTraceElement frame =
- access.getStackTraceElement(throwable, ix);
- final String cname = frame.getClassName();
+ /**
+ * Returns StackFrame of the caller's frame.
+ * @return StackFrame of the caller's frame.
+ */
+ Optional<StackWalker.StackFrame> get() {
+ return WALKER.walk((s) -> s.filter(this).findFirst());
+ }
+
+ private boolean lookingForLogger = true;
+ /**
+ * Returns true if we have found the caller's frame, false if the frame
+ * must be skipped.
+ *
+ * @param t The frame info.
+ * @return true if we have found the caller's frame, false if the frame
+ * must be skipped.
+ */
+ @Override
+ public boolean test(StackWalker.StackFrame t) {
+ final String cname = t.getClassName();
+ // We should skip all frames until we have found the logger,
+ // because these frames could be frames introduced by e.g. custom
+ // sub classes of Handler.
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
- if (cname.equals(logClassName) || cname.equals(simpleLoggerClassName)) {
- lookingForLogger = false;
- }
- } else {
- if (skipLoggingFrame(cname)) continue;
- if (!cname.equals(logClassName) && !cname.equals(simpleLoggerClassName)) {
- // We've found the relevant frame.
- sourceClassName = cname;
- sourceMethodName = frame.getMethodName();
- break;
- }
+ lookingForLogger = !isLoggerImplFrame(cname);
+ return false;
}
+ // We've found the relevant frame.
+ return !skipLoggingFrame(cname) && !isLoggerImplFrame(cname);
}
- if (sourceClassName != null) {
- return sourceClassName + " " + sourceMethodName;
- } else {
- return name;
+ private boolean isLoggerImplFrame(String cname) {
+ return (cname.equals("sun.util.logging.PlatformLogger") ||
+ cname.equals("jdk.internal.logger.SimpleConsoleLogger"));
}
}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java Tue Nov 24 15:05:58 2015 -0800
@@ -103,16 +103,6 @@
void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
/**
- * Returns the number of stack frames represented by the given throwable.
- */
- int getStackTraceDepth(Throwable t);
-
- /**
- * Returns the ith StackTraceElement for the given throwable.
- */
- StackTraceElement getStackTraceElement(Throwable t, int i);
-
- /**
* Returns a new string backed by the provided character array. The
* character array is not copied and must never be modified after the
* String is created, in order to fulfill String's contract.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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 jdk.internal.misc;
+
+public interface JavaLangInvokeAccess {
+ /**
+ * Create a new MemberName instance
+ */
+ Object newMemberName();
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java Tue Nov 24 15:05:58 2015 -0800
@@ -45,6 +45,7 @@
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static JavaUtilJarAccess javaUtilJarAccess;
private static JavaLangAccess javaLangAccess;
+ private static JavaLangInvokeAccess javaLangInvokeAccess;
private static JavaLangRefAccess javaLangRefAccess;
private static JavaIOAccess javaIOAccess;
private static JavaNetAccess javaNetAccess;
@@ -80,6 +81,20 @@
return javaLangAccess;
}
+ public static void setJavaLangInvokeAccess(JavaLangInvokeAccess jlia) {
+ javaLangInvokeAccess = jlia;
+ }
+
+ public static JavaLangInvokeAccess getJavaLangInvokeAccess() {
+ if (javaLangInvokeAccess == null) {
+ try {
+ Class<?> c = Class.forName("java.lang.invoke.MemberName");
+ unsafe.ensureClassInitialized(c);
+ } catch (ClassNotFoundException e) {};
+ }
+ return javaLangInvokeAccess;
+ }
+
public static void setJavaLangRefAccess(JavaLangRefAccess jlra) {
javaLangRefAccess = jlra;
}
--- a/jdk/src/java.base/share/native/include/jvm.h Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.base/share/native/include/jvm.h Tue Nov 24 15:05:58 2015 -0800
@@ -178,6 +178,37 @@
JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index);
/*
+ * java.lang.StackWalker
+ */
+enum {
+ JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2,
+ JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE = 0x10,
+ JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20,
+ JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100
+};
+
+JNIEXPORT jobject JNICALL
+JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
+ jint skip_frames, jint frame_count, jint start_index,
+ jobjectArray classes,
+ jobjectArray frames);
+
+JNIEXPORT jint JNICALL
+JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor,
+ jint frame_count, jint start_index,
+ jobjectArray classes,
+ jobjectArray frames);
+
+JNIEXPORT void JNICALL
+JVM_FillStackFrames(JNIEnv* env, jclass cls,
+ jint start_index,
+ jobjectArray stackFrames,
+ jint from_index, jint toIndex);
+
+JNIEXPORT void JNICALL
+JVM_SetMethodInfo(JNIEnv* env, jobject frame);
+
+/*
* java.lang.Thread
*/
JNIEXPORT void JNICALL
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/native/libjava/StackFrameInfo.c Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+/*
+ * Implementation of class StackFrameInfo
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "jni.h"
+#include "jvm.h"
+
+#include "java_lang_StackFrameInfo.h"
+
+
+/*
+ * Class: java_lang_StackFrameInfo
+ * Method: fillInStackFrames
+ * Signature: (I[Ljava/lang/Object;[Ljava/lang/Object;II)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_StackFrameInfo_fillInStackFrames
+ (JNIEnv *env, jclass dummy, jint startIndex,
+ jobjectArray stackFrames, jint fromIndex, jint toIndex) {
+ JVM_FillStackFrames(env, dummy, startIndex,
+ stackFrames, fromIndex, toIndex);
+}
+
+/*
+ * Class: java_lang_StackFrameInfo
+ * Method: setMethodInfo
+ * Signature: (Ljava/lang/Class;)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_StackFrameInfo_setMethodInfo
+ (JNIEnv *env, jobject stackframeinfo) {
+ JVM_SetMethodInfo(env, stackframeinfo);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/native/libjava/StackStreamFactory.c Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.
+ */
+
+/*
+ * Implementation of class StackStreamfactory and AbstractStackWalker
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "jni.h"
+#include "jvm.h"
+
+#include "java_lang_StackStreamFactory.h"
+#include "java_lang_StackStreamFactory_AbstractStackWalker.h"
+
+/*
+ * Class: java_lang_StackStreamFactory
+ * Method: checkStackWalkModes
+ * Signature: ()
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_StackStreamFactory_checkStackWalkModes
+ (JNIEnv *env, jclass dummy)
+{
+ return JVM_STACKWALK_FILL_CLASS_REFS_ONLY == java_lang_StackStreamFactory_FILL_CLASS_REFS_ONLY &&
+ JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE == java_lang_StackStreamFactory_FILTER_FILL_IN_STACKTRACE &&
+ JVM_STACKWALK_SHOW_HIDDEN_FRAMES == java_lang_StackStreamFactory_SHOW_HIDDEN_FRAMES &&
+ JVM_STACKWALK_FILL_LIVE_STACK_FRAMES == java_lang_StackStreamFactory_FILL_LIVE_STACK_FRAMES;
+}
+
+/*
+ * Class: java_lang_StackStreamFactory_AbstractStackWalker
+ * Method: callStackWalk
+ * Signature: (JIII[Ljava/lang/Class;[Ljava/lang/StackWalker/StackFrame;)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_callStackWalk
+ (JNIEnv *env, jobject stackstream, jlong mode, jint skipFrames, jint batchSize, jint startIndex,
+ jobjectArray classes, jobjectArray frames)
+{
+ return JVM_CallStackWalk(env, stackstream, mode, skipFrames, batchSize,
+ startIndex, classes, frames);
+}
+
+/*
+ * Class: java_lang_StackStreamFactory_AbstractStackWalker
+ * Method: fetchStackFrames
+ * Signature: (JJII[Ljava/lang/Class;[Ljava/lang/StackWalker/StackFrame;)I
+ */
+JNIEXPORT jint JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_fetchStackFrames
+ (JNIEnv *env, jobject stackstream, jlong mode, jlong anchor,
+ jint batchSize, jint startIndex,
+ jobjectArray classes, jobjectArray frames)
+{
+ return JVM_MoreStackWalk(env, stackstream, mode, anchor, batchSize,
+ startIndex, classes, frames);
+}
--- a/jdk/src/java.logging/share/classes/java/util/logging/LogRecord.java Tue Nov 24 18:32:38 2015 +0000
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LogRecord.java Tue Nov 24 15:05:58 2015 -0800
@@ -30,9 +30,8 @@
import java.util.concurrent.atomic.AtomicLong;
import java.io.*;
import java.time.Clock;
+import java.util.function.Predicate;
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
import static jdk.internal.logger.SimpleConsoleLogger.skipLoggingFrame;
/**
@@ -661,42 +660,58 @@
//
private void inferCaller() {
needToInferCaller = false;
- JavaLangAccess access = SharedSecrets.getJavaLangAccess();
- Throwable throwable = new Throwable();
- int depth = access.getStackTraceDepth(throwable);
+ // Skip all frames until we have found the first logger frame.
+ Optional<StackWalker.StackFrame> frame = new CallerFinder().get();
+ frame.ifPresent(f -> {
+ setSourceClassName(f.getClassName());
+ setSourceMethodName(f.getMethodName());
+ });
- boolean lookingForLogger = true;
- for (int ix = 0; ix < depth; ix++) {
- // Calling getStackTraceElement directly prevents the VM
- // from paying the cost of building the entire stack frame.
- StackTraceElement frame =
- access.getStackTraceElement(throwable, ix);
- String cname = frame.getClassName();
- boolean isLoggerImpl = isLoggerImplFrame(cname);
- if (lookingForLogger) {
- // Skip all frames until we have found the first logger frame.
- if (isLoggerImpl) {
- lookingForLogger = false;
- }
- } else {
- if (!isLoggerImpl) {
- // skip logging/logger infrastructure and reflection calls
- if (!skipLoggingFrame(cname)) {
- // We've found the relevant frame.
- setSourceClassName(cname);
- setSourceMethodName(frame.getMethodName());
- return;
- }
- }
- }
- }
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
- private boolean isLoggerImplFrame(String cname) {
- // the log record could be created for a platform logger
- return (cname.equals("java.util.logging.Logger") ||
- cname.startsWith("sun.util.logging.PlatformLogger"));
+ /*
+ * CallerFinder is a stateful predicate.
+ */
+ static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
+ static final StackWalker WALKER = StackWalker.getInstance();
+
+ /**
+ * Returns StackFrame of the caller's frame.
+ * @return StackFrame of the caller's frame.
+ */
+ Optional<StackWalker.StackFrame> get() {
+ return WALKER.walk((s) -> s.filter(this).findFirst());
+ }
+
+ private boolean lookingForLogger = true;
+ /**
+ * Returns true if we have found the caller's frame, false if the frame
+ * must be skipped.
+ *
+ * @param t The frame info.
+ * @return true if we have found the caller's frame, false if the frame
+ * must be skipped.
+ */
+ @Override
+ public boolean test(StackWalker.StackFrame t) {
+ final String cname = t.getClassName();
+ // We should skip all frames until we have found the logger,
+ // because these frames could be frames introduced by e.g. custom
+ // sub classes of Handler.
+ if (lookingForLogger) {
+ // the log record could be created for a platform logger
+ lookingForLogger = !isLoggerImplFrame(cname);
+ return false;
+ }
+ // skip logging/logger infrastructure and reflection calls
+ return !skipLoggingFrame(cname);
+ }
+
+ private boolean isLoggerImplFrame(String cname) {
+ return (cname.equals("java.util.logging.Logger") ||
+ cname.startsWith("sun.util.logging.PlatformLogger"));
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/AcrossThreads.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8143214
+ * @summary Verify that StackWalker works well when one instance of StackWalker
+ * is used by several threads sequentially or concurrently.
+ * @run testng AcrossThreads
+ */
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import java.lang.StackWalker.StackFrame;
+import static java.lang.StackWalker.Option.*;
+
+import org.testng.annotations.*;
+import static org.testng.Assert.*;
+
+public class AcrossThreads {
+ static final StackWalker WALKERS[] = new StackWalker[] {
+ StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
+ StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
+ StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
+ };
+
+ @DataProvider
+ public StackWalker[][] walkerProvider() {
+ return new StackWalker[][] {
+ new StackWalker[] { WALKERS[0] },
+ new StackWalker[] { WALKERS[1] },
+ new StackWalker[] { WALKERS[2] }
+ };
+ }
+
+ @Test(dataProvider = "walkerProvider")
+ public void test(StackWalker walker) {
+ Thread t1 = new T1(walker);
+ // call methods of one instance of StackWalker sequentially in T1, T2, T3.
+ t1.start();
+ try {
+ t1.join();
+ } catch (InterruptedException e) { }
+
+ List<Thread> threads = new ArrayList<Thread>();
+ for (int i = 0; i < 100; i++) {
+ threads.add(new T1(walker));
+ threads.add(new T2(walker));
+ threads.add(new T3(walker));
+ }
+ // call methods of one instance of StackWalker concurrently in several threads.
+ threads.parallelStream().forEach(t -> {
+ t.setDaemon(true);
+ t.start();
+ });
+ threads.parallelStream().forEach(t -> {
+ try {
+ t.join();
+ } catch (InterruptedException e) { }
+ });
+ }
+
+ interface Consumer {
+ final int LOOPS = 5;
+
+ public void consume();
+
+ default public void assertWalker(StackWalker walker, int n) {
+ if (--n == 0) {
+ Map<String, Integer> methods = new HashMap<String, Integer>();
+ walker.forEach(f -> {
+ Integer i = methods.putIfAbsent(f.getMethodName(), 1);
+ if (i != null) {
+ methods.put(f.getMethodName(), i + 1);
+ }
+ });
+
+ // verify that walker.forEach(...) reaches the specified methods.
+ assertTrue(methods.get("consume") == 1);
+ assertTrue(methods.get("run") == 1);
+ assertTrue(methods.get("assertWalker") == LOOPS);
+
+ // verify that walker.walk(...) reaches the specified methods.
+ assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
+ .filter(mn -> mn.equals("consume"))
+ .count()) == 1);
+ assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
+ .filter(mn -> mn.equals("run"))
+ .count()) == 1);
+ assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
+ .filter(mn -> mn.equals("assertWalker"))
+ .count()) == LOOPS);
+ } else {
+ assertWalker(walker, n);
+ }
+ }
+ }
+
+ class T1 extends Thread implements Consumer {
+ final StackWalker walker;
+
+ public T1(StackWalker walker) {
+ this.walker = walker;
+ }
+
+ public void run() {
+ consume();
+
+ Thread t2 = new T2(walker);
+ t2.start();
+ try {
+ t2.join();
+ } catch (InterruptedException e) { }
+
+ consume();
+ }
+
+ public void consume() {
+ assertWalker(walker, LOOPS);
+
+ // verify walker.walk() reaches T1 class through methods run() and consume().
+ assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
+ .count()) == 2);
+
+ assertCallerClass(walker);
+ assertEquals(T1.class, walker.getCallerClass());
+ }
+ }
+
+ class T2 extends Thread implements Consumer {
+ final StackWalker walker;
+
+ public T2(StackWalker walker) {
+ this.walker = walker;
+ }
+
+ public void run() {
+ consume();
+
+ Thread t3 = new T3(walker);
+ t3.start();
+ try {
+ t3.join();
+ } catch (InterruptedException e) { }
+
+ consume();
+ }
+
+ public void consume() {
+ assertWalker(walker, LOOPS);
+
+ // verify walker.walk() reaches T2 class through methods run() and consume().
+ assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass())
+ .count()) == 2);
+ // verify T1 is not reached, even if call is invoked
+ // from test()->T1.start()->T1.run()->T2
+ assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
+ .count()) == 0);
+
+ assertCallerClass(walker);
+ assertEquals(T2.class, walker.getCallerClass());
+ }
+ }
+
+ class T3 extends Thread implements Consumer {
+ final StackWalker walker;
+
+ public T3(StackWalker walker) {
+ this.walker = walker;
+ }
+
+ public void run() {
+ consume();
+ }
+
+ public void consume() {
+ assertWalker(walker, LOOPS);
+
+ // verify walker.walk() reaches T1 class through methods run() and consume().
+ assertTrue(walker.walk(s -> s.filter(f -> T3.class == f.getDeclaringClass())
+ .count()) == 2);
+ // verify T1, T2 is not reached, even if call is invoked
+ // from test() -> T1.start() -> T1.run() -> T2.start() -> T2.run() -> T3
+ assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass())
+ .count()) == 0);
+ assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
+ .count()) == 0);
+
+ assertCallerClass(walker);
+ assertEquals(T3.class, walker.getCallerClass());
+ }
+ }
+
+ static void assertCallerClass(StackWalker walker) {
+ // verify walker.getCallerClass() get the expected class.
+ call(walker);
+ }
+
+ static void call(StackWalker walker) {
+ Class<?> c = walker.getCallerClass();
+ assertEquals(c, AcrossThreads.class);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/Basic.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8140450
+ * @summary Basic test for the StackWalker::walk method
+ * @run testng Basic
+ */
+
+import java.lang.StackWalker.StackFrame;
+import java.util.List;
+import java.util.stream.Collectors;
+import static java.lang.StackWalker.Option.*;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class Basic {
+ private static boolean verbose = false;
+
+ @DataProvider(name = "stackDepths")
+ public static Object[][] stackDepths() {
+ return new Object[][] {
+ { new int[] { 12 }, new int[] { 4, 8, 12} },
+ { new int[] { 18 }, new int[] { 8, 16, 20} },
+ { new int[] { 32 }, new int[] { 16, 32, 64} },
+ };
+ }
+
+ /**
+ * For a stack of a given depth, it creates a StackWalker with an estimate.
+ * Test walking different number of frames
+ */
+ @Test(dataProvider = "stackDepths")
+ public static void test(int[] depth, int[] estimates) {
+ Basic test = new Basic(depth[0]);
+ for (int estimate : estimates) {
+ test.walk(estimate);
+ }
+ }
+
+ private final int depth;
+ Basic(int depth) {
+ this.depth = depth;
+ }
+
+ /*
+ * Setup a stack builder with the expected stack depth
+ * Walk the stack and count the frames.
+ */
+ void walk(int estimate) {
+ int limit = Math.min(depth, 16);
+ List<StackFrame> frames = new StackBuilder(depth, limit).build();
+ System.out.format("depth=%d estimate=%d expected=%d walked=%d%n",
+ depth, estimate, limit, frames.size());
+ assertEquals(limit, frames.size());
+ }
+
+ class StackBuilder {
+ private final int stackDepth;
+ private final int limit;
+ private int depth = 0;
+ private List<StackFrame> result;
+ StackBuilder(int stackDepth, int limit) {
+ this.stackDepth = stackDepth; // build method;
+ this.limit = limit;
+ }
+ List<StackFrame> build() {
+ trace("build");
+ m1();
+ return result;
+ }
+ void m1() {
+ trace("m1");
+ m2();
+ }
+ void m2() {
+ trace("m2");
+ m3();
+ }
+ void m3() {
+ trace("m3");
+ m4();
+ }
+ void m4() {
+ trace("m4");
+ int remaining = stackDepth-depth-1;
+ if (remaining >= 4) {
+ m1();
+ } else {
+ filler(remaining);
+ }
+ }
+ void filler(int i) {
+ trace("filler");
+ if (i == 0)
+ walk();
+ else
+ filler(--i);
+ }
+
+ void walk() {
+ StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
+ result = walker.walk(s -> s.limit(limit).collect(Collectors.toList()));
+ }
+ void trace(String methodname) {
+ ++depth;
+ if (verbose)
+ System.out.format("%2d: %s%n", depth, methodname);
+ }
+ }
+
+ static void assertEquals(int x, int y) {
+ if (x != y) {
+ throw new RuntimeException(x + " != " + y);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/CallerFromMain.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8140450
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.*
+ * @summary Test if the getCallerClass method returns empty optional
+ * @run main CallerFromMain exec
+ */
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.OutputAnalyzer;
+
+public class CallerFromMain {
+
+ private static final StackWalker sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+ public static void main(String[] args) throws Exception {
+ if (args.length > 0) {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "CallerFromMain");
+ OutputAnalyzer output = ProcessTools.executeProcess(pb);
+ System.out.println(output.getOutput());
+ output.shouldHaveExitValue(0);
+ return;
+ }
+
+ // StackWalker::getCallerClass
+ // CallerFromMain::main
+ // no caller
+ try {
+ Class<?> c = sw.getCallerClass();
+ throw new RuntimeException("UOE not thrown. Caller: " + c);
+ } catch (IllegalStateException e) {}
+
+ // StackWalker::getCallerClass
+ // Runnable::run
+ // Thread::run
+ Thread t1 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Class<?> c = sw.getCallerClass();
+ System.out.println("Call from Thread.run: " + c);
+ assertThreadClassAsCaller(c);
+ }
+ });
+ t1.setDaemon(true);
+ t1.start();
+
+ // StackWalker::getCallerClass
+ // CallerFromMain::doit
+ // Thread::run
+ Thread t2 = new Thread(CallerFromMain::doit);
+ t2.setDaemon(true);
+ t2.start();
+
+ // StackWalker::getCallerClass
+ // MyRunnable::run
+ // Thread::run
+ Thread t3 = new Thread(new MyRunnable());
+ t3.setDaemon(true);
+ t3.start();
+
+ // StackWalker::getCallerClass
+ // Runnable::run
+ // MyThread::run
+ Thread t4 = new MyThread(new Runnable() {
+ @Override
+ public void run() {
+ Class<?> c = sw.getCallerClass();
+ System.out.println("Call from MyThread.run: " + c);
+ if (c != MyThread.class) {
+ throw new RuntimeException("Expected MyThread.class but got " + c);
+ }
+ }
+ });
+ t4.setDaemon(true);
+ t4.start();
+
+ t1.join();
+ t2.join();
+ t3.join();
+ t4.join();
+ }
+
+ static class MyThread extends Thread {
+ final Runnable runnable;
+ MyThread(Runnable runnable) {
+ super("MyThread");
+ this.runnable = runnable;
+ }
+ public void run() {
+ runnable.run();
+ }
+ }
+
+ static class MyRunnable implements Runnable {
+ @Override
+ public void run() {
+ Class<?> c = sw.getCallerClass();
+ System.out.println("Call from Thread::run: " + c);
+ assertThreadClassAsCaller(c);
+ }
+ }
+
+ static void doit() {
+ Class<?> c = sw.getCallerClass();
+ System.out.println("Call from CallerFromMain.doit: " + c);
+ assertThreadClassAsCaller(c);
+ }
+
+ static void assertThreadClassAsCaller(Class<?> caller) {
+ if (caller != Thread.class) {
+ throw new RuntimeException("Expected Thread.class but got " + caller);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/DumpStackTest.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8143214
+ * @summary Verify outputs of Thread.dumpStack() and Throwable.printStackTrace().
+ * This test should also been run against jdk9 successfully except of
+ * VM option MemberNameInStackFrame.
+ * @run main/othervm DumpStackTest
+ * @run main/othervm -Dstackwalk.newThrowable=false DumpStackTest
+ * @run main/othervm -Dstackwalk.newThrowable=true -XX:-MemberNameInStackFrame DumpStackTest
+ * @run main/othervm -Dstackwalk.newThrowable=true -XX:+MemberNameInStackFrame DumpStackTest
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public class DumpStackTest {
+
+ public static void main(String args[]) {
+ test();
+ testThread();
+ testLambda();
+ testMethodInvoke();
+ testMethodHandle();
+ }
+
+ static class CallFrame {
+ final String classname;
+ final String methodname;
+ CallFrame(Class<?> c, String methodname) {
+ this(c.getName(), methodname);
+ }
+ CallFrame(String classname, String methodname) {
+ this.classname = classname;
+ this.methodname = methodname;
+ }
+
+ String getClassName() {
+ return classname;
+ }
+ String getMethodName() {
+ return methodname;
+ }
+ String getFileName() {
+ int i = classname.lastIndexOf('.');
+ int j = classname.lastIndexOf('$');
+ String name = classname.substring(i+1, j >= 0 ? j : classname.length());
+ return name + ".java";
+ }
+ @Override
+ public String toString() {
+ return classname + "." + methodname + "(" + getFileName() + ")";
+ }
+ }
+
+ static void test() {
+ CallFrame[] callStack = new CallFrame[] {
+ new CallFrame(Thread.class, "getStackTrace"),
+ new CallFrame(DumpStackTest.class, "test"),
+ new CallFrame(DumpStackTest.class, "main"),
+ // if invoked from jtreg
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), // non-public class
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
+ new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
+ new CallFrame(Method.class, "invoke"),
+ new CallFrame(Thread.class, "run"),
+ };
+
+ assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
+ getStackTrace(callStack);
+ }
+
+ static void getStackTrace(CallFrame[] callStack) {
+ // this method is the top of the stack
+ callStack[0] = new CallFrame(DumpStackTest.class, "getStackTrace");
+
+ try {
+ throw new RuntimeException();
+ } catch(RuntimeException ex) {
+ assertStackTrace(ex.getStackTrace(), callStack);
+ }
+ }
+ static void testThread() {
+ Thread t1 = new Thread() {
+ public void run() {
+ c();
+ }
+
+ void c() {
+ CallFrame[] callStack = new CallFrame[] {
+ new CallFrame(Thread.class, "getStackTrace"),
+ new CallFrame(this.getClass(), "c"),
+ new CallFrame(this.getClass(), "run")
+ };
+ assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
+ DumpStackTest.getStackTrace(callStack);
+ }
+ };
+ t1.start();
+ try {
+ t1.join();
+ } catch(InterruptedException e) {}
+ }
+
+ static void testLambda() {
+ Consumer<Void> c = (x) -> consumeLambda();
+ c.accept(null);
+ }
+
+ static void consumeLambda() {
+ CallFrame[] callStack = new CallFrame[]{
+ new CallFrame(Thread.class, "getStackTrace"),
+ new CallFrame(DumpStackTest.class, "consumeLambda"),
+ new CallFrame(DumpStackTest.class, "lambda$testLambda$0"),
+ new CallFrame(DumpStackTest.class, "testLambda"),
+ new CallFrame(DumpStackTest.class, "main"),
+ // if invoked from jtreg
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
+ new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
+ new CallFrame(Method.class, "invoke"),
+ new CallFrame(Thread.class, "run")
+ };
+ assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
+ DumpStackTest.getStackTrace(callStack);
+ }
+
+ static void testMethodInvoke() {
+ try {
+ Method m = DumpStackTest.class.getDeclaredMethod("methodInvoke");
+ m.invoke(null);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static void methodInvoke() {
+ CallFrame[] callStack = new CallFrame[] {
+ new CallFrame(Thread.class, "getStackTrace"),
+ new CallFrame(DumpStackTest.class, "methodInvoke"),
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
+ new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
+ new CallFrame(Method.class, "invoke"),
+ new CallFrame(DumpStackTest.class, "testMethodInvoke"),
+ new CallFrame(DumpStackTest.class, "main"),
+ // if invoked from jtreg
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
+ new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
+ new CallFrame(Method.class, "invoke"),
+ new CallFrame(Thread.class, "run")
+ };
+ assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
+ DumpStackTest.getStackTrace(callStack);
+ }
+
+ static void testMethodHandle() {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ try {
+ MethodHandle handle = lookup.findStatic(DumpStackTest.class, "methodHandle",
+ MethodType.methodType(void.class));
+ handle.invoke();
+ } catch(Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ static void methodHandle() {
+ CallFrame[] callStack = new CallFrame[]{
+ new CallFrame(Thread.class, "getStackTrace"),
+ new CallFrame(DumpStackTest.class, "methodHandle"),
+ new CallFrame(DumpStackTest.class, "testMethodHandle"),
+ new CallFrame(DumpStackTest.class, "main"),
+ // if invoked from jtreg
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
+ new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
+ new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
+ new CallFrame(Method.class, "invoke"),
+ new CallFrame(Thread.class, "run")
+ };
+ assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
+ DumpStackTest.getStackTrace(callStack);
+ }
+
+ static void assertStackTrace(StackTraceElement[] actual, CallFrame[] expected) {
+ System.out.println("--- Actual ---");
+ Arrays.stream(actual).forEach(e -> System.out.println(e));
+ System.out.println("--- Expected ---");
+ Arrays.stream(expected).forEach(e -> System.out.println(e));
+
+ for (int i = 0, j = 0; i < actual.length; i++) {
+ // filter test framework classes
+ if (actual[i].getClassName().startsWith("com.sun.javatest.regtest"))
+ continue;
+ assertEquals(actual[i], expected[j++], i);
+ }
+
+ }
+ static void assertEquals(StackTraceElement actual, CallFrame expected, int idx) {
+ if (!actual.getClassName().equals(expected.getClassName()) ||
+ !actual.getFileName().equals(expected.getFileName()) ||
+ !actual.getMethodName().equals(expected.getMethodName())) {
+ throw new RuntimeException("StackTraceElements mismatch at index " + idx +
+ ". Expected [" + expected + "], but get [" + actual + "]");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/EmbeddedStackWalkTest.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+/**
+ * @test
+ * @bug 8143214
+ * @summary Verify StackWalker works well when embedded in another
+ * StackWalker's functions.
+ * @run testng/othervm EmbeddedStackWalkTest
+ */
+
+import java.lang.StackWalker.StackFrame;
+import static java.lang.StackWalker.Option.*;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.EnumSet;
+
+import org.testng.annotations.*;
+import static org.testng.Assert.*;
+
+public class EmbeddedStackWalkTest {
+ static final StackWalker WALKERS[] = new StackWalker[] {
+ StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
+ StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
+ StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
+ };
+
+ static final int BIG_LOOP = 30;
+ static final int SMALL_LOOP = 5;
+
+ @DataProvider
+ public StackWalker[][] walkerProvider() {
+ return new StackWalker[][] {
+ new StackWalker[] { WALKERS[0] },
+ new StackWalker[] { WALKERS[1] },
+ new StackWalker[] { WALKERS[2] }
+ };
+ }
+
+ @Test(dataProvider = "walkerProvider")
+ public void test(StackWalker walker) {
+ C1.call(walker, BIG_LOOP);
+ }
+
+ // line numbers are hardcoded for now.
+ // Should annotate the line numbers and auto-generated these constants
+ // for test verification instead
+ static final int BEGIN_LINE = 71; // the begin line number of approximate range.
+ static final int END_LINE = 136; // the end line number of approximate range.
+ static class C1 { // here is the begin line number of approximate range, L71.
+ public static void call(StackWalker walker, int loops) {
+ if (loops == 0) {
+ String caller = walker.walk(s ->
+ s.map(StackFrame::getClassName)
+ .filter(cn -> !cn.startsWith("sun.reflect.") && !cn.startsWith("java.lang.invoke"))
+ .skip(2).findFirst()
+ ).get();
+ assertEquals(caller, C1.class.getName());
+
+ walker.forEach(f -> C2.testEmbeddedWalker());
+ } else {
+ call(walker, --loops);
+ }
+ }
+ }
+
+ static class C2 {
+ static final StackWalker embeddedWalkers[] = new StackWalker[] {
+ StackWalker.getInstance(),
+ StackWalker.getInstance(SHOW_REFLECT_FRAMES),
+ StackWalker.getInstance(SHOW_HIDDEN_FRAMES)
+ };
+
+ public static void testEmbeddedWalker() {
+ walk(SMALL_LOOP);
+ }
+
+ static void walk(int loops) {
+ if (loops == 0) {
+ Arrays.stream(embeddedWalkers)
+ .forEach(walker -> run(walker));
+ } else {
+ walk(--loops);
+ }
+ }
+
+ static void run(StackWalker walker) {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodHandle handle = null;
+ try {
+ handle = lookup.findStatic(C2.class, "call",
+ MethodType.methodType(void.class, StackWalker.class));
+ handle.invoke(walker);
+ } catch(Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ static void call(StackWalker walker) {
+ String caller = walker.walk(s ->
+ s.map(StackFrame::getClassName)
+ .filter(cn -> !cn.startsWith("sun.reflect.") && !cn.startsWith("java.lang.invoke"))
+ .skip(2).findFirst()
+ ).get();
+ assertEquals(caller, C2.class.getName());
+
+ verify(walker, C1.class, "call");
+ verify(walker, C2.class, "call");
+ verify(walker, C2.class, "run");
+ verify(walker, C2.class, "walk");
+ verify(walker, C2.class, "testEmbeddedWalker");
+ } // here is the end line number of approximate range, L136.
+
+ static void verify(StackWalker walker, Class<?> c, String mn) {
+ final String fileName = "EmbeddedStackWalkTest.java";
+ walker.walk(s -> {
+ s.limit(BIG_LOOP)
+ .filter(f -> c.getName().equals(f.getClassName()) && mn.equals(f.getMethodName()))
+ .forEach(f -> {
+ assertEquals(f.getFileName().get(), fileName);
+ int line = f.getLineNumber().getAsInt();
+ assertTrue(line >= BEGIN_LINE && line <= END_LINE);
+
+ StackTraceElement st = f.toStackTraceElement();
+ assertEquals(c.getName(), st.getClassName());
+ assertEquals(mn, st.getMethodName());
+ assertEquals(st.getFileName(), fileName);
+ line = st.getLineNumber();
+ assertTrue(line >= BEGIN_LINE && line <= END_LINE);
+ });
+ return null;
+ });
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/GetCallerClassTest.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8140450
+ * @summary Basic test for StackWalker.getCallerClass()
+ * @run main/othervm -XX:-MemberNameInStackFrame GetCallerClassTest
+ * @run main/othervm -XX:+MemberNameInStackFrame GetCallerClassTest
+ * @run main/othervm GetCallerClassTest sm
+ */
+
+import static java.lang.StackWalker.Option.*;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.List;
+
+public class GetCallerClassTest {
+ private final StackWalker walker;
+ private final boolean expectUOE;
+
+ public GetCallerClassTest(StackWalker sw, boolean expect) {
+ this.walker = sw;
+ this.expectUOE = expect;
+ }
+ public static void main(String... args) throws Exception {
+ if (args.length > 0 && args[0].equals("sm")) {
+ PermissionCollection perms = new Permissions();
+ perms.add(new StackFramePermission("retainClassReference"));
+ Policy.setPolicy(new Policy() {
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission p) {
+ return perms.implies(p);
+ }
+ });
+ System.setSecurityManager(new SecurityManager());
+ }
+ new GetCallerClassTest(StackWalker.getInstance(), true).test();
+ new GetCallerClassTest(StackWalker.getInstance(RETAIN_CLASS_REFERENCE), false).test();
+ }
+
+ public void test() {
+ new TopLevelCaller().run();
+ new Nested().createNestedCaller().run();
+ new InnerClassCaller().run();
+ new ReflectionTest().run();
+
+ List<Thread> threads = Arrays.asList(
+ new Thread(new TopLevelCaller()),
+ new Thread(new Nested().createNestedCaller()),
+ new Thread(new InnerClassCaller()),
+ new Thread(new ReflectionTest())
+ );
+ threads.stream().forEach(Thread::start);
+ threads.stream().forEach(t -> {
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ public static void staticGetCallerClass(StackWalker stackWalker,
+ Class<?> expected,
+ boolean expectUOE) {
+ try {
+ Class<?> c = stackWalker.getCallerClass();
+ assertEquals(c, expected);
+ if (expectUOE) { // Should have thrown
+ throw new RuntimeException("Didn't get expected exception");
+ }
+ } catch (RuntimeException e) { // also catches UOE
+ if (expectUOE && causeIsUOE(e)) {
+ return; /* expected */
+ }
+ System.err.println("Unexpected exception:");
+ throw e;
+ }
+ }
+
+ public static void reflectiveGetCallerClass(StackWalker stackWalker,
+ Class<?> expected,
+ boolean expectUOE) {
+ try {
+ Method m = StackWalker.class.getMethod("getCallerClass");
+ Class<?> c = (Class<?>) m.invoke(stackWalker);
+ assertEquals(c, expected);
+ if (expectUOE) { // Should have thrown
+ throw new RuntimeException("Didn't get expected exception");
+ }
+ } catch (Throwable e) {
+ if (expectUOE && causeIsUOE(e)) {
+ return; /* expected */
+ }
+ System.err.println("Unexpected exception:");
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void methodHandleGetCallerClass(StackWalker stackWalker,
+ Class<?> expected,
+ boolean expectUOE) {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ try {
+ MethodHandle mh = lookup.findVirtual(StackWalker.class, "getCallerClass",
+ MethodType.methodType(Class.class));
+ Class<?> c = (Class<?>) mh.invokeExact(stackWalker);
+ assertEquals(c, expected);
+ if (expectUOE) { // Should have thrown
+ throw new RuntimeException("Didn't get expected exception");
+ }
+ } catch (Throwable e) {
+ if (expectUOE && causeIsUOE(e)) {
+ return; /* expected */
+ }
+ System.err.println("Unexpected exception:");
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void assertEquals(Class<?> c, Class<?> expected) {
+ if (expected != c) {
+ throw new RuntimeException(c + " != " + expected);
+ }
+ }
+
+ /** Is there an UnsupportedOperationException in there? */
+ public static boolean causeIsUOE(Throwable t) {
+ while (t != null) {
+ if (t instanceof UnsupportedOperationException) {
+ return true;
+ }
+ t = t.getCause();
+ }
+ return false;
+ }
+
+ class TopLevelCaller implements Runnable {
+ public void run() {
+ GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
+ GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
+ GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
+ }
+ }
+
+ class Nested {
+ NestedClassCaller createNestedCaller() { return new NestedClassCaller(); }
+ class NestedClassCaller implements Runnable {
+ public void run() {
+ GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
+ GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
+ GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
+ }
+ }
+ }
+
+ class InnerClassCaller implements Runnable {
+ public void run() {
+ new Inner().test();
+ }
+ class Inner {
+ void test() {
+ GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
+ GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
+ GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
+ }
+ }
+ }
+
+ class ReflectionTest implements Runnable {
+ final MethodType methodType =
+ MethodType.methodType(void.class, StackWalker.class, Class.class, boolean.class);
+
+ public void run() {
+ callMethodHandle();
+ callMethodHandleRefl();
+ callMethodInvoke();
+ callMethodInvokeRefl();
+ }
+ void callMethodHandle() {
+ MethodHandles.Lookup lookup = MethodHandles.publicLookup();
+ try {
+ MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
+ "staticGetCallerClass",
+ methodType);
+ mh.invokeExact(walker, ReflectionTest.class, expectUOE);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void callMethodHandleRefl() {
+ MethodHandles.Lookup lookup = MethodHandles.publicLookup();
+ try {
+ MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
+ "reflectiveGetCallerClass",
+ methodType);
+ mh.invokeExact(walker, ReflectionTest.class, expectUOE);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void callMethodInvoke() {
+ try {
+ Method m = GetCallerClassTest.class.getMethod("staticGetCallerClass",
+ StackWalker.class, Class.class, boolean.class);
+ m.invoke(null, walker, ReflectionTest.class, expectUOE);
+ } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ void callMethodInvokeRefl() {
+ try {
+ Method m = GetCallerClassTest.class.getMethod("reflectiveGetCallerClass",
+ StackWalker.class, Class.class, boolean.class);
+ m.invoke(null, walker, ReflectionTest.class, expectUOE);
+ } catch (UnsupportedOperationException e) {
+ throw e;
+ } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/HiddenFrames.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8020968
+ * @summary Basic test for hidden frames
+ * @run main HiddenFrames
+ */
+
+import java.lang.StackWalker.Option;
+import java.lang.StackWalker.StackFrame;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+public class HiddenFrames {
+ public static void main(String... args) throws Exception {
+ new HiddenFrames().test();
+ new HiddenFrames(Option.SHOW_REFLECT_FRAMES).test();
+ new HiddenFrames(Option.SHOW_HIDDEN_FRAMES).test();
+ }
+
+ private final Option option;
+ private final StackWalker walker;
+ private final List<StackFrame> lambdas = new ArrayList<>();
+ private final List<StackFrame> reflects = new ArrayList<>();
+
+ HiddenFrames() {
+ this.option = null;
+ this.walker = StackWalker.getInstance();
+ }
+ HiddenFrames(Option option) {
+ this.option = option;
+ this.walker = StackWalker.getInstance(option);
+ }
+
+ void test() throws Exception {
+ walk();
+ walkFromReflection();
+ }
+
+ void walk() {
+ Stream.of(0).forEach(i -> walker.walk(s ->
+ {
+ s.forEach(this::checkFrame);
+ return null;
+ }));
+
+ // only check hidden frames but not reflection frames
+ // walk is not invoked via reflection
+ if (option == null && !lambdas.isEmpty()) {
+ throw new RuntimeException("Hidden frames are shown");
+ }
+
+ if (option == Option.SHOW_HIDDEN_FRAMES && lambdas.isEmpty()) {
+ throw new RuntimeException("No hidden Lambda frame");
+ }
+ }
+
+ void walkFromReflection() throws Exception {
+ Method m = HiddenFrames.class.getDeclaredMethod("walk");
+ m.invoke(this);
+
+ if (option == null && !lambdas.isEmpty()) {
+ throw new RuntimeException("Hidden frames are shown");
+ }
+
+ if (option == Option.SHOW_HIDDEN_FRAMES && lambdas.isEmpty()) {
+ throw new RuntimeException("No hidden Lambda frame");
+ }
+
+ if (option != null && reflects.isEmpty()) {
+ throw new RuntimeException("No reflection frame");
+ }
+ }
+
+ void checkFrame(StackFrame frame) {
+ String cn = frame.getClassName();
+ if (cn.startsWith("java.lang.reflect.") || cn.startsWith("sun.reflect.")) {
+ reflects.add(frame);
+ }
+ if (cn.contains("$$Lambda$")) {
+ lambdas.add(frame);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/LocalsAndOperands.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8020968
+ * @summary Sanity test for locals and operands
+ * @run main LocalsAndOperands
+ */
+
+import java.lang.StackWalker.StackFrame;
+import java.lang.reflect.*;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class LocalsAndOperands {
+ static Class<?> liveStackFrameClass;
+ static Class<?> primitiveValueClass;
+ static StackWalker extendedWalker;
+ static Method getLocals;
+ static Method getOperands;
+ static Method getMonitors;
+ static Method primitiveType;
+ public static void main(String... args) throws Exception {
+ liveStackFrameClass = Class.forName("java.lang.LiveStackFrame");
+ primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue");
+
+ getLocals = liveStackFrameClass.getDeclaredMethod("getLocals");
+ getLocals.setAccessible(true);
+
+ getOperands = liveStackFrameClass.getDeclaredMethod("getStack");
+ getOperands.setAccessible(true);
+
+ getMonitors = liveStackFrameClass.getDeclaredMethod("getMonitors");
+ getMonitors.setAccessible(true);
+
+ primitiveType = primitiveValueClass.getDeclaredMethod("type");
+ primitiveType.setAccessible(true);
+
+ Method method = liveStackFrameClass.getMethod("getStackWalker");
+ method.setAccessible(true);
+ extendedWalker = (StackWalker) method.invoke(null);
+ new LocalsAndOperands(extendedWalker, true).test();
+
+ // no access to local and operands.
+ new LocalsAndOperands(StackWalker.getInstance(), false).test();
+ }
+
+ private final StackWalker walker;
+ private final boolean extended;
+ LocalsAndOperands(StackWalker walker, boolean extended) {
+ this.walker = walker;
+ this.extended = extended;
+ }
+
+ synchronized void test() throws Exception {
+ int x = 10;
+ char c = 'z';
+ String hi = "himom";
+ long l = 1000000L;
+ double d = 3.1415926;
+
+ List<StackWalker.StackFrame> frames = walker.walk(s -> s.collect(Collectors.toList()));
+ if (extended) {
+ for (StackWalker.StackFrame f : frames) {
+ System.out.println("frame: " + f);
+ Object[] locals = (Object[]) getLocals.invoke(f);
+ for (int i = 0; i < locals.length; i++) {
+ System.out.format("local %d: %s type %s%n", i, locals[i], type(locals[i]));
+ }
+
+ Object[] operands = (Object[]) getOperands.invoke(f);
+ for (int i = 0; i < operands.length; i++) {
+ System.out.format("operand %d: %s type %s%n", i, operands[i], type(operands[i]));
+ }
+
+ Object[] monitors = (Object[]) getMonitors.invoke(f);
+ for (int i = 0; i < monitors.length; i++) {
+ System.out.format("monitor %d: %s%n", i, monitors[i]);
+ }
+ }
+ } else {
+ for (StackFrame f : frames) {
+ if (liveStackFrameClass.isInstance(f))
+ throw new RuntimeException("should not be LiveStackFrame");
+ }
+ }
+ }
+
+ String type(Object o) throws Exception {
+ if (primitiveValueClass.isInstance(o)) {
+ char c = (char)primitiveType.invoke(o);
+ return String.valueOf(c);
+ } else {
+ return o.getClass().getName();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/MultiThreadStackWalk.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.lang.StackWalker.StackFrame;
+import static java.lang.StackWalker.Option.*;
+
+
+/**
+ * @test
+ * @bug 8140450
+ * @summary This test will walk the stack using different methods, called
+ * from several threads running concurrently.
+ * Except in the case of MTSTACKSTREAM - which takes a snapshot
+ * of the stack before walking, all the methods only allow to
+ * walk the current thread stack.
+ * @run main/othervm MultiThreadStackWalk
+ * @author danielfuchs
+ */
+public class MultiThreadStackWalk {
+
+ static Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
+ "sun.reflect.NativeMethodAccessorImpl",
+ "sun.reflect.DelegatingMethodAccessorImpl",
+ "java.lang.reflect.Method",
+ "com.sun.javatest.regtest.MainWrapper$MainThread",
+ "java.lang.Thread"
+ ));
+
+
+ static final List<Class<?>> streamPipelines = Arrays.asList(
+ classForName("java.util.stream.AbstractPipeline"),
+ classForName("java.util.stream.TerminalOp")
+ );
+
+ static Class<?> classForName(String name) {
+ try {
+ return Class.forName(name);
+ } catch (ClassNotFoundException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static boolean isStreamPipeline(Class<?> clazz) {
+ for (Class<?> c : streamPipelines) {
+ if (c.isAssignableFrom(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * An object that contains variables pertaining to the execution
+ * of the test within one thread.
+ * A small amount of those variable are shared with sub threads when
+ * the stack walk is executed in parallel - that is when spliterators
+ * obtained from trySplit are handed over to an instance of SplitThread
+ * in order to parallelize thread walking.
+ * @see WalkThread#handOff(MultiThreadStackWalk.Env, java.util.Spliterator, boolean, boolean)
+ * @see Env#split(MultiThreadStackWalk.Env)
+ */
+ public static class Env {
+ final AtomicLong frameCounter; // private: the counter for the current thread.
+ final long checkMarkAt; // constant: the point at which we expect to
+ // find the marker in consume()
+ final long max; // constant: the maximum number of recursive
+ // calls to Call.
+ final AtomicBoolean debug ; // shared: whether debug is active for the
+ // instance of Test from which this instance
+ // of Env was spawned
+ final AtomicLong markerCalled; // shared: whether the marker was reached
+ final AtomicLong maxReached; // shared: whether max was reached
+ final Set<String> unexpected; // shared: list of unexpected infrastructure
+ // classes encountered after max is reached
+
+ public Env(long total, long markAt, AtomicBoolean debug) {
+ this.debug = debug;
+ frameCounter = new AtomicLong();
+ maxReached = new AtomicLong();
+ unexpected = Collections.synchronizedSet(new TreeSet<>());
+ this.max = total+2;
+ this.checkMarkAt = total - markAt + 1;
+ this.markerCalled = new AtomicLong();
+ }
+
+ // Used when delegating part of the stack walking to a sub thread
+ // see WalkThread.handOff.
+ private Env(Env orig, long start) {
+ debug = orig.debug;
+ frameCounter = new AtomicLong(start);
+ maxReached = orig.maxReached;
+ unexpected = orig.unexpected;
+ max = orig.max;
+ checkMarkAt = orig.checkMarkAt;
+ markerCalled = orig.markerCalled;
+ }
+
+ // The stack walk consumer method, where all the checks are
+ // performed.
+ public void consume(StackFrame sfi) {
+ if (frameCounter.get() == 0 && isStreamPipeline(sfi.getDeclaringClass())) {
+ return;
+ }
+
+ final long count = frameCounter.getAndIncrement();
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Declaring class[")
+ .append(count)
+ .append("]: ")
+ .append(sfi.getDeclaringClass());
+ builder.append('\n');
+ builder.append("\t")
+ .append(sfi.getClassName())
+ .append(".")
+ .append(sfi.toStackTraceElement().getMethodName())
+ .append(sfi.toStackTraceElement().isNativeMethod()
+ ? "(native)"
+ : "(" + sfi.toStackTraceElement().getFileName()
+ +":"+sfi.toStackTraceElement().getLineNumber()+")");
+ builder.append('\n');
+ if (debug.get()) {
+ System.out.print("[debug] " + builder.toString());
+ builder.setLength(0);
+ }
+ if (count == max) {
+ maxReached.incrementAndGet();
+ }
+ if (count == checkMarkAt) {
+ if (sfi.getDeclaringClass() != MultiThreadStackWalk.Marker.class) {
+ throw new RuntimeException("Expected Marker at " + count
+ + ", found " + sfi.getDeclaringClass());
+ }
+ } else {
+ if (count <= 0 && sfi.getDeclaringClass() != MultiThreadStackWalk.Call.class) {
+ throw new RuntimeException("Expected Call at " + count
+ + ", found " + sfi.getDeclaringClass());
+ } else if (count > 0 && count < max && sfi.getDeclaringClass() != MultiThreadStackWalk.Test.class) {
+ throw new RuntimeException("Expected Test at " + count
+ + ", found " + sfi.getDeclaringClass());
+ } else if (count == max && sfi.getDeclaringClass() != MultiThreadStackWalk.class) {
+ throw new RuntimeException("Expected MultiThreadStackWalk at "
+ + count + ", found " + sfi.getDeclaringClass());
+ } else if (count == max && !sfi.toStackTraceElement().getMethodName().equals("runTest")) {
+ throw new RuntimeException("Expected runTest method at "
+ + count + ", found " + sfi.toStackTraceElement().getMethodName());
+ } else if (count == max+1) {
+ if (sfi.getDeclaringClass() != MultiThreadStackWalk.WalkThread.class) {
+ throw new RuntimeException("Expected MultiThreadStackWalk at "
+ + count + ", found " + sfi.getDeclaringClass());
+ }
+ if (count == max && !sfi.toStackTraceElement().getMethodName().equals("run")) {
+ throw new RuntimeException("Expected main method at "
+ + count + ", found " + sfi.toStackTraceElement().getMethodName());
+ }
+ } else if (count > max+1) {
+ // expect JTreg infrastructure...
+ if (!infrastructureClasses.contains(sfi.getDeclaringClass().getName())) {
+ System.err.println("**** WARNING: encountered unexpected infrastructure class at "
+ + count +": " + sfi.getDeclaringClass().getName());
+ unexpected.add(sfi.getDeclaringClass().getName());
+ }
+ }
+ }
+ if (count == 100) {
+ // Maybe we should had some kind of checking inside that lambda
+ // too. For the moment we should be satisfied if it doesn't throw
+ // any exception and doesn't make the outer walk fail...
+ StackWalker.getInstance(RETAIN_CLASS_REFERENCE).forEach(x -> {
+ StackTraceElement st = x.toStackTraceElement();
+ StringBuilder b = new StringBuilder();
+ b.append("*** inner walk: ")
+ .append(x.getClassName())
+ .append(st == null ? "- no stack trace element -" :
+ ("." + st.getMethodName()
+ + (st.isNativeMethod() ? "(native)" :
+ "(" + st.getFileName()
+ + ":" + st.getLineNumber() + ")")))
+ .append('\n');
+ if (debug.get()) {
+ System.out.print(b.toString());
+ b.setLength(0);
+ }
+ });
+ }
+ }
+ }
+
+ public interface Call {
+ enum WalkType {
+ WALKSTACK, // use Thread.walkStack
+ }
+ default WalkType getWalkType() { return WalkType.WALKSTACK;}
+ default void walk(Env env) {
+ WalkType walktype = getWalkType();
+ System.out.println("Thread "+ Thread.currentThread().getName()
+ +" starting walk with " + walktype);
+ switch(walktype) {
+ case WALKSTACK:
+ StackWalker.getInstance(RETAIN_CLASS_REFERENCE)
+ .forEach(env::consume);
+ break;
+ default:
+ throw new InternalError("Unknown walk type: " + walktype);
+ }
+ }
+ default void call(Env env, Call next, int total, int current, int markAt) {
+ if (current < total) {
+ next.call(env, next, total, current+1, markAt);
+ }
+ }
+ }
+
+ public static class Marker implements Call {
+ final WalkType walkType;
+ Marker(WalkType walkType) {
+ this.walkType = walkType;
+ }
+ @Override
+ public WalkType getWalkType() {
+ return walkType;
+ }
+
+ @Override
+ public void call(Env env, Call next, int total, int current, int markAt) {
+ env.markerCalled.incrementAndGet();
+ if (current < total) {
+ next.call(env, next, total, current+1, markAt);
+ } else {
+ next.walk(env);
+ }
+ }
+ }
+
+ public static class Test implements Call {
+ final Marker marker;
+ final WalkType walkType;
+ final AtomicBoolean debug;
+ Test(WalkType walkType) {
+ this.walkType = walkType;
+ this.marker = new Marker(walkType);
+ this.debug = new AtomicBoolean();
+ }
+ @Override
+ public WalkType getWalkType() {
+ return walkType;
+ }
+ @Override
+ public void call(Env env, Call next, int total, int current, int markAt) {
+ if (current < total) {
+ int nexti = current + 1;
+ Call nextObj = nexti==markAt ? marker : next;
+ nextObj.call(env, next, total, nexti, markAt);
+ } else {
+ walk(env);
+ }
+ }
+ }
+
+ public static Env runTest(Test test, int total, int markAt) {
+ Env env = new Env(total, markAt, test.debug);
+ test.call(env, test, total, 0, markAt);
+ return env;
+ }
+
+ public static void checkTest(Env env, Test test) {
+ String threadName = Thread.currentThread().getName();
+ System.out.println(threadName + ": Marker called: " + env.markerCalled.get());
+ System.out.println(threadName + ": Max reached: " + env.maxReached.get());
+ System.out.println(threadName + ": Frames consumed: " + env.frameCounter.get());
+ if (env.markerCalled.get() == 0) {
+ throw new RuntimeException(Thread.currentThread().getName() + ": Marker was not called.");
+ }
+ if (env.markerCalled.get() > 1) {
+ throw new RuntimeException(Thread.currentThread().getName()
+ + ": Marker was called more than once: " + env.maxReached.get());
+ }
+ if (!env.unexpected.isEmpty()) {
+ System.out.flush();
+ System.err.println("Encountered some unexpected infrastructure classes below 'main': "
+ + env.unexpected);
+ }
+ if (env.maxReached.get() == 0) {
+ throw new RuntimeException(Thread.currentThread().getName()
+ + ": max not reached");
+ }
+ if (env.maxReached.get() > 1) {
+ throw new RuntimeException(Thread.currentThread().getName()
+ + ": max was reached more than once: " + env.maxReached.get());
+ }
+ }
+
+ static class WalkThread extends Thread {
+ final static AtomicLong walkersCount = new AtomicLong();
+ Throwable failed = null;
+ final Test test;
+ public WalkThread(Test test) {
+ super("WalkThread[" + walkersCount.incrementAndGet() + ", type="
+ + test.getWalkType() + "]");
+ this.test = test;
+ }
+
+ public void run() {
+ try {
+ Env env = runTest(test, 2000, 10);
+ //waitWalkers(env);
+ checkTest(env, test);
+ } catch(Throwable t) {
+ failed = t;
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ WalkThread[] threads = new WalkThread[Call.WalkType.values().length*3];
+ Throwable failed = null;
+ for (int i=0; i<threads.length; i++) {
+ Test test = new Test(Call.WalkType.values()[i%Call.WalkType.values().length]);
+ threads[i] = new WalkThread(test);
+ }
+ for (int i=0; i<threads.length; i++) {
+ threads[i].start();
+ }
+ for (int i=0; i<threads.length; i++) {
+ threads[i].join();
+ if (failed == null) failed = threads[i].failed;
+ else if (threads[i].failed == null) {
+ failed.addSuppressed(threads[i].failed);
+ }
+ }
+ if (failed != null) {
+ throw failed;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/SanityTest.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8140450
+ * @summary Sanity test for exception cases
+ * @run testng SanityTest
+ */
+
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+
+public class SanityTest {
+ @Test
+ public static void testNPE() {
+ try {
+ StackWalker sw = StackWalker.getInstance((Set<StackWalker.Option>) null);
+ throw new RuntimeException("NPE expected");
+ } catch (NullPointerException e) {}
+
+ try {
+ StackWalker sw = StackWalker.getInstance((StackWalker.Option) null);
+ throw new RuntimeException("NPE expected");
+ } catch (NullPointerException e) {}
+ }
+
+ @Test
+ public static void testUOE() {
+ try {
+ StackWalker.getInstance().getCallerClass();
+ throw new RuntimeException("UOE expected");
+ } catch (UnsupportedOperationException expected) {}
+ }
+
+ @Test
+ public static void testInvalidEstimateDepth() {
+ try {
+ StackWalker sw = StackWalker.getInstance(Collections.emptySet(), 0);
+ throw new RuntimeException("Illegal estimateDepth should throw IAE");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ @Test
+ public static void testNullFuncation() {
+ try {
+ StackWalker.getInstance().walk(null);
+ throw new RuntimeException("NPE expected");
+ } catch (NullPointerException e) {}
+ }
+
+ @Test
+ public static void testNullConsumer() {
+ try {
+ StackWalker.getInstance().forEach(null);
+ throw new RuntimeException("NPE expected");
+ } catch (NullPointerException e) {}
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/SecurityExceptions.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test 8020968
+ * @summary Test security permission check
+ * @run main/othervm/java.security.policy=noperms.policy SecurityExceptions true
+ * @run main/othervm/java.security.policy=stackwalk.policy SecurityExceptions false
+ */
+public class SecurityExceptions {
+ public static void main(String[] args) {
+ boolean expectException = Boolean.parseBoolean(args[0]);
+
+ StackWalker sw = StackWalker.getInstance();
+
+ try {
+ sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+ if (expectException) {
+ throw new RuntimeException("Expected SecurityException, but none thrown");
+ }
+ } catch (SecurityException e) {
+ if (!expectException) {
+ System.err.println("Unexpected security exception:");
+ throw e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/StackRecorderUtil.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.StackWalker.Option;
+import java.lang.StackWalker.StackFrame;
+import java.util.*;
+
+/**
+ * Utility class for recording a stack trace for later comparison to
+ * StackWalker results.
+ *
+ * StackTraceElement comparison does not include line number, isNativeMethod
+ */
+public class StackRecorderUtil implements Iterable<StackRecorderUtil.TestFrame> {
+ private List<TestFrame> testFrames = new LinkedList();
+
+ private boolean compareClasses;
+ private boolean compareClassNames = true;
+ private boolean compareMethodNames = true;
+ private boolean compareSTEs;
+
+ public StackRecorderUtil(Set<StackWalker.Option> swOptions) {
+ compareClasses = swOptions.contains(Option.RETAIN_CLASS_REFERENCE);
+ compareSTEs = true;
+ }
+
+ /**
+ * Add a method call to this recorded stack.
+ */
+ public void add(Class declaringClass, String methodName, String fileName) {
+ testFrames.add(0, new TestFrame(declaringClass, methodName, fileName));
+ }
+
+ public int frameCount() { return testFrames.size(); }
+
+ /**
+ * Compare the given StackFrame returned from the StackWalker to the
+ * recorded frame at the given index.
+ *
+ * Tests for equality, as well as functional correctness with respect to
+ * the StackWalker's options (e.g. throws or doesn't throw exceptions)
+ */
+ public void compareFrame(int index, StackFrame sf) {
+ TestFrame tf = testFrames.get(index);
+ if (compareClasses) {
+ if (!tf.declaringClass.equals(sf.getDeclaringClass())) {
+ throw new RuntimeException("Expected class: " +
+ tf.declaringClass.toString() + ", but got: " +
+ sf.getDeclaringClass().toString());
+ }
+ } else {
+ boolean caught = false;
+ try {
+ sf.getDeclaringClass();
+ } catch (UnsupportedOperationException e) {
+ caught = true;
+ }
+ if (!caught) {
+ throw new RuntimeException("StackWalker did not have " +
+ "RETAIN_CLASS_REFERENCE Option, but did not throw " +
+ "UnsupportedOperationException");
+ }
+ }
+
+ if (compareClassNames && !tf.className().equals(sf.getClassName())) {
+ throw new RuntimeException("Expected class name: " + tf.className() +
+ ", but got: " + sf.getClassName());
+ }
+ if (compareMethodNames && !tf.methodName.equals(sf.getMethodName())) {
+ throw new RuntimeException("Expected method name: " + tf.methodName +
+ ", but got: " + sf.getMethodName());
+ }
+ if (compareSTEs) {
+ StackTraceElement ste = sf.toStackTraceElement();
+ if (!(ste.getClassName().equals(tf.className()) &&
+ ste.getMethodName().equals(tf.methodName)) &&
+ ste.getFileName().equals(tf.fileName)) {
+ throw new RuntimeException("Expected StackTraceElement info: " +
+ tf + ", but got: " + ste);
+ }
+ if (!Objects.equals(ste.getClassName(), sf.getClassName())
+ || !Objects.equals(ste.getMethodName(), sf.getMethodName())
+ || !Objects.equals(ste.getFileName(), sf.getFileName().orElse(null))
+ || !Objects.equals(ste.getLineNumber(), sf.getLineNumber().orElse(-1))
+ || !Objects.equals(ste.isNativeMethod(), sf.isNativeMethod())) {
+ throw new RuntimeException("StackFrame and StackTraceElement differ: " +
+ "sf=" + sf + ", ste=" + ste);
+ }
+ }
+ }
+
+ public Iterator<TestFrame> iterator() {
+ return testFrames.iterator();
+ }
+
+ /**
+ * Class used to record stack frame information.
+ */
+ public static class TestFrame {
+ public Class declaringClass;
+ public String methodName;
+ public String fileName = null;
+
+ public TestFrame (Class declaringClass, String methodName, String fileName) {
+ this.declaringClass = declaringClass;
+ this.methodName = methodName;
+ this.fileName = fileName;
+ }
+ public String className() {
+ return declaringClass.getName();
+ }
+ public String toString() {
+ return "TestFrame: " + className() + "." + methodName +
+ (fileName == null ? "" : "(" + fileName + ")");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/StackStreamState.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8020968
+ * @summary Basic test for Stream<StackFrame> state
+ * @run main StackStreamState
+ */
+
+import java.lang.StackWalker.StackFrame;
+import java.util.stream.Stream;
+
+public class StackStreamState {
+ public static void main(String... args) {
+ StackStreamState test = new StackStreamState();
+ test.testStatic();
+ test.testInstance();
+ test.testLocal();
+ }
+
+ private static Stream<StackFrame> staticStream;
+ private Stream<StackFrame> instanceStream;
+ private final StackWalker walker = StackWalker.getInstance();
+ void testStatic() {
+ walker.walk(s -> {
+ staticStream = s;
+ return null;
+ });
+ checkStreamState(staticStream);
+ }
+ void testInstance() {
+ walker.walk(s -> {
+ instanceStream = s;
+ return null;
+ });
+ checkStreamState(instanceStream);
+ }
+ void testLocal() {
+ Stream<StackFrame> stream = walker.walk(s -> {
+ return s;
+ });
+ checkStreamState(stream);
+ }
+ void checkStreamState(Stream<StackFrame> stream) {
+ try {
+ stream.count();
+ throw new RuntimeException("IllegalStateException not thrown");
+ } catch (IllegalStateException e) {
+ System.out.println("Got expected IllegalStateException: " + e.getMessage());
+ e.printStackTrace(System.out);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/StackStreamTest.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static java.lang.StackWalker.Option.*;
+import java.lang.StackWalker.StackFrame;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ * @test
+ * @bug 8140450
+ * @summary Stack Stream Test
+ * @run main/othervm StackStreamTest
+ */
+public class StackStreamTest {
+ public static void main(String[] argv) throws Exception {
+ new StackStreamTest().test();
+ }
+
+ private static Logger logger = Logger.getLogger("stackstream");
+ public StackStreamTest() {
+ }
+
+ public void test() {
+ A.a();
+ }
+ static class A {
+ public static void a() {
+ B.b();
+ }
+ }
+ static class B {
+ public static void b() {
+ C.c();
+ }
+ }
+ static class C {
+ public static void c() {
+ D.d();
+ }
+ }
+ static class D {
+ public static void d() {
+ E.e();
+ }
+ }
+ static class E {
+ public static void e() {
+ F.f();
+ }
+ }
+ static class F {
+ public static void f() {
+ logger.severe("log message");
+ G.g();
+ new K().k();
+ }
+ }
+
+ private static boolean isTestClass(StackFrame f) {
+ // Filter jtreg frames from the end of the stack
+ return f.getClassName().startsWith("StackStreamTest");
+ }
+
+ static class G {
+ static StackWalker STE_WALKER = StackWalker.getInstance();
+ static StackWalker DEFAULT_WALKER = StackWalker.getInstance();
+
+ private static final List<String> GOLDEN_CLASS_NAMES =
+ Arrays.asList("StackStreamTest$G",
+ "StackStreamTest$F",
+ "StackStreamTest$E",
+ "StackStreamTest$D",
+ "StackStreamTest$C",
+ "StackStreamTest$B",
+ "StackStreamTest$A",
+ "StackStreamTest",
+ "StackStreamTest");
+ private static final List<String> GOLDEN_METHOD_NAMES =
+ Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main");
+
+
+ public static void g() {
+
+ System.out.println("Thread dump");
+ Thread.dumpStack();
+
+ caller();
+ firstFrame();
+
+ // Check class names
+ System.out.println("check class names");
+ List<String> sfs = DEFAULT_WALKER.walk(s -> {
+ return s.filter(StackStreamTest::isTestClass)
+ .map(StackFrame::getClassName)
+ .collect(Collectors.toList());
+ });
+ equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES);
+
+ // Check method names
+ System.out.println("methodNames()");
+ sfs = DEFAULT_WALKER.walk(s -> {
+ return s.filter(StackStreamTest::isTestClass)
+ .map(StackFrame::getMethodName)
+ .collect(Collectors.toList());}
+ );
+ equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES);
+
+ Exception exc = new Exception("G.g stack");
+ exc.printStackTrace();
+
+ System.out.println("Stream of StackTraceElement");
+ StackWalker.getInstance()
+ .walk(s ->
+ {
+ s.map(StackFrame::toStackTraceElement)
+ .forEach(ste -> System.out.println("STE: " + ste));
+ return null;
+ });
+
+ // Do we need this?
+ System.out.println("Collect StackTraceElement");
+ List<StackTraceElement> stacktrace = STE_WALKER.walk(s ->
+ {
+ // Filter out jtreg frames
+ return s.filter(StackStreamTest::isTestClass)
+ .collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList()));
+ });
+ int i=0;
+ for (StackTraceElement s : stacktrace) {
+ System.out.format(" %d: %s%n", i++, s);
+ }
+
+ // Check STEs for correctness
+ checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
+ }
+
+ static void checkStackTraceElements(List<String> classNames,
+ List<String> methodNames,
+ List<StackTraceElement> stes) {
+ if (classNames.size() != methodNames.size() ) {
+ throw new RuntimeException("Test error: classNames and methodNames should be same size");
+ }
+ if (classNames.size() != stes.size()) {
+ dumpSTEInfo(classNames, methodNames, stes);
+ throw new RuntimeException("wrong number of elements in stes");
+ }
+ for (int i = 0; i < classNames.size() ; i++) {
+ if (!classNames.get(i).equals(stes.get(i).getClassName()) ||
+ !methodNames.get(i).equals(stes.get(i).getMethodName())) {
+ dumpSTEInfo(classNames, methodNames, stes);
+ throw new RuntimeException("class & method names don't match");
+ }
+ }
+ }
+
+ static void dumpSTEInfo(List<String> classNames, List<String> methodNames,
+ List<StackTraceElement> stes) {
+ System.out.println("Observed class, method names:");
+ for (StackTraceElement ste : stes) {
+ System.out.println(" " + ste.getClassName() + ", " + ste.getMethodName());
+ }
+ System.out.println("Expected class, method names:");
+ for (int i = 0; i < classNames.size(); i++) {
+ System.out.println(" " + classNames.get(i) + ", " + methodNames.get(i));
+ }
+ }
+
+ static void firstFrame() {
+ System.out.println("first frame()");
+ StackWalker sw = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
+ sw.forEach(e -> {
+ System.out.println(e.getClassName() + "," + e.getMethodName());
+ });
+ System.out.println("\n");
+ Optional<StackFrame> frame = sw.walk(s ->
+ {
+ return s.filter(e -> {
+ System.err.println(e.getClassName() + " == " +
+ e.getClassName().equals("StackStreamTest"));
+ return e.getClassName().equals("StackStreamTest");
+ }).findFirst();
+ });
+ Class<?> c = frame.get().getDeclaringClass();
+ System.out.println("\nfirst frame: " + c);
+ if (c != StackStreamTest.class) {
+ throw new RuntimeException("Unexpected first caller class " + c);
+ }
+ }
+ }
+
+ private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) {
+ System.out.println("List: " + list);
+ System.out.println("Expectd: " + list);
+ if (!list.equals(expected)) {
+ System.err.println("Observed " + label);
+ for (T s1 : list) {
+ System.out.println(" " + s1);
+ }
+ System.err.println("Expected " + label);
+ for (T s2 : expected) {
+ System.out.println(" " + s2);
+ }
+ throw new RuntimeException("Error with " + label);
+ }
+ }
+
+
+ static class K {
+ void k() {
+ k1();
+ }
+ void k1() {
+ k2();
+ }
+ void k2() {
+ k3();
+ }
+ void k3() {
+ k4();
+ }
+ void k4() {
+ k5();
+ }
+ void k5() {
+ k6();
+ }
+ void k6() {
+ k7();
+ }
+ void k7() {
+ k8();
+ }
+ void k8() {
+ k9();
+ }
+ void k9() {
+ k10();
+ }
+ void k10() {
+ k20();
+ }
+ void k20() {
+ new Caller().test();
+ }
+
+ class Caller {
+ void test() {
+ Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
+ System.out.println("\nTesting K class : " + c);
+ Thread.dumpStack();
+ if (c != K.class) {
+ throw new RuntimeException("Unexpected caller class "+ c);
+ }
+ }
+ }
+ }
+
+ static void caller() {
+ Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
+ System.out.println("\ncaller class : " + c);
+ if (c != G.class) {
+ throw new RuntimeException("Unexpected caller class "+ c);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/StackWalkTest.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static java.lang.StackWalker.Option.*;
+import java.lang.StackWalker.StackFrame;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeSet;
+
+import jdk.testlibrary.RandomFactory;
+
+/**
+ * @test
+ * @bug 8140450
+ * @summary Stack Walk Test (use -Dseed=X to set PRNG seed)
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.*
+ * @compile StackRecorderUtil.java
+ * @run main/othervm StackWalkTest
+ * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest
+ * @run main/othervm StackWalkTest -random:50
+ * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest -random:50
+ * @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50
+ * @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=true StackWalkTest -random:50
+ * @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50
+ * @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=true StackWalkTest -random:50
+ * @author danielfuchs, bchristi
+ * @key randomness
+ */
+public class StackWalkTest {
+ private static boolean random = false;
+ private static boolean verbose = false;
+ private static int randomRuns = 50;
+
+ private static final int MAX_RANDOM_DEPTH = 1000;
+
+ static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
+ "sun.reflect.NativeMethodAccessorImpl",
+ "sun.reflect.DelegatingMethodAccessorImpl",
+ "java.lang.reflect.Method",
+ "com.sun.javatest.regtest.MainWrapper$MainThread",
+ "com.sun.javatest.regtest.agent.MainWrapper$MainThread",
+ "java.lang.Thread"
+ ));
+ static final List<Class<?>> streamPipelines = Arrays.asList(
+ classForName("java.util.stream.AbstractPipeline"),
+ classForName("java.util.stream.TerminalOp")
+ );
+ static Class<?> classForName(String name) {
+ try {
+ return Class.forName(name);
+ } catch (ClassNotFoundException e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static boolean isStreamPipeline(Class<?> clazz) {
+ for (Class<?> c : streamPipelines) {
+ if (c.isAssignableFrom(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ StackRecorderUtil recorder;
+ int count = 0;
+ boolean didWalk = false;
+
+ final int estDepth;
+ final Set<StackWalker.Option> swOptions;
+
+ public StackWalkTest() {
+ this(EnumSet.noneOf(StackWalker.Option.class), -1);
+ }
+
+ public StackWalkTest(Set<StackWalker.Option> swOptions) {
+ this(swOptions, -1);
+ }
+
+ public StackWalkTest(int estimatedDepth) {
+ this(EnumSet.noneOf(StackWalker.Option.class), -1);
+ }
+
+ public StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth) {
+ this.swOptions = swOptions;
+ this.estDepth = estimatedDepth;
+ }
+
+ private StackWalker createStackWalker() {
+ // test all StackWalker factory methods
+ if (this.estDepth < 0) {
+ if (swOptions.isEmpty()) {
+ return StackWalker.getInstance();
+ } else {
+ return StackWalker.getInstance(swOptions);
+ }
+ }
+ return StackWalker.getInstance(swOptions, estDepth);
+ }
+ public void consume(StackFrame sf) {
+ if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE)
+ && isStreamPipeline(sf.getDeclaringClass())) {
+ return;
+ }
+ if (verbose) {
+ System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName());
+ }
+ if (count >= recorder.frameCount()) {
+ // We've gone past main()...
+ if (infrastructureClasses.contains(sf.getClassName())) {
+ // safe to ignore
+ return;
+ }
+ }
+ try {
+ recorder.compareFrame(count, sf);
+ } catch (IndexOutOfBoundsException e) {
+ // Extra non-infra frame in stream
+ throw new RuntimeException("extra non-infra stack frame at count "
+ + count + ": <" + sf + ">", e);
+ }
+ count++;
+ }
+
+ public class Call {
+ public void walk(int total, int markAt) {
+ recorder.add(Call.class, "walk", "StackWalkTest.java");
+ long swFrameCount = createStackWalker().walk(s -> s.count());
+
+ if (verbose) {
+ System.out.println("Call.walk() total=" + total + ", markAt=" + markAt);
+ System.out.println("recorder frames:");
+ for (StackRecorderUtil.TestFrame f : recorder) {
+ System.out.println("\t" + f.declaringClass + "." + f.methodName);
+ }
+ System.out.println("\nStackWalker recorded " + swFrameCount + " frames");
+ System.out.flush();
+ }
+ long recFrameCount = (long)recorder.frameCount();
+ if (swFrameCount < recFrameCount) {
+ throw new RuntimeException("StackWalker recorded fewer frames ("+
+ swFrameCount + ") than recorded ("+ recorder.frameCount() +
+ ") - " + "estimatedDepth set to " + estDepth);
+ }
+ if (verbose) {
+ System.out.println("StackWalker frames:");
+ }
+ createStackWalker().forEach(StackWalkTest.this::consume);
+ didWalk = true;
+ }
+ public void call(int total, int current, int markAt) {
+ recorder.add(Call.class, "call", "StackWalkTest.java");
+ if (current < total) {
+ testCall.call(total, current+1, markAt);
+ } else {
+ walk(total, markAt);
+ }
+ }
+ }
+
+ public class Marker extends Call {
+ @Override
+ public void call(int total, int current, int markAt) {
+ recorder.add(Marker.class, "call", "StackWalkTest.java");
+ if (current < total) {
+ testCall.call(total, current+1, markAt);
+ } else {
+ walk(total, markAt);
+ }
+ }
+ }
+ private Call markerCall = new Marker();
+
+ public class Test extends Call {
+ @Override
+ public void call(int total, int current, int markAt) {
+ recorder.add(Test.class, "call", "StackWalkTest.java");
+ if (current < total) {
+ int nexti = current + 1;
+ if (nexti==markAt) {
+ markerCall.call(total, nexti, markAt);
+ } else {
+ testCall.call2(total, nexti, markAt);
+ }
+ } else {
+ walk(total, markAt);
+ }
+ }
+ public void call2(int total, int current, int markAt) {
+ recorder.add(Test.class, "call2", "StackWalkTest.java");
+ if (current < total) {
+ int nexti = current + 1;
+ if (nexti==markAt) {
+ markerCall.call(total, nexti, markAt);
+ } else {
+ test2Call.call(total, nexti, markAt);
+ }
+ } else {
+ walk(total, markAt);
+ }
+ }
+ }
+ private Test testCall = new Test();
+
+ /** Inherits call() from Call */
+ public class Test2 extends Call {}
+ private Test2 test2Call = new Test2();
+
+ public void runTest(Class callerClass, String callerMethod, int stackDepth,
+ int markAt) {
+ if (didWalk) {
+ throw new IllegalStateException("StackWalkTest already used");
+ }
+ assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth("
+ + stackDepth + ")";
+ System.out.print("runTest(" + swOptions
+ + "), estimatedDepth=" + estDepth);
+
+ recorder = new StackRecorderUtil(swOptions);
+ recorder.add(callerClass, callerMethod, "StackWalkTest.java");
+ recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java");
+
+ Test test1 = new Test();
+ test1.call(stackDepth, 0, markAt);
+
+ System.out.println(" finished");
+ if (!didWalk) {
+ throw new IllegalStateException("Test wasn't actually performed");
+ }
+ }
+
+ public static void main(String[] args) {
+ String rand = "-random";
+ String randItems = "-random:";
+ for(String arg : args) {
+ if (arg.startsWith(rand)) {
+ random = true;
+ try {
+ if(arg.startsWith(randItems)) {
+ randomRuns = Integer.valueOf(arg.substring(randItems.length()));
+ }
+ } catch(NumberFormatException e) {}
+ } else if("-verbose".equals(arg)) {
+ verbose = true;
+ }
+ }
+ if (random) {
+ Random rng = RandomFactory.getRandom();
+ for (int iters = 0; iters < randomRuns; iters++) {
+ Set<StackWalker.Option> opts = new HashSet<>();
+ if (rng.nextBoolean()) {
+ opts.add(RETAIN_CLASS_REFERENCE);
+ }
+
+ int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH);
+
+ StackWalkTest swt;
+ if (rng.nextBoolean() && depth > 1) {
+ // Test that specifying an estimatedDepth doesn't prevent
+ // full stack traversal
+ swt = new StackWalkTest(opts, 1+rng.nextInt(depth-1));
+ } else {
+ swt = new StackWalkTest(opts);
+ }
+
+ int markAt = rng.nextInt(depth+1);
+ System.out.print(depth + "@" + markAt + " ");
+ System.out.flush();
+ swt.runTest(StackWalkTest.class, "main", depth, markAt);
+ }
+ } else {
+ // Long stack, default maxDepth
+ StackWalkTest swt;
+ swt = new StackWalkTest();
+ swt.runTest(StackWalkTest.class, "main", 2000, 10);
+
+ // Long stack, matching maxDepth
+ swt = new StackWalkTest(2000);
+ swt.runTest(StackWalkTest.class, "main", 2000, 10);
+
+ // Long stack, maximum maxDepth
+ swt = new StackWalkTest(Integer.MAX_VALUE);
+ swt.runTest(StackWalkTest.class, "main", 2000, 10);
+
+ //
+ // Single batch
+ //
+ swt = new StackWalkTest(); // default maxDepth
+ swt.runTest(StackWalkTest.class, "main", 6, 3);
+
+ swt = new StackWalkTest(4); // maxDepth < stack
+ swt.runTest(StackWalkTest.class, "main", 6, 3);
+
+ swt = new StackWalkTest(2); // maxDepth < marker
+ swt.runTest(StackWalkTest.class, "main", 6, 4);
+
+ //
+ // 2 batches
+ //
+ swt = new StackWalkTest(); // default maxDepth
+ swt.runTest(StackWalkTest.class, "main", 24, 10);
+ swt = new StackWalkTest(18); // maxDepth < stack
+ swt.runTest(StackWalkTest.class, "main", 24, 10);
+ swt = new StackWalkTest(8); // maxDepth < marker
+ swt.runTest(StackWalkTest.class, "main", 24, 10);
+
+ //
+ // 3 batch
+ //
+ swt = new StackWalkTest(); // default maxDepth
+ swt.runTest(StackWalkTest.class, "main", 60, 20);
+ swt = new StackWalkTest(35); // maxDepth < stack
+ swt.runTest(StackWalkTest.class, "main", 60, 20);
+ swt = new StackWalkTest(8); // maxDepth < marker
+ swt.runTest(StackWalkTest.class, "main", 60, 20);
+
+ //
+ // StackWalker.Options
+ //
+ swt = new StackWalkTest();
+ swt.runTest(StackWalkTest.class, "main", 50, 10);
+
+ swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE));
+ swt.runTest(StackWalkTest.class, "main", 80, 40);
+
+ swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE), 50);
+ swt.runTest(StackWalkTest.class, "main", 2000, 1048);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/VerifyStackTrace.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.EnumSet;
+import java.util.concurrent.atomic.AtomicLong;
+import java.lang.StackWalker.StackFrame;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Objects;
+
+import static java.lang.StackWalker.Option.*;
+
+/**
+ * @test
+ * @bug 8140450
+ * @summary Verify stack trace information obtained with respect to StackWalker
+ * options, when the stack contains lambdas, method handle invoke
+ * virtual calls, and reflection.
+ * @run main/othervm -XX:-MemberNameInStackFrame VerifyStackTrace
+ * @run main/othervm -XX:+MemberNameInStackFrame VerifyStackTrace
+ * @run main/othervm/java.security.policy=stackwalk.policy VerifyStackTrace
+ * @author danielfuchs
+ */
+public class VerifyStackTrace {
+
+ static interface TestCase {
+ StackWalker walker();
+ String description();
+ String expected();
+ }
+ static final class TestCase1 implements TestCase {
+ private final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
+
+ private final String description = "StackWalker.getInstance(" +
+ "StackWalker.Option.RETAIN_CLASS_REFERENCE)";
+
+ // Note: line numbers and lambda hashes will be erased when
+ // comparing stack traces. However, the stack may change
+ // if some methods are being renamed in the code base.
+ // If the JDKcode base changes and the test fails because of that,
+ // then after validating that the actual stack trace obtained
+ // is indeed correct (no frames are skipped that shouldn't)
+ // then you can cut & paste the <-- actual --> stack printed in the
+ // test output in here:
+ private final String expected =
+ "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" +
+ "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" +
+ "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" +
+ "4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" +
+ "5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" +
+ "6: java.security.AccessController.doPrivileged(Native Method)\n" +
+ "7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" +
+ "8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n";
+
+ @Override public StackWalker walker() { return walker;}
+ @Override public String description() { return description;}
+ @Override public String expected() { return expected;}
+ }
+ static final class TestCase2 implements TestCase {
+ private final StackWalker walker = StackWalker.getInstance(
+ EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES));
+
+ private final String description = "nStackWalker.getInstance(" +
+ "StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
+ "StackWalker.Option.SHOW_REFLECT_FRAMES)";
+
+ // Note: line numbers and lambda hashes will be erased when
+ // comparing stack traces. However, the stack may change
+ // if some methods are being renamed in the code base.
+ // If the JDK code base changes and the test fails because of that,
+ // then after validating that the actual stack trace obtained
+ // is indeed correct (no frames are skipped that shouldn't)
+ // then you can cut & paste the <-- actual --> stack printed in the
+ // test output in here (don't forget the final \n):
+ private final String expected =
+ "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" +
+ "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" +
+ "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" +
+ "4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" +
+ "5: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
+ "6: sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
+ "7: sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
+ "8: java.lang.reflect.Method.invoke(Method.java:520)\n" +
+ "9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
+ "10: java.security.AccessController.doPrivileged(Native Method)\n" +
+ "11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
+ "12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
+
+ @Override public StackWalker walker() { return walker;}
+ @Override public String description() { return description;}
+ @Override public String expected() { return expected;}
+ }
+ static class TestCase3 implements TestCase {
+ private final StackWalker walker = StackWalker.getInstance(
+ EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));
+
+ private final String description = "StackWalker.getInstance(" +
+ "StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
+ "StackWalker.Option.SHOW_HIDDEN_FRAMES)";
+
+ // Note: line numbers and lambda hashes will be erased when
+ // comparing stack traces. However, the stack may change
+ // if some methods are being renamed in the code base.
+ // If the JDK code base changes and the test fails because of that,
+ // then after validating that the actual stack trace obtained
+ // is indeed correct (no frames are skipped that shouldn't)
+ // then you can cut & paste the <-- actual --> stack printed in the
+ // test output in here (don't forget the final \n):
+ private final String expected =
+ "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" +
+ "2: VerifyStackTrace$$Lambda$1/662441761.run(Unknown Source)\n" +
+ "3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" +
+ "4: java.lang.invoke.LambdaForm$DMH/2008017533.invokeVirtual_LL_V(LambdaForm$DMH)\n" +
+ "5: java.lang.invoke.LambdaForm$MH/1395089624.invoke_MT(LambdaForm$MH)\n" +
+ "6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" +
+ "7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" +
+ "8: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
+ "9: sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
+ "10: sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
+ "11: java.lang.reflect.Method.invoke(Method.java:520)\n" +
+ "12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" +
+ "13: java.security.AccessController.doPrivileged(Native Method)\n" +
+ "14: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" +
+ "15: VerifyStackTrace.main(VerifyStackTrace.java:188)\n";
+
+ @Override public StackWalker walker() { return walker;}
+ @Override public String description() { return description;}
+ @Override public String expected() { return expected;}
+ }
+
+ static final class TestCase4 extends TestCase3 {
+ private final StackWalker walker = StackWalker.getInstance(
+ EnumSet.allOf(StackWalker.Option.class));
+
+ private final String description = "StackWalker.getInstance(" +
+ "StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
+ "StackWalker.Option.SHOW_HIDDEN_FRAMES, " +
+ "StackWalker.Option.SHOW_REFLECT_FRAMES)";
+
+ @Override public StackWalker walker() {return walker;}
+ @Override public String description() {return description;}
+ }
+
+ public static class Handle implements Runnable {
+
+ Runnable impl;
+ public Handle(Runnable run) {
+ this.impl = run;
+ }
+
+ public void execute(Runnable run) {
+ run.run();
+ }
+
+ public void run() {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodHandle handle = null;
+ try {
+ handle = lookup.findVirtual(Handle.class, "execute",
+ MethodType.methodType(void.class, Runnable.class));
+ } catch(NoSuchMethodException | IllegalAccessException x) {
+ throw new RuntimeException(x);
+ }
+ try {
+ handle.invoke(this, impl);
+ } catch(Error | RuntimeException x) {
+ throw x;
+ } catch(Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ static String prepare(String produced, boolean eraseSensitiveInfo) {
+ if (eraseSensitiveInfo) {
+ // Erase sensitive information before comparing:
+ // comparing line numbers is too fragile, so we just erase them
+ // out before comparing. We also erase the hash-like names of
+ // synthetic frames introduced by lambdas & method handles
+ return produced.replaceAll(":[1-9][0-9]*\\)", ":00)")
+ .replaceAll("/[0-9]+\\.run", "/xxxxxxxx.run")
+ .replaceAll("/[0-9]+\\.invoke", "/xxxxxxxx.invoke")
+ .replaceAll("\\$[0-9]+", "\\$??");
+ } else {
+ return produced;
+ }
+ }
+
+
+ public static void main(String[] args) {
+ test(new TestCase1());
+ test(new TestCase2());
+ test(new TestCase3());
+ test(new TestCase4());
+ }
+
+ public static void invoke(Runnable run) {
+ run.run();
+ }
+
+ static final class Recorder {
+ boolean found; // stop recording after main
+ public void recordSTE(long counter, StringBuilder s, StackFrame f) {
+ if (found) return;
+ found = VerifyStackTrace.class.equals(f.getDeclaringClass()) &&
+ "main".equals(f.getMethodName());
+ String line = String.format("%d: %s", counter, f.toStackTraceElement());
+ s.append(line).append('\n');
+ System.out.println(line);
+ }
+ }
+
+
+ static void test(TestCase test) {
+ System.out.println("\nTesting: " + test.description());
+ final AtomicLong counter = new AtomicLong();
+ final StringBuilder builder = new StringBuilder();
+ final Recorder recorder = new Recorder();
+ final Runnable run = () -> test.walker().forEach(
+ f -> recorder.recordSTE(counter.incrementAndGet(), builder, f));
+ final Handle handle = new Handle(run);
+
+ // We're not using lambda on purpose here. We want the anonymous
+ // class on the stack.
+ PrivilegedAction<Object> pa = new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ try {
+ return VerifyStackTrace.class
+ .getMethod("invoke", Runnable.class)
+ .invoke(null, handle);
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | InvocationTargetException ex) {
+ System.out.flush();
+ throw new RuntimeException(ex);
+ }
+ }
+ };
+ AccessController.doPrivileged(pa);
+ System.out.println("Main found: " + recorder.found);
+ if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) {
+ System.out.flush();
+ try {
+ // sleep to make it less likely that System.out & System.err will
+ // interleave.
+ Thread.sleep(1000);
+ } catch (InterruptedException ex) {
+ }
+ System.err.println("\nUnexpected stack trace: "
+ + "\n<!-- expected -->\n"
+ + prepare(test.expected(), true)
+ + "\n<-- actual -->\n"
+ + prepare(builder.toString(), false));
+ throw new RuntimeException("Unexpected stack trace for: " + test.description());
+ }
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/WalkFunction.java Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8020968
+ * @summary Sanity test for Function wildcard signature
+ * @run main WalkFunction
+ */
+
+import java.lang.StackWalker.StackFrame;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class WalkFunction {
+ private static final StackWalker walker = StackWalker.getInstance();
+
+ public static void main(String... args) throws Exception {
+ testFunctions();
+ testWildcards();
+ walker.walk(counter());
+ walker.walk(wildCounter());
+ }
+
+ private static void testFunctions() {
+ walker.walk(Stream::count);
+
+ try {
+ walker.walk(null);
+ throw new RuntimeException("NPE expected");
+ } catch (NullPointerException e) {}
+
+ Optional<StackFrame> result = walker.walk(WalkFunction::reduce);
+ if (!result.get().getClassName().equals(WalkFunction.class.getName())) {
+ throw new RuntimeException(result.get() + " expected: " + WalkFunction.class.getName());
+ }
+ }
+
+ static Optional<StackFrame> reduce(Stream<StackFrame> stream) {
+ return stream.reduce((r,f) -> r.getClassName().compareTo(f.getClassName()) > 0 ? f : r);
+ }
+
+ private static void testWildcards() {
+ Function<? super Stream<? extends StackFrame>, Void> f1 = WalkFunction::function;
+ Function<? super Stream<? super StackFrame>, Void> f2 = WalkFunction::function;
+ Function<? super Stream<StackFrame>, Void> f3 = WalkFunction::function;
+ Function<Stream<? extends StackFrame>, Void> f4 = WalkFunction::function;
+ Function<Stream<? super StackFrame>, Void> f5 = WalkFunction::function;
+ Function<Stream<StackFrame>, Void> f6 = WalkFunction::function;
+ walker.walk(f1);
+ walker.walk(f2);
+ walker.walk(f3);
+ walker.walk(f4);
+ walker.walk(f5);
+ walker.walk(f6);
+ }
+
+ private static Void function(Stream<?> s) {
+ return null;
+ }
+
+ private static Function<Stream<?>, Long> wildCounter() {
+ return Stream::count;
+ }
+ private static <T> Function<Stream<T>, Long> counter() {
+ return Stream::count;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/noperms.policy Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,5 @@
+/*
+ * grant nothing
+ */
+grant {};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/stackwalk.policy Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,4 @@
+grant {
+ permission java.lang.StackFramePermission "retainClassReference";
+};
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/stackwalktest.policy Tue Nov 24 15:05:58 2015 -0800
@@ -0,0 +1,5 @@
+grant {
+ permission java.lang.StackFramePermission "retainClassReference";
+ permission java.util.PropertyPermission "seed", "read";
+};
+