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