/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.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.Uninterruptible; /** * A class to represent the reference in a class file to a method of * that class or interface. */ public final class VM_MethodReference extends VM_MemberReference { /** * The VM_Method that this method reference resolved to (null if not yet resolved). */ private VM_Method resolvedMember; /** * type of return value */ private final VM_TypeReference returnType; /** * types of parameters (not including "this", if virtual) */ private final VM_TypeReference[] parameterTypes; /** * @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") */ VM_MethodReference(VM_TypeReference tr, VM_Atom mn, VM_Atom d) { super(tr, mn, d); ClassLoader cl = tr.getClassLoader(); returnType = d.parseForReturnType(cl); parameterTypes = d.parseForParameterTypes(cl); } /** * @return return type of the method */ @Uninterruptible public VM_TypeReference getReturnType() { return returnType; } /** * @return parameter types of the method */ @Uninterruptible public VM_TypeReference[] getParameterTypes() { return parameterTypes; } /** * 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 (VM_TypeReference parameterType : parameterTypes) pw += parameterType.getStackWords(); return pw; } /** * Do this and that definitely refer to the different methods? */ public boolean definitelyDifferent(VM_MethodReference that) { if (this == that) return false; if (name != that.name || descriptor != that.descriptor) return true; VM_Method mine = peekResolvedMethod(false); VM_Method theirs = that.peekResolvedMethod(false); if (mine == null || theirs == null) return false; return mine != theirs; } /** * Do this and that definitely refer to the same method? */ public boolean definitelySame(VM_MethodReference that) { if (this == that) return true; if (name != that.name || descriptor != that.descriptor) return false; VM_Method mine = peekResolvedMethod(false); VM_Method theirs = that.peekResolvedMethod(false); if (mine == null || theirs == null) return false; return mine == theirs; } /** * Has the method reference 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. */ @Uninterruptible public VM_Method getResolvedMember() { return resolvedMember; } /** * For use by VM_Method constructor */ void setResolvedMember(VM_Method it) { if (VM.VerifyAssertions) VM._assert(resolvedMember == null || resolvedMember == it); resolvedMember = it; } /** * Resolve the method reference for an invoke special into a target * method, return null if the method cannot be resolved without classloading. */ public synchronized VM_Method resolveInvokeSpecial(boolean forSubArch) { VM_Class thisClass = (VM_Class) type.peekType(); if (thisClass == null && name != VM_ClassLoader.StandardObjectInitializerMethodName) { thisClass = (VM_Class) type.resolve(forSubArch); /* 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. } VM_Method sought = resolveInternal(thisClass, forSubArch); if (sought.isObjectInitializer()) { return sought; // <init> } VM_Class cls = sought.getDeclaringClass(); if (!cls.isSpecial()) { return sought; // old-style invokespecial semantics } for (; cls != null; cls = cls.getSuperClass()) { VM_Method found = cls.findDeclaredMethod(sought.getName(), sought.getDescriptor()); if (found != null) { return found; // new-style invokespecial semantics } } return null; // cannot be found } /** * Find the VM_Method that this method reference refers to using * the search order specified in JVM spec 5.4.3.3. * @return the VM_Method that this method ref resolved to or null if it cannot be resolved. */ public VM_Method peekResolvedMethod(boolean forSubArch) { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Try to do it now without triggering class loading. VM_Class declaringClass = (VM_Class) type.peekType(); if (declaringClass == null) return null; return resolveInternal(declaringClass, forSubArch); } /** * Find the VM_Method that this field reference refers to using * the search order specified in JVM spec 5.4.3.3. * @return the VM_Method that this method ref resolved to. */ public synchronized VM_Method resolve(boolean forSubArch) { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Do it now triggering class loading if necessary. return resolveInternal((VM_Class) type.resolve(forSubArch), forSubArch); } static final boolean DBG = false; /** * Return 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". * * 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 boolean 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. VM_Class declaringClass = (VM_Class) type.peekType(); if (declaringClass == null) { return false; } if (!declaringClass.isResolved(false)) { declaringClass.resolve(false); } // See if method is in any superclasses for (VM_Class c = declaringClass; c != null; c = c.getSuperClass()) { if (c.findDeclaredMethod(name, descriptor) != null) { // Method in superclass => not interface method return false; } // See if method is in any interfaces of c for (VM_Class 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. */ 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. */ 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. */ public boolean isSysCall() { return (getType() == VM_TypeReference.SysCall) || ((resolvedMember != null) && (resolvedMember.isSysCall())); } /** * Find the VM_Method that this member reference refers to using * the search order specified in JVM spec 5.4.3.3. * @return the VM_Method that this method ref resolved to. */ private VM_Method resolveInternal(VM_Class declaringClass, boolean forSubArch) { if (!declaringClass.isResolved(forSubArch)) { declaringClass.resolve(forSubArch); } for (VM_Class c = declaringClass; c != null; c = c.getSuperClass()) { if (DBG) { VM.sysWrite("Checking for <" + name + "," + descriptor + "> in class " + c + "..."); } VM_Method it = c.findDeclaredMethod(name, descriptor); if (it != null) { if (DBG) { VM.sysWriteln("...found <" + name + "," + descriptor + "> in class " + c); } resolvedMember = it; return resolvedMember; } if (DBG) { VM.sysWriteln("...NOT found <" + name + "," + descriptor + "> in class " + c); } } if (!VM.fullyBooted) { VM.sysWrite("VM_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"); } else { VM.sysWriteln(", while writing the boot image"); } VM.sysFail( "VM_MethodReference.resolveInternal(): Unable to resolve a method during VM booting or boot image writing"); } throw new NoSuchMethodError(this.toString()); } /** * Find the VM_Method that this member reference refers to using * the search order specified in JVM spec 5.4.3.4. * @return the VM_Method that this method ref resolved to or null if it cannot be resolved without trigering class loading */ public VM_Method peekInterfaceMethod() { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Try to do it now. VM_Class declaringClass = (VM_Class) type.peekType(); if (declaringClass == null) return null; if (!declaringClass.isResolved(false)) { declaringClass.resolve(false); } if (!declaringClass.isInterface()) return null; return resolveInterfaceMethodInternal(declaringClass); } /** * Find the VM_Method that this member reference refers to using * the search order specified in JVM spec 5.4.3.4. * @return the VM_Method that this method ref resolved to */ public VM_Method resolveInterfaceMethod(boolean forSubArch) throws IncompatibleClassChangeError, NoSuchMethodError { if (resolvedMember != null) return resolvedMember; // Hasn't been resolved yet. Do it now. VM_Class declaringClass = (VM_Class) type.resolve(false); if (!declaringClass.isResolved(forSubArch)) { declaringClass.resolve(forSubArch); } /* Interface method may be either in interface, or a miranda. */ if (!declaringClass.isInterface() && !isMiranda()) { throw new IncompatibleClassChangeError(); } VM_Method ans = resolveInterfaceMethodInternal(declaringClass); if (ans == null) { throw new NoSuchMethodError(this.toString()); } return ans; } /** * Find the VM_Method that this member reference refers to using * the search order specified in JVM spec 5.4.3.4. * @return the VM_Method that this method ref resolved to or null for error */ private VM_Method resolveInterfaceMethodInternal(VM_Class declaringClass) { VM_Method it = declaringClass.findDeclaredMethod(name, descriptor); if (it != null) { resolvedMember = it; return resolvedMember; } for (VM_Class intf : declaringClass.getDeclaredInterfaces()) { it = searchInterfaceMethods(intf); if (it != null) { resolvedMember = it; return resolvedMember; } } return null; } private VM_Method searchInterfaceMethods(VM_Class c) { if (!c.isResolved(false)) c.resolve(false); VM_Method it = c.findDeclaredMethod(name, descriptor); if (it != null) return it; for (VM_Class intf : c.getDeclaredInterfaces()) { it = searchInterfaceMethods(intf); if (it != null) return it; } return null; } }