/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.classloader; import org.jikesrvm.VM; import org.vmmagic.pragma.Pure; import org.vmmagic.pragma.Uninterruptible; /** * A class to represent the reference in a class file to a method of * that class or interface. */ public final class MethodReference extends MemberReference { /** * type of return value */ private final TypeReference returnType; /** * types of parameters (not including "this", if virtual) */ private final TypeReference[] parameterTypes; /** * The RVMMethod that this method reference resolved to (null if not yet resolved). */ private RVMMethod resolvedMember; /** * Find or create a method reference * @param tRef the type reference * @param mn the name of the member * @param md the descriptor of the member * @return a method reference, never {@code null} * @see MemberReference#findOrCreate(TypeReference, Atom, Atom) */ @Pure public static MethodReference findOrCreate(TypeReference tRef, Atom mn, Atom md) { return MemberReference.findOrCreate(tRef, mn, md).asMethodReference(); } /** * @param tr a type reference to the defining class in which this method * appears. (e.g., "Ljava/lang/String;") * @param mn the name of this method (e.g., "equals") * @param d the method descriptor (e.g., "(Ljava/lang/Object;)Z") * @param id the new ID of the member were a new member required */ MethodReference(TypeReference tr, Atom mn, Atom d, int id) { super(tr, mn, d, id); ClassLoader cl = tr.getClassLoader(); returnType = d.parseForReturnType(cl); parameterTypes = d.parseForParameterTypes(cl); } /** * @return return type of the method */ @Uninterruptible public TypeReference getReturnType() { return returnType; } /** * @return parameter types of the method */ @Uninterruptible public TypeReference[] getParameterTypes() { return parameterTypes; } /** * @return space required by method for its parameters, in words. * Note: does *not* include implicit "this" parameter, if any. */ @Uninterruptible public int getParameterWords() { int pw = 0; for (TypeReference parameterType : parameterTypes) pw += parameterType.getStackWords(); return pw; } /** * Do this and that definitely refer to different methods? * * @param that the reference to compare with * @return {@code true} if the methods are definitely different, {@code false} * if it's not known (e.g. because at least one of the method references is * unresolved) */ public boolean definitelyDifferent(MethodReference that) { if (this == that) return false; if (name != that.name || descriptor != that.descriptor) return true; RVMMethod mine = peekResolvedMethod(); RVMMethod theirs = that.peekResolvedMethod(); if (mine == null || theirs == null) return false; return mine != theirs; } /** * Do this and that definitely refer to the same method? * * @param that the reference to compare with * @return {@code true} if the methods are definitely the same, {@code false} * if it's not known (e.g. because at least one of the method references is * unresolved) */ public boolean definitelySame(MethodReference that) { if (this == that) return true; if (name != that.name || descriptor != that.descriptor) return false; RVMMethod mine = peekResolvedMethod(); RVMMethod theirs = that.peekResolvedMethod(); if (mine == null || theirs == null) return false; return mine == theirs; } /** * @return {@code true} if the method reference has already been resolved * into a target method */ public boolean isResolved() { return resolvedMember != null; } /** * Get the member this reference has been resolved to, if * it has already been resolved. Does NOT force resolution. * * @return the resolved RVMMethod or {@code null} if not yet resolved */ @Uninterruptible public RVMMethod getResolvedMember() { return resolvedMember; } void setResolvedMember(RVMMethod it) { if (VM.VerifyAssertions) VM._assert(resolvedMember == null || resolvedMember == it); resolvedMember = it; } /** * Resolves the method reference for an invoke special into a target * method, if that is possible without causing classloading. * * @return target method or {@code null} if the method cannot be resolved without classloading. */ public synchronized RVMMethod resolveInvokeSpecial() { RVMClass thisClass = (RVMClass) type.peekType(); if (thisClass == null && name != RVMClassLoader.StandardObjectInitializerMethodName) { thisClass = (RVMClass) type.resolve(); /* Can't fail to resolve thisClass; we're at compile time doing resolution of an invocation to a private method or super call. We must have loaded the class already */ } if (thisClass == null) { return null; // can't be found now. } RVMMethod sought = resolveInternal(thisClass); if (sought.isObjectInitializer()) { return sought; // <init> } RVMClass cls = sought.getDeclaringClass(); if (!cls.isSpecial()) { return sought; // old-style invokespecial semantics } for (; cls != null; cls = cls.getSuperClass()) { RVMMethod found = cls.findDeclaredMethod(sought.getName(), sought.getDescriptor()); if (found != null) { return found; // new-style invokespecial semantics } } return null; // cannot be found } /** * Find the RVMMethod that this method reference refers to using * the search order specified in JVM spec 5.4.3.3. * @return the RVMMethod that this method ref resolved to or {@code null} if it cannot be resolved. */ public RVMMethod peekResolvedMethod() { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Try to do it now without triggering class loading. RVMType declaringClass = type.peekType(); if (declaringClass == null) return null; return resolveInternal((RVMClass)declaringClass); } /** * Find the RVMMethod that this field reference refers to using * the search order specified in JVM specification 5.4.3.3. * @return the RVMMethod that this method reference resolved to. */ public synchronized RVMMethod resolve() { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Do it now triggering class loading if necessary. return resolveInternal((RVMClass) type.resolve()); } /** * Return {@code true} iff this member reference refers to a method which * is declared as part of an abstract class but actually is an * interface method, known formally as a "miranda method". * <p> * This method is necessary to handle the special case where an * invokevirtual is defined on an abstract class, where the * method invocation points to a method inherited from an * interface. * * @return {@code true} iff this member method reference is a miranda method */ public boolean isMiranda() { // Hasn't been resolved yet. Try to do it now without triggering class loading. RVMClass declaringClass = (RVMClass) type.peekType(); if (declaringClass == null) { return false; } if (!declaringClass.isResolved()) { declaringClass.resolve(); } // See if method is explicitly declared in any superclass for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) { if (c.findDeclaredMethod(name, descriptor) != null) { // Method declared in superclass => not interface method return false; } } // Not declared in any superclass; now check to see if it is coming from an interface somewhere for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) { // See if method is in any interfaces of c for (RVMClass intf : c.getDeclaredInterfaces()) { if (searchInterfaceMethods(intf) != null) { // Found method in interface or superinterface return true; } } } // neither in superclass or interface => not interface method return false; } /** * Is the method reference to a magic method? NB. In the case of * SysCall annotated methods we don't know until they are resolved. * * @return {@code true} if this method reference is for a magic method */ public boolean isMagic() { return getType().isMagicType() || ((resolvedMember != null) && (resolvedMember.isSysCall() || resolvedMember.isSpecializedInvoke())); } /** * Is the method reference to a specialized invoke? NB. we don't know until they are resolved. * * @return {@code true} if this method reference is for a specialized invoke */ public boolean isSpecializedInvoke() { return (resolvedMember != null) && (resolvedMember.isSpecializedInvoke()); } /** * Is the method reference to a magic method? NB. In the case of * SysCall annotated methods we don't know until they are resolved. * * @return {@code true} if this method reference is for a sysCall method */ public boolean isSysCall() { return (getType() == TypeReference.SysCall) || ((resolvedMember != null) && (resolvedMember.isSysCall())); } /** * Find the RVMMethod that this member reference refers to using * the search order specified in JVM spec 5.4.3.3. * @param declaringClass the class that declared the method * @return the RVMMethod that this method ref resolved to. */ private RVMMethod resolveInternal(RVMClass declaringClass) { final boolean DBG = false; if (!declaringClass.isResolved()) { declaringClass.resolve(); } // According to 5.4.3.3, (2) in the JVM spec, search all classes first, ignoring // interfaces for (RVMClass c = declaringClass; c != null; c = c.getSuperClass()) { if (DBG) { VM.sysWrite("Checking for <" + name + "," + descriptor + "> in class " + c + "..."); } RVMMethod method = c.findDeclaredMethod(name, descriptor); if (method != null) { if (DBG) { VM.sysWriteln("...found <" + name + "," + descriptor + "> in class " + c); } resolvedMember = method; return resolvedMember; } if (DBG) { VM.sysWriteln("...NOT found <" + name + "," + descriptor + "> in class " + c); } } // If nothing found, search superinterfaces (i.e. all interfaces implemented by // the class, directly or indirectly), according to 5.4.3.3 (3) for (RVMClass i : declaringClass.getAllImplementedInterfaces()) { if (DBG) { VM.sysWrite("Checking for <" + name + "," + descriptor + "> in interface " + i + "..."); VM.sysWrite("interface " + i + " is resolved: " + i.isResolved()); } RVMMethod method = i.findDeclaredMethod(name, descriptor); if (method != null) { if (DBG) { VM.sysWriteln("...found <" + name + "," + descriptor + "> in interface " + i); } // Interfaces only declare methods - they don't implement them (at least for Java 6 // which is all we support right now). if (VM.VerifyAssertions) VM._assert(!method.hasOffset()); // Find the virtual method to call. resolvedMember = declaringClass.findVirtualMethod(name, descriptor); if (VM.VerifyAssertions) VM._assert(resolvedMember != null); if (VM.VerifyAssertions) VM._assert(resolvedMember.hasOffset()); return resolvedMember; } if (DBG) { VM.sysWriteln("...NOT found <" + name + "," + descriptor + "> in interface " + i); } } if (!VM.fullyBooted) { VM.sysWrite("MethodReference.resolveInternal():"); VM.sysWrite(" Unable to find a method named "); name.sysWrite(); VM.sysWrite(" with descriptor "); descriptor.sysWrite(); VM.sysWrite(" in the class "); declaringClass.getDescriptor().sysWrite(); if (VM.runningVM) { VM.sysWriteln(", while booting the VM"); VM.sysFail("MethodReference.resolveInternal(): Unable to resolve a method during VM booting"); } else { VM.sysWriteln(", while writing the boot image"); Thread.dumpStack(); throw new Error("MethodReference.resolveInternal(): Unable to resolve a method during boot image writing"); } } throw new NoSuchMethodError(this.toString()); } /** * Find the RVMMethod that this member reference refers to using * the search order specified in JVM spec 5.4.3.4. * @return the RVMMethod that this method ref resolved to or {@code null} if it cannot be resolved without trigering class loading */ public RVMMethod peekInterfaceMethod() { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Try to do it now. RVMClass declaringClass = (RVMClass) type.peekType(); if (declaringClass == null) return null; if (!declaringClass.isResolved()) { declaringClass.resolve(); } if (!declaringClass.isInterface()) return null; return resolveInterfaceMethodInternal(declaringClass); } /** * Find the RVMMethod that this member reference refers to using * the search order specified in JVM spec 5.4.3.4. * @return the RVMMethod that this method ref resolved to */ public RVMMethod resolveInterfaceMethod() throws IncompatibleClassChangeError, NoSuchMethodError { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Do it now. RVMClass declaringClass = (RVMClass) type.resolve(); if (!declaringClass.isResolved()) { declaringClass.resolve(); } /* Interface method may be either in interface, or a miranda. */ if (!declaringClass.isInterface() && !isMiranda()) { throw new IncompatibleClassChangeError(); } RVMMethod ans = resolveInterfaceMethodInternal(declaringClass); if (ans == null) { throw new NoSuchMethodError(this.toString()); } return ans; } /** * Find the RVMMethod that this member reference refers to using * the search order specified in JVM spec 5.4.3.4. * @param declaringClass the class that declared the method * @return the RVMMethod that this method ref resolved to or {@code null} for error */ private RVMMethod resolveInterfaceMethodInternal(RVMClass declaringClass) { RVMMethod it = declaringClass.findDeclaredMethod(name, descriptor); if (it != null) { resolvedMember = it; return resolvedMember; } for (RVMClass intf : declaringClass.getDeclaredInterfaces()) { it = searchInterfaceMethods(intf); if (it != null) { resolvedMember = it; return resolvedMember; } } return null; } private RVMMethod searchInterfaceMethods(RVMClass c) { if (!c.isResolved()) c.resolve(); RVMMethod it = c.findDeclaredMethod(name, descriptor); if (it != null) return it; for (RVMClass intf : c.getDeclaredInterfaces()) { it = searchInterfaceMethods(intf); if (it != null) return it; } return null; } }