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    }