1 /*
   2  * Copyright (c) 2015, 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 /**
  26  * @test
  27  * @bug 8143214
  28  * @summary Verify StackWalker works well when embedded in another
  29  *          StackWalker's functions.
  30  * @run testng/othervm EmbeddedStackWalkTest
  31  */
  32 
  33 import java.lang.StackWalker.StackFrame;
  34 import static java.lang.StackWalker.Option.*;
  35 import java.lang.invoke.MethodHandle;
  36 import java.lang.invoke.MethodHandles;
  37 import java.lang.invoke.MethodType;
  38 import java.util.Arrays;
  39 import java.util.EnumSet;
  40 
  41 import org.testng.annotations.*;
  42 import static org.testng.Assert.*;
  43 
  44 public class EmbeddedStackWalkTest {
  45     static final StackWalker WALKERS[] = new StackWalker[] {
  46             StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
  47             StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
  48             StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
  49     };
  50 
  51     static final int BIG_LOOP   = 30;
  52     static final int SMALL_LOOP = 5;
  53 
  54     @DataProvider
  55     public StackWalker[][] walkerProvider() {
  56         return new StackWalker[][] {
  57                 new StackWalker[] { WALKERS[0] },
  58                 new StackWalker[] { WALKERS[1] },
  59                 new StackWalker[] { WALKERS[2] }
  60         };
  61     }
  62 
  63     @Test(dataProvider = "walkerProvider")
  64     public void test(StackWalker walker) {
  65         C1.call(walker, BIG_LOOP);
  66     }
  67 
  68     // line numbers are hardcoded for now.
  69     // Should annotate the line numbers and auto-generated these constants
  70     // for test verification instead
  71     static final int BEGIN_LINE = 71;   // the begin line number of approximate range.
  72     static final int END_LINE   = 136;  // the end line number of approximate range.
  73     static class C1 { // here is the begin line number of approximate range, L71.
  74         public static void call(StackWalker walker, int loops) {
  75             if (loops == 0) {
  76                 String caller = walker.walk(s ->
  77                     s.map(StackFrame::getClassName)
  78                      .filter(cn -> !cn.startsWith("sun.reflect.") && !cn.startsWith("java.lang.invoke"))
  79                      .skip(2).findFirst()
  80                 ).get();
  81                 assertEquals(caller, C1.class.getName());
  82 
  83                 walker.forEach(f -> C2.testEmbeddedWalker());
  84             } else {
  85                 call(walker, --loops);
  86             }
  87         }
  88     }
  89 
  90     static class C2 {
  91         static final StackWalker embeddedWalkers[] = new StackWalker[] {
  92             StackWalker.getInstance(),
  93             StackWalker.getInstance(SHOW_REFLECT_FRAMES),
  94             StackWalker.getInstance(SHOW_HIDDEN_FRAMES)
  95         };
  96 
  97         public static void testEmbeddedWalker() {
  98             walk(SMALL_LOOP);
  99         }
 100 
 101         static void walk(int loops) {
 102             if (loops == 0) {
 103                 Arrays.stream(embeddedWalkers)
 104                       .forEach(walker -> run(walker));
 105             } else {
 106                 walk(--loops);
 107             }
 108         }
 109 
 110         static void run(StackWalker walker) {
 111             MethodHandles.Lookup lookup = MethodHandles.lookup();
 112             MethodHandle handle = null;
 113             try {
 114                 handle = lookup.findStatic(C2.class, "call",
 115                         MethodType.methodType(void.class, StackWalker.class));
 116                 handle.invoke(walker);
 117             } catch(Throwable t) {
 118                 throw new RuntimeException(t);
 119             }
 120         }
 121 
 122         static void call(StackWalker walker) {
 123             String caller = walker.walk(s ->
 124                 s.map(StackFrame::getClassName)
 125                  .filter(cn -> !cn.startsWith("sun.reflect.") && !cn.startsWith("java.lang.invoke"))
 126                  .skip(2).findFirst()
 127             ).get();
 128             assertEquals(caller, C2.class.getName());
 129 
 130             verify(walker, C1.class, "call");
 131             verify(walker, C2.class, "call");
 132             verify(walker, C2.class, "run");
 133             verify(walker, C2.class, "walk");
 134             verify(walker, C2.class, "testEmbeddedWalker");
 135         } // here is the end line number of approximate range, L136.
 136 
 137         static void verify(StackWalker walker, Class<?> c, String mn) {
 138             final String fileName = "EmbeddedStackWalkTest.java";
 139             walker.walk(s -> {
 140                 s.limit(BIG_LOOP)
 141                  .filter(f -> c.getName().equals(f.getClassName()) && mn.equals(f.getMethodName()))
 142                  .forEach(f -> {
 143                     StackTraceElement st = f.toStackTraceElement();
 144                     assertEquals(c.getName(), st.getClassName());
 145                     assertEquals(mn, st.getMethodName());
 146                 });
 147                 return null;
 148             });
 149         }
 150     }
 151 }