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