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     */
013    package gnu.classpath;
014    
015    /** This is a cheap stack browser.  Better would be something like
016     * the Jikes RVM {@link StackBrowser} class.
017     * <p>
018     * This is our interface to GNU Classpath.  We quote the official
019     * Classpath Javadoc here, as part of clearly describing the interface.
020     * Never the less, look at the source code of the GNU Class
021     * (classpath/vm/reference/gnu/classpath/VMStackWalker.java) for the latest
022     * description of what these methods should do.
023     */
024    
025    import org.jikesrvm.VM;
026    import org.jikesrvm.runtime.StackBrowser;
027    import org.jikesrvm.runtime.Entrypoints;
028    
029    import org.jikesrvm.classloader.RVMType;
030    
031    
032    public final class VMStackWalker {
033    
034      /**
035       * Walk up the stack and return the first non-{@code null} class loader.
036       * If there aren't any non-{@code null} class loaders on the stack, return
037       * {@code null}.
038       *
039       * @return the first non-{@code null} classloader on stack or {@code null}
040       */
041      public static ClassLoader firstNonNullClassLoader() {
042        for (Class<?> type : getClassContext()) {
043          ClassLoader loader = type.getClassLoader();
044          if (loader != null)
045            return loader;
046        }
047        return null;
048      }
049    
050      /**
051       * Classpath's Javadoc for this method says:
052       * <blockquote>
053       * Get a list of all the classes currently executing methods on the
054       * Java stack. <code>getClassContext()[0]</code> is the class associated
055       * with the currently executing method, i.e., the method that called
056       * <code>VMStackWalker.getClassContext()</code> (possibly through
057       * reflection). So you may need to pop off these stack frames from
058       * the top of the stack:
059       * <ul>
060       * <li><code>VMStackWalker.getClassContext()</code>
061       * <li><code>Method.invoke()</code>
062       * </ul>
063       *
064       * @return an array of the declaring classes of each stack frame
065       * </blockquote>
066       */
067      public static Class<?>[] getClassContext() {
068        StackBrowser b = new StackBrowser();
069        int frames = 0;
070        VM.disableGC();
071    
072        b.init();
073        b.up(); // skip VMStackWalker.getClassContext (this call)
074    
075        boolean reflected;  // Were we invoked by reflection?
076        if (b.getMethod() == Entrypoints.java_lang_reflect_Method_invokeMethod){
077          reflected = true;
078          b.up();         // Skip Method.invoke, (if we were called by reflection)
079        } else {
080          reflected = false;
081        }
082    
083        /* Count # of frames. */
084        while(b.hasMoreFrames()) {
085          frames++;
086          b.up();
087        }
088    
089        VM.enableGC();
090    
091    
092        RVMType[] iclasses = new RVMType[ frames ];
093    
094        int i = 0;
095        b = new StackBrowser();
096    
097        VM.disableGC();
098        b.init();
099        b.up(); // skip this method
100        if (reflected)
101          b.up();            // Skip Method.invoke if we were called by reflection
102    
103        while(b.hasMoreFrames()) {
104          iclasses[i++] = b.getCurrentClass();
105          b.up();
106        }
107        VM.enableGC();
108    
109        Class<?>[] classes = new Class[ frames ];
110        for(int j = 0; j < iclasses.length; j++) {
111          classes[j] = iclasses[j].getClassForType();
112        }
113    
114        return classes;
115      }
116    
117      /**
118       * Classpath's Javadoc for this method is:
119       * <blockquote>
120       * Get the class associated with the method invoking the method
121       * invoking this method, or <code>null</code> if the stack is not
122       * that deep (e.g., invoked via JNI invocation API). This method
123       * is an optimization for the expression <code>getClassContext()[1]</code>
124       * and should return the same result.
125       *
126       * <p>
127       * VM implementers are encouraged to provide a more efficient
128       * version of this method.
129       * </blockquote>
130       */
131      public static Class<?> getCallingClass() {
132        return getCallingClass(1);  // Skip this method (getCallingClass())
133      }
134    
135      public static Class<?> getCallingClass(int skip) {
136        StackBrowser b = new StackBrowser();
137        VM.disableGC();
138    
139        b.init();
140        b.up(); // skip VMStackWalker.getCallingClass(int) (this call)
141        while (skip-- > 0)          // Skip what the caller asked for.
142          b.up();
143    
144        /* Skip Method.invoke, (if the caller was called by reflection) */
145        if (b.getMethod() == Entrypoints.java_lang_reflect_Method_invokeMethod){
146          b.up();
147        }
148        /* skip past another frame, whatever getClassContext()[0] would be. */
149        if (!b.hasMoreFrames())
150          return null;
151        b.up();
152    
153        /* OK, we're there at getClassContext()[1] now.  Return it. */
154        RVMType ret = b.getCurrentClass();
155        VM.enableGC();
156    
157        return ret.getClassForType();
158      }
159    
160      /**
161       * Classpath's Javadoc for this method is:
162       * <blockquote>
163       * Get the class loader associated with the Class returned by
164       * <code>getCallingClass()</code>, or <code>null</code> if no
165       * such class exists or it is the boot loader. This method is an optimization
166       * for the expression <code>getClassContext()[1].getClassLoader()</code>
167       * and should return the same result.
168       * </blockquote>
169       */
170      public static ClassLoader getCallingClassLoader() {
171        Class<?> caller = getCallingClass(1); // skip getCallingClassLoader
172        if (caller == null)
173          return null;
174        return caller.getClassLoader();
175      }
176    }
177