001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package gnu.classpath;
014
015import org.jikesrvm.VM;
016import org.jikesrvm.classloader.RVMType;
017import org.jikesrvm.runtime.Entrypoints;
018import org.jikesrvm.runtime.StackBrowser;
019
020/**
021 * This is a cheap stack browser.  Better would be something like
022 * the Jikes RVM {@link StackBrowser} class.
023 * <p>
024 * This is our interface to GNU Classpath.  We quote the official
025 * Classpath Javadoc here, as part of clearly describing the interface.
026 * Never the less, look at the source code of the GNU Class
027 * (classpath/vm/reference/gnu/classpath/VMStackWalker.java) for the latest
028 * description of what these methods should do. NOTE: we do not quote
029 * any JavaDoc that would cause JavaDoc warnings to be emitted in JDK8.
030 */
031public final class VMStackWalker {
032
033  /**
034   * Walk up the stack and return the first non-{@code null} class loader.
035   * If there aren't any non-{@code null} class loaders on the stack, return
036   * {@code null}.
037   *
038   * @return the first non-{@code null} classloader on stack or {@code null}
039   */
040  public static ClassLoader firstNonNullClassLoader() {
041    for (Class<?> type : getClassContext()) {
042      ClassLoader loader = type.getClassLoader();
043      if (loader != null)
044        return loader;
045    }
046    return null;
047  }
048
049  /**
050   * Classpath's Javadoc for this method says:
051   * <blockquote>
052   * Get a list of all the classes currently executing methods on the
053   * Java stack. <code>getClassContext()[0]</code> is the class associated
054   * with the currently executing method, i.e., the method that called
055   * <code>VMStackWalker.getClassContext()</code> (possibly through
056   * reflection). So you may need to pop off these stack frames from
057   * the top of the stack:
058   * <ul>
059   * <li><code>VMStackWalker.getClassContext()</code>
060   * <li><code>Method.invoke()</code>
061   * </ul>
062   *
063   * @return an array of the declaring classes of each stack frame
064   * </blockquote>
065   */
066  public static Class<?>[] getClassContext() {
067    StackBrowser b = new StackBrowser();
068    int frames = 0;
069    VM.disableGC();
070
071    b.init();
072    b.up(); // skip VMStackWalker.getClassContext (this call)
073
074    boolean reflected;  // Were we invoked by reflection?
075    if (b.getMethod() == Entrypoints.java_lang_reflect_Method_invokeMethod) {
076      reflected = true;
077      b.up();         // Skip Method.invoke, (if we were called by reflection)
078    } else {
079      reflected = false;
080    }
081
082    /* Count # of frames. */
083    while (b.hasMoreFrames()) {
084      frames++;
085      b.up();
086    }
087
088    VM.enableGC();
089
090
091    RVMType[] iclasses = new RVMType[ frames ];
092
093    int i = 0;
094    b = new StackBrowser();
095
096    VM.disableGC();
097    b.init();
098    b.up(); // skip this method
099    if (reflected)
100      b.up();            // Skip Method.invoke if we were called by reflection
101
102    while (b.hasMoreFrames()) {
103      iclasses[i++] = b.getCurrentClass();
104      b.up();
105    }
106    VM.enableGC();
107
108    Class<?>[] classes = new Class[ frames ];
109    for (int j = 0; j < iclasses.length; j++) {
110      classes[j] = iclasses[j].getClassForType();
111    }
112
113    return classes;
114  }
115
116  // JavaDoc not quoted because Classpath 0.97.2's JavaDoc is missing a @return tag
117  public static Class<?> getCallingClass() {
118    return getCallingClass(1);  // Skip this method (getCallingClass())
119  }
120
121  // JavaDoc not quoted because Classpath 0.97.2's JavaDoc is missing a @return tag
122  public static Class<?> getCallingClass(int skip) {
123    StackBrowser b = new StackBrowser();
124    VM.disableGC();
125
126    b.init();
127    b.up(); // skip VMStackWalker.getCallingClass(int) (this call)
128    while (skip-- > 0)          // Skip what the caller asked for.
129      b.up();
130
131    /* Skip Method.invoke, (if the caller was called by reflection) */
132    if (b.getMethod() == Entrypoints.java_lang_reflect_Method_invokeMethod) {
133      b.up();
134    }
135    /* skip past another frame, whatever getClassContext()[0] would be. */
136    if (!b.hasMoreFrames())
137      return null;
138    b.up();
139
140    /* OK, we're there at getClassContext()[1] now.  Return it. */
141    RVMType ret = b.getCurrentClass();
142    VM.enableGC();
143
144    return ret.getClassForType();
145  }
146
147  // JavaDoc not quoted because Classpath 0.97.2's JavaDoc is missing a @return tag
148  public static ClassLoader getCallingClassLoader() {
149    Class<?> caller = getCallingClass(1); // skip getCallingClassLoader
150    if (caller == null)
151      return null;
152    return caller.getClassLoader();
153  }
154}
155