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