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 org.jikesrvm.classloader;
014
015 import org.jikesrvm.VM;
016 import org.vmmagic.pragma.Pure;
017 import org.vmmagic.pragma.Uninterruptible;
018
019 /**
020 * A class to represent the reference in a class file to a method of
021 * that class or interface.
022 */
023 public final class MethodReference extends MemberReference {
024
025 /**
026 * type of return value
027 */
028 private final TypeReference returnType;
029
030 /**
031 * types of parameters (not including "this", if virtual)
032 */
033 private final TypeReference[] parameterTypes;
034
035 /**
036 * The RVMMethod that this method reference resolved to (null if not yet resolved).
037 */
038 private RVMMethod resolvedMember;
039
040 /**
041 * Find or create a method reference
042 * @see MemberReference#findOrCreate(TypeReference, Atom, Atom)
043 */
044 @Pure
045 public static MethodReference findOrCreate(TypeReference tRef, Atom mn, Atom md) {
046 return MemberReference.findOrCreate(tRef, mn, md).asMethodReference();
047 }
048
049 /**
050 * @param tr a type reference to the defining class in which this method
051 * appears. (e.g., "Ljava/lang/String;")
052 * @param mn the name of this method (e.g., "equals")
053 * @param d the method descriptor (e.g., "(Ljava/lang/Object;)Z")
054 * @param id the new ID of the member were a new member required
055 */
056 MethodReference(TypeReference tr, Atom mn, Atom d, int id) {
057 super(tr, mn, d, id);
058 ClassLoader cl = tr.getClassLoader();
059 returnType = d.parseForReturnType(cl);
060 parameterTypes = d.parseForParameterTypes(cl);
061 }
062
063 /**
064 * @return return type of the method
065 */
066 @Uninterruptible
067 public TypeReference getReturnType() {
068 return returnType;
069 }
070
071 /**
072 * @return parameter types of the method
073 */
074 @Uninterruptible
075 public TypeReference[] getParameterTypes() {
076 return parameterTypes;
077 }
078
079 /**
080 * Space required by method for its parameters, in words.
081 * Note: does *not* include implicit "this" parameter, if any.
082 */
083 @Uninterruptible
084 public int getParameterWords() {
085 int pw = 0;
086 for (TypeReference parameterType : parameterTypes) pw += parameterType.getStackWords();
087 return pw;
088 }
089
090 /**
091 * Do this and that definitely refer to the different methods?
092 */
093 public boolean definitelyDifferent(MethodReference that) {
094 if (this == that) return false;
095 if (name != that.name || descriptor != that.descriptor) return true;
096 RVMMethod mine = peekResolvedMethod();
097 RVMMethod theirs = that.peekResolvedMethod();
098 if (mine == null || theirs == null) return false;
099 return mine != theirs;
100 }
101
102 /**
103 * Do this and that definitely refer to the same method?
104 */
105 public boolean definitelySame(MethodReference that) {
106 if (this == that) return true;
107 if (name != that.name || descriptor != that.descriptor) return false;
108 RVMMethod mine = peekResolvedMethod();
109 RVMMethod theirs = that.peekResolvedMethod();
110 if (mine == null || theirs == null) return false;
111 return mine == theirs;
112 }
113
114 /**
115 * Has the method reference already been resolved into a target method?
116 */
117 public boolean isResolved() {
118 return resolvedMember != null;
119 }
120
121 /**
122 * Get the member this reference has been resolved to, if
123 * it has already been resolved. Does NOT force resolution.
124 */
125 @Uninterruptible
126 public RVMMethod getResolvedMember() {
127 return resolvedMember;
128 }
129
130 /**
131 * For use by RVMMethod constructor
132 */
133 void setResolvedMember(RVMMethod it) {
134 if (VM.VerifyAssertions) VM._assert(resolvedMember == null || resolvedMember == it);
135 resolvedMember = it;
136 }
137
138 /**
139 * Resolve the method reference for an invoke special into a target
140 * method, return null if the method cannot be resolved without classloading.
141 */
142 public synchronized RVMMethod resolveInvokeSpecial() {
143 RVMClass thisClass = (RVMClass) type.peekType();
144 if (thisClass == null && name != RVMClassLoader.StandardObjectInitializerMethodName) {
145 thisClass = (RVMClass) type.resolve();
146 /* Can't fail to resolve thisClass; we're at compile time doing
147 resolution of an invocation to a private method or super call. We
148 must have loaded the class already */
149 }
150 if (thisClass == null) {
151 return null; // can't be found now.
152 }
153 RVMMethod sought = resolveInternal(thisClass);
154
155 if (sought.isObjectInitializer()) {
156 return sought; // <init>
157 }
158
159 RVMClass cls = sought.getDeclaringClass();
160 if (!cls.isSpecial()) {
161 return sought; // old-style invokespecial semantics
162 }
163
164 for (; cls != null; cls = cls.getSuperClass()) {
165 RVMMethod found = cls.findDeclaredMethod(sought.getName(), sought.getDescriptor());
166 if (found != null) {
167 return found; // new-style invokespecial semantics
168 }
169 }
170 return null; // cannot be found
171 }
172
173 /**
174 * Find the RVMMethod that this method reference refers to using
175 * the search order specified in JVM spec 5.4.3.3.
176 * @return the RVMMethod that this method ref resolved to or null if it cannot be resolved.
177 */
178 public RVMMethod peekResolvedMethod() {
179 if (resolvedMember != null) return resolvedMember;
180
181 // Hasn't been resolved yet. Try to do it now without triggering class loading.
182 RVMType declaringClass = type.peekType();
183 if (declaringClass == null) return null;
184 return resolveInternal((RVMClass)declaringClass);
185 }
186
187 /**
188 * Find the RVMMethod that this field reference refers to using
189 * the search order specified in JVM spec 5.4.3.3.
190 * @return the RVMMethod that this method ref resolved to.
191 */
192 public synchronized RVMMethod resolve() {
193 if (resolvedMember != null) return resolvedMember;
194
195 // Hasn't been resolved yet. Do it now triggering class loading if necessary.
196 return resolveInternal((RVMClass) type.resolve());
197 }
198
199 /**
200 * Return true iff this member reference refers to a method which
201 * is declared as part of an abstract class but actually is an
202 * interface method, known formally as a "miranda method".
203 *
204 * This method is necessary to handle the special case where an
205 * invokevirtual is defined on an abstract class, where the
206 * method invocation points to a method inherited from an
207 * interface.
208 *
209 * @return boolean true iff this member method reference is a miranda method
210 */
211 public boolean isMiranda() {
212
213 // Hasn't been resolved yet. Try to do it now without triggering class loading.
214 RVMClass declaringClass = (RVMClass) type.peekType();
215 if (declaringClass == null) { return false; }
216
217 if (!declaringClass.isResolved()) {
218 declaringClass.resolve();
219 }
220
221 // See if method is explicitly declared in any superclass
222 for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
223
224 if (c.findDeclaredMethod(name, descriptor) != null) {
225 // Method declared in superclass => not interface method
226 return false;
227 }
228 }
229
230 // Not declared in any superclass; now check to see if it is coming from an interface somewhere
231 for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
232 // See if method is in any interfaces of c
233 for (RVMClass intf : c.getDeclaredInterfaces()) {
234 if (searchInterfaceMethods(intf) != null) {
235 // Found method in interface or superinterface
236 return true;
237 }
238 }
239 }
240
241 // neither in superclass or interface => not interface method
242 return false;
243 }
244
245 /**
246 * Is the method reference to a magic method? NB. In the case of
247 * SysCall annotated methods we don't know until they are resolved.
248 */
249 public boolean isMagic() {
250 return getType().isMagicType() || ((resolvedMember != null) && (resolvedMember.isSysCall() || resolvedMember.isSpecializedInvoke()));
251 }
252
253 /**
254 * Is the method reference to a specialized invoke? NB. we don't know until they are resolved.
255 */
256 public boolean isSpecializedInvoke() {
257 return (resolvedMember != null) && (resolvedMember.isSpecializedInvoke());
258 }
259
260 /**
261 * Is the method reference to a magic method? NB. In the case of
262 * SysCall annotated methods we don't know until they are resolved.
263 */
264 public boolean isSysCall() {
265 return (getType() == TypeReference.SysCall) || ((resolvedMember != null) && (resolvedMember.isSysCall()));
266 }
267
268 /**
269 * Find the RVMMethod that this member reference refers to using
270 * the search order specified in JVM spec 5.4.3.3.
271 * @return the RVMMethod that this method ref resolved to.
272 */
273 private RVMMethod resolveInternal(RVMClass declaringClass) {
274 final boolean DBG=false;
275 if (!declaringClass.isResolved()) {
276 declaringClass.resolve();
277 }
278 for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
279 if (DBG) {
280 VM.sysWrite("Checking for <" + name + "," + descriptor + "> in class " + c + "...");
281 }
282
283 RVMMethod it = c.findDeclaredMethod(name, descriptor);
284 if (it != null) {
285 if (DBG) {
286 VM.sysWriteln("...found <" + name + "," + descriptor + "> in class " + c);
287 }
288 resolvedMember = it;
289 return resolvedMember;
290 }
291 if (DBG) {
292 VM.sysWriteln("...NOT found <" + name + "," + descriptor + "> in class " + c);
293 }
294 }
295 if (!VM.fullyBooted) {
296 VM.sysWrite("MethodReference.resolveInternal():");
297 VM.sysWrite(" Unable to find a method named ");
298 name.sysWrite();
299 VM.sysWrite(" with descriptor ");
300 descriptor.sysWrite();
301 VM.sysWrite(" in the class ");
302 declaringClass.getDescriptor().sysWrite();
303 if (VM.runningVM) {
304 VM.sysWriteln(", while booting the VM");
305 VM.sysFail("MethodReference.resolveInternal(): Unable to resolve a method during VM booting");
306
307 } else {
308 VM.sysWriteln(", while writing the boot image");
309 Thread.dumpStack();
310 throw new Error("MethodReference.resolveInternal(): Unable to resolve a method during boot image writing");
311 }
312 }
313 throw new NoSuchMethodError(this.toString());
314 }
315
316 /**
317 * Find the RVMMethod that this member reference refers to using
318 * the search order specified in JVM spec 5.4.3.4.
319 * @return the RVMMethod that this method ref resolved to or null if it cannot be resolved without trigering class loading
320 */
321 public RVMMethod peekInterfaceMethod() {
322 if (resolvedMember != null) return resolvedMember;
323
324 // Hasn't been resolved yet. Try to do it now.
325 RVMClass declaringClass = (RVMClass) type.peekType();
326 if (declaringClass == null) return null;
327 if (!declaringClass.isResolved()) {
328 declaringClass.resolve();
329 }
330 if (!declaringClass.isInterface()) return null;
331 return resolveInterfaceMethodInternal(declaringClass);
332 }
333
334 /**
335 * Find the RVMMethod that this member reference refers to using
336 * the search order specified in JVM spec 5.4.3.4.
337 * @return the RVMMethod that this method ref resolved to
338 */
339 public RVMMethod resolveInterfaceMethod() throws IncompatibleClassChangeError, NoSuchMethodError {
340 if (resolvedMember != null) return resolvedMember;
341
342 // Hasn't been resolved yet. Do it now.
343 RVMClass declaringClass = (RVMClass) type.resolve();
344 if (!declaringClass.isResolved()) {
345 declaringClass.resolve();
346 }
347
348 /* Interface method may be either in interface, or a miranda.
349 */
350 if (!declaringClass.isInterface() && !isMiranda()) {
351 throw new IncompatibleClassChangeError();
352 }
353 RVMMethod ans = resolveInterfaceMethodInternal(declaringClass);
354 if (ans == null) {
355 throw new NoSuchMethodError(this.toString());
356 }
357 return ans;
358 }
359
360 /**
361 * Find the RVMMethod that this member reference refers to using
362 * the search order specified in JVM spec 5.4.3.4.
363 * @return the RVMMethod that this method ref resolved to or null for error
364 */
365 private RVMMethod resolveInterfaceMethodInternal(RVMClass declaringClass) {
366 RVMMethod it = declaringClass.findDeclaredMethod(name, descriptor);
367 if (it != null) {
368 resolvedMember = it;
369 return resolvedMember;
370 }
371 for (RVMClass intf : declaringClass.getDeclaredInterfaces()) {
372 it = searchInterfaceMethods(intf);
373 if (it != null) {
374 resolvedMember = it;
375 return resolvedMember;
376 }
377 }
378 return null;
379 }
380
381 private RVMMethod searchInterfaceMethods(RVMClass c) {
382 if (!c.isResolved()) c.resolve();
383 RVMMethod it = c.findDeclaredMethod(name, descriptor);
384 if (it != null) return it;
385 for (RVMClass intf : c.getDeclaredInterfaces()) {
386 it = searchInterfaceMethods(intf);
387 if (it != null) return it;
388 }
389 return null;
390 }
391 }