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, if that is possible without causing classloading.
141       *
142       * @return target method or {@code null} if the method cannot be resolved without classloading.
143       */
144      public synchronized RVMMethod resolveInvokeSpecial() {
145        RVMClass thisClass = (RVMClass) type.peekType();
146        if (thisClass == null && name != RVMClassLoader.StandardObjectInitializerMethodName) {
147          thisClass = (RVMClass) type.resolve();
148          /* Can't fail to resolve thisClass; we're at compile time doing
149             resolution of an invocation to a private method or super call.  We
150             must have loaded the class already */
151        }
152        if (thisClass == null) {
153          return null; // can't be found now.
154        }
155        RVMMethod sought = resolveInternal(thisClass);
156    
157        if (sought.isObjectInitializer()) {
158          return sought;   // <init>
159        }
160    
161        RVMClass cls = sought.getDeclaringClass();
162        if (!cls.isSpecial()) {
163          return sought;   // old-style invokespecial semantics
164        }
165    
166        for (; cls != null; cls = cls.getSuperClass()) {
167          RVMMethod found = cls.findDeclaredMethod(sought.getName(), sought.getDescriptor());
168          if (found != null) {
169            return found; // new-style invokespecial semantics
170          }
171        }
172        return null; // cannot be found
173      }
174    
175      /**
176       * Find the RVMMethod that this method reference refers to using
177       * the search order specified in JVM spec 5.4.3.3.
178       * @return the RVMMethod that this method ref resolved to or {@code null} if it cannot be resolved.
179       */
180      public RVMMethod peekResolvedMethod() {
181        if (resolvedMember != null) return resolvedMember;
182    
183        // Hasn't been resolved yet. Try to do it now without triggering class loading.
184        RVMType declaringClass = type.peekType();
185        if (declaringClass == null) return null;
186        return resolveInternal((RVMClass)declaringClass);
187      }
188    
189      /**
190       * Find the RVMMethod that this field reference refers to using
191       * the search order specified in JVM specification 5.4.3.3.
192       * @return the RVMMethod that this method reference resolved to.
193       */
194      public synchronized RVMMethod resolve() {
195        if (resolvedMember != null) return resolvedMember;
196    
197        // Hasn't been resolved yet. Do it now triggering class loading if necessary.
198        return resolveInternal((RVMClass) type.resolve());
199      }
200    
201      /**
202       * Return {@code true} iff this member reference refers to a method which
203       * is declared as part of an abstract class but actually is an
204       * interface method, known formally as a "miranda method".
205       * <p>
206       * This method is necessary to handle the special case where an
207       * invokevirtual is defined on an abstract class, where the
208       * method invocation points to a method inherited from an
209       * interface.
210       *
211       * @return boolean    {@code true} iff this member method reference is a miranda method
212       */
213      public boolean isMiranda() {
214    
215        // Hasn't been resolved yet. Try to do it now without triggering class loading.
216        RVMClass declaringClass = (RVMClass) type.peekType();
217        if (declaringClass == null) { return false; }
218    
219        if (!declaringClass.isResolved()) {
220          declaringClass.resolve();
221        }
222    
223        // See if method is explicitly declared in any superclass
224        for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
225    
226          if (c.findDeclaredMethod(name, descriptor) != null) {
227            // Method declared in superclass => not interface method
228            return false;
229          }
230        }
231    
232        // Not declared in any superclass; now check to see if it is coming from an interface somewhere
233        for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
234          // See if method is in any interfaces of c
235          for (RVMClass intf : c.getDeclaredInterfaces()) {
236            if (searchInterfaceMethods(intf) != null) {
237              // Found method in interface or superinterface
238              return true;
239            }
240          }
241        }
242    
243        // neither in superclass or interface => not interface method
244        return false;
245      }
246    
247      /**
248       * Is the method reference to a magic method? NB. In the case of
249       * SysCall annotated methods we don't know until they are resolved.
250       */
251      public boolean isMagic() {
252        return getType().isMagicType() || ((resolvedMember != null) && (resolvedMember.isSysCall() || resolvedMember.isSpecializedInvoke()));
253      }
254    
255      /**
256       * Is the method reference to a specialized invoke? NB. we don't know until they are resolved.
257       */
258      public boolean isSpecializedInvoke() {
259        return (resolvedMember != null) && (resolvedMember.isSpecializedInvoke());
260      }
261    
262      /**
263       * Is the method reference to a magic method? NB. In the case of
264       * SysCall annotated methods we don't know until they are resolved.
265       */
266      public boolean isSysCall() {
267        return (getType() == TypeReference.SysCall) || ((resolvedMember != null) && (resolvedMember.isSysCall()));
268      }
269    
270      /**
271       * Find the RVMMethod that this member reference refers to using
272       * the search order specified in JVM spec 5.4.3.3.
273       * @return the RVMMethod that this method ref resolved to.
274       */
275      private RVMMethod resolveInternal(RVMClass declaringClass) {
276        final boolean DBG=false;
277        if (!declaringClass.isResolved()) {
278          declaringClass.resolve();
279        }
280        for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) {
281          if (DBG) {
282            VM.sysWrite("Checking for <" + name + "," + descriptor + "> in class " + c + "...");
283          }
284    
285          RVMMethod it = c.findDeclaredMethod(name, descriptor);
286          if (it != null) {
287            if (DBG) {
288              VM.sysWriteln("...found <" + name + "," + descriptor + "> in class " + c);
289            }
290            resolvedMember = it;
291            return resolvedMember;
292          }
293          if (DBG) {
294            VM.sysWriteln("...NOT found <" + name + "," + descriptor + "> in class " + c);
295          }
296        }
297        if (!VM.fullyBooted) {
298          VM.sysWrite("MethodReference.resolveInternal():");
299          VM.sysWrite(" Unable to find a method named ");
300          name.sysWrite();
301          VM.sysWrite(" with descriptor ");
302          descriptor.sysWrite();
303          VM.sysWrite(" in the class ");
304          declaringClass.getDescriptor().sysWrite();
305          if (VM.runningVM) {
306            VM.sysWriteln(", while booting the VM");
307            VM.sysFail("MethodReference.resolveInternal(): Unable to resolve a method during VM booting");
308    
309          } else {
310            VM.sysWriteln(", while writing the boot image");
311            Thread.dumpStack();
312            throw new Error("MethodReference.resolveInternal(): Unable to resolve a method during boot image writing");
313          }
314        }
315        throw new NoSuchMethodError(this.toString());
316      }
317    
318      /**
319       * Find the RVMMethod that this member reference refers to using
320       * the search order specified in JVM spec 5.4.3.4.
321       * @return the RVMMethod that this method ref resolved to or {@code null} if it cannot be resolved without trigering class loading
322       */
323      public RVMMethod peekInterfaceMethod() {
324        if (resolvedMember != null) return resolvedMember;
325    
326        // Hasn't been resolved yet. Try to do it now.
327        RVMClass declaringClass = (RVMClass) type.peekType();
328        if (declaringClass == null) return null;
329        if (!declaringClass.isResolved()) {
330          declaringClass.resolve();
331        }
332        if (!declaringClass.isInterface()) return null;
333        return resolveInterfaceMethodInternal(declaringClass);
334      }
335    
336      /**
337       * Find the RVMMethod that this member reference refers to using
338       * the search order specified in JVM spec 5.4.3.4.
339       * @return the RVMMethod that this method ref resolved to
340       */
341      public RVMMethod resolveInterfaceMethod() throws IncompatibleClassChangeError, NoSuchMethodError {
342        if (resolvedMember != null) return resolvedMember;
343    
344        // Hasn't been resolved yet. Do it now.
345        RVMClass declaringClass = (RVMClass) type.resolve();
346        if (!declaringClass.isResolved()) {
347          declaringClass.resolve();
348        }
349    
350        /* Interface method may be either in interface, or a miranda.
351        */
352        if (!declaringClass.isInterface() && !isMiranda()) {
353          throw new IncompatibleClassChangeError();
354        }
355        RVMMethod ans = resolveInterfaceMethodInternal(declaringClass);
356        if (ans == null) {
357          throw new NoSuchMethodError(this.toString());
358        }
359        return ans;
360      }
361    
362      /**
363       * Find the RVMMethod that this member reference refers to using
364       * the search order specified in JVM spec 5.4.3.4.
365       * @return the RVMMethod that this method ref resolved to or {@code null} for error
366       */
367      private RVMMethod resolveInterfaceMethodInternal(RVMClass declaringClass) {
368        RVMMethod it = declaringClass.findDeclaredMethod(name, descriptor);
369        if (it != null) {
370          resolvedMember = it;
371          return resolvedMember;
372        }
373        for (RVMClass intf : declaringClass.getDeclaredInterfaces()) {
374          it = searchInterfaceMethods(intf);
375          if (it != null) {
376            resolvedMember = it;
377            return resolvedMember;
378          }
379        }
380        return null;
381      }
382    
383      private RVMMethod searchInterfaceMethods(RVMClass c) {
384        if (!c.isResolved()) c.resolve();
385        RVMMethod it = c.findDeclaredMethod(name, descriptor);
386        if (it != null) return it;
387        for (RVMClass intf : c.getDeclaredInterfaces()) {
388          it = searchInterfaceMethods(intf);
389          if (it != null) return it;
390        }
391        return null;
392      }
393    }