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}