• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8140450 8173898
27  * @summary Basic test for the StackWalker::walk method
28  * @run testng Basic
29  */
30 
31 
32 package test.java.lang.StackWalker;
33 
34 import java.lang.StackWalker.StackFrame;
35 import java.lang.invoke.MethodType;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.stream.Collectors;
39 import java.util.stream.Stream;
40 import static java.lang.StackWalker.Option.*;
41 
42 import org.testng.annotations.DataProvider;
43 import org.testng.annotations.Test;
44 import static org.testng.Assert.*;
45 
46 public class Basic {
47     private static boolean verbose = false;
48 
49     @DataProvider(name = "stackDepths")
stackDepths()50     public static Object[][] stackDepths() {
51         return new Object[][] {
52                 { new int[] { 12 },  new int[] { 4, 8, 12}      },
53                 { new int[] { 18 },  new int[] { 8, 16, 20}     },
54                 { new int[] { 32 },  new int[] { 16, 32, 64}    },
55         };
56     }
57 
58     /**
59      * For a stack of a given depth, it creates a StackWalker with an estimate.
60      * Test walking different number of frames
61      */
62     @Test(dataProvider = "stackDepths")
test(int[] depth, int[] estimates)63     public static void test(int[] depth, int[] estimates) {
64         Basic test = new Basic(depth[0]);
65         for (int estimate : estimates) {
66             test.walk(estimate);
67         }
68     }
69 
70     @Test
testWalkFromConstructor()71     public static void testWalkFromConstructor() throws Exception {
72         System.out.println("testWalkFromConstructor:");
73         List<String> found = ((ConstructorNewInstance)ConstructorNewInstance.class.getMethod("create")
74                              .invoke(null)).collectedFrames();
75         assertEquals(List.of(ConstructorNewInstance.class.getName()+"::<init>",
76                              ConstructorNewInstance.class.getName()+"::create",
77                              Basic.class.getName()+"::testWalkFromConstructor"),
78                      found);
79     }
80 
81     @Test
testMethodSignature()82     public static void testMethodSignature() throws Exception {
83         List<StackFrame> frames = new StackBuilder(16, 16).build();
84         Map<String, MethodType> methodTypes = StackBuilder.methodTypes();
85         for (StackFrame f : frames) {
86             MethodType type = methodTypes.get(f.getMethodName());
87             if (type != null) {
88                 System.out.format("%s.%s %s%n", f.getClassName(), f.getMethodName(),
89                                   f.getDescriptor());
90 
91                 String descriptor = f.getDescriptor();
92                 if (!descriptor.equals(type.toMethodDescriptorString())) {
93                     throw new RuntimeException("Expected: " + type.toMethodDescriptorString()
94                         + " got: " + f.getDescriptor());
95                 }
96 
97                 if (!f.getMethodType().equals(type)) {
98                     throw new RuntimeException("Expected: " + type
99                         + " got: " + f.getMethodType());
100                 }
101 
102                 // verify descriptor returned by getDescriptor() before and after
103                 // getMethodType() is called
104                 if (!descriptor.equals(f.getDescriptor())) {
105                     throw new RuntimeException("Mismatched: " + descriptor
106                         + " got: " + f.getDescriptor());
107                 }
108             }
109         }
110     }
111 
112     private final int depth;
Basic(int depth)113     Basic(int depth) {
114         this.depth = depth;
115     }
116 
117     /** For TestNG */
Basic()118     public Basic() {
119         depth = 0;
120     }
121 
122     /*
123      * Setup a stack builder with the expected stack depth
124      * Walk the stack and count the frames.
125      */
walk(int estimate)126     void walk(int estimate) {
127         int limit = Math.min(depth, 16);
128         List<StackFrame> frames = new StackBuilder(depth, limit).build();
129         System.out.format("depth=%d estimate=%d expected=%d walked=%d%n",
130                           depth, estimate, limit, frames.size());
131         assertEquals(limit, frames.size());
132     }
133 
134     static class ConstructorNewInstance {
135         static final StackWalker walker =
136             StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
137         List<String> testFramesOrReflectionFrames;
ConstructorNewInstance()138         public ConstructorNewInstance() {
139             testFramesOrReflectionFrames = walker.walk(this::parse);
140         }
collectedFrames()141         public List<String> collectedFrames() {
142             return testFramesOrReflectionFrames;
143         }
accept(StackFrame f)144         public boolean accept(StackFrame f) {
145             // Frames whose class names don't contain "."
146             // are our own test frames. These are the ones
147             // we expect.
148             // Frames whose class names contain ".reflect."
149             // are reflection frames. None should be present,
150             // since they are supposed to be filtered by
151             // by StackWalker. If we find any, we want to fail.
152             // Android-changed: Unlike upstream, this test class has a package name.
153             // if (!f.getClassName().contains(".")
154             if (f.getClassName().startsWith(Basic.class.getPackageName())
155                 || f.getClassName().contains(".reflect.")) {
156                 System.out.println("    " + f);
157                 return true;
158             }
159             // Filter out all other frames (in particular
160             // those from the test framework) in order to
161             // have predictable results.
162             return false;
163         }
frame(StackFrame f)164         public String frame(StackFrame f) {
165             return f.getClassName() + "::" + f.getMethodName();
166         }
parse(Stream<StackFrame> s)167         List<String> parse(Stream<StackFrame> s) {
168             return s.filter(this::accept)
169                     .map(this::frame)
170                     .collect(Collectors.toList());
171         }
create()172         public static ConstructorNewInstance create() throws Exception {
173             return ConstructorNewInstance.class.getConstructor().newInstance();
174         }
175     }
176 
177     static class StackBuilder {
178         private final int stackDepth;
179         private final int limit;
180         private int depth = 0;
181         private List<StackFrame> result;
StackBuilder(int stackDepth, int limit)182         StackBuilder(int stackDepth, int limit) {
183             this.stackDepth = stackDepth; // build method;
184             this.limit = limit;
185         }
build()186         List<StackFrame> build() {
187             trace("build");
188             m1();
189             return result;
190         }
m1()191         void m1() {
192             trace("m1");
193             m2();
194         }
m2()195         List m2() {
196             trace("m2");
197             m3();
198             return null;
199         }
m3()200         int m3() {
201             trace("m3");
202             m4(null);
203             return 0;
204         }
m4(Object o)205         void m4(Object o) {
206             trace("m4");
207             int remaining = stackDepth-depth-1;
208             if (remaining >= 4) {
209                 m1();
210             } else {
211                 filler(remaining);
212             }
213         }
filler(int i)214         void filler(int i) {
215             trace("filler");
216             if (i == 0)
217                 walk();
218             else
219                 filler(--i);
220         }
221 
walk()222         void walk() {
223             StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
224             result = walker.walk(s -> s.limit(limit).collect(Collectors.toList()));
225         }
trace(String methodname)226         void trace(String methodname) {
227             ++depth;
228             if (verbose)
229                 System.out.format("%2d: %s%n", depth, methodname);
230         }
231 
methodTypes()232         static Map<String, MethodType> methodTypes() throws Exception {
233             return Map.of("m1", MethodType.methodType(void.class),
234                           "m2", MethodType.methodType(List.class),
235                           "m3", MethodType.methodType(int.class),
236                           "m4", MethodType.methodType(void.class, Object.class));
237         }
238     }
239 
240 }
241