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