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 java.io.DataInputStream; 016import java.io.IOException; 017import java.lang.annotation.Annotation; 018import org.jikesrvm.compilers.common.CodeArray; 019import org.jikesrvm.compilers.common.LazyCompilationTrampoline; 020import org.jikesrvm.VM; 021import org.jikesrvm.compilers.common.CompiledMethod; 022import org.jikesrvm.compilers.common.CompiledMethods; 023import org.jikesrvm.runtime.Entrypoints; 024import org.jikesrvm.runtime.Reflection; 025import org.jikesrvm.runtime.ReflectionBase; 026import org.jikesrvm.runtime.Statics; 027import org.jikesrvm.util.HashMapRVM; 028import org.jikesrvm.util.ImmutableEntryHashMapRVM; 029import org.vmmagic.pragma.Pure; 030import org.vmmagic.pragma.RuntimePure; 031import org.vmmagic.pragma.Uninterruptible; 032import org.vmmagic.pragma.Unpreemptible; 033import org.vmmagic.unboxed.Offset; 034 035import static org.jikesrvm.classloader.TypeReference.baseReflectionClass; 036import static org.jikesrvm.runtime.JavaSizeConstants.BITS_IN_SHORT; 037import static org.jikesrvm.classloader.BytecodeConstants.*; 038import static org.jikesrvm.classloader.ClassLoaderConstants.*; 039 040 041/** 042 * A method of a java class corresponding to a method_info structure 043 * in the class file. A method is read from a class file using the 044 * {@link #readMethod} method. 045 */ 046public abstract class RVMMethod extends RVMMember { 047 048 /** 049 * current compiled method for this method 050 */ 051 protected CompiledMethod currentCompiledMethod; 052 /** 053 * exceptions this method might throw (null --> none) 054 */ 055 private static final ImmutableEntryHashMapRVM<RVMMethod, TypeReference[]> exceptionTypes = 056 new ImmutableEntryHashMapRVM<RVMMethod, TypeReference[]>(); 057 /** 058 * Method parameter annotations from the class file that are 059 * described as runtime visible. These annotations are available to 060 * the reflection API. 061 */ 062 private static final ImmutableEntryHashMapRVM<RVMMethod, RVMAnnotation[][]> parameterAnnotations = 063 new ImmutableEntryHashMapRVM<RVMMethod, RVMAnnotation[][]>(); 064 /** 065 * A table mapping to values present in the method info tables of annotation 066 * types. It represents the default result from an annotation method. 067 */ 068 private static final HashMapRVM<RVMMethod, Object> annotationDefaults = 069 new HashMapRVM<RVMMethod, Object>(); 070 /** 071 * The offsets of virtual methods in the JTOC, if it's been placed 072 * there by constant propagation. 073 */ 074 private static final ImmutableEntryHashMapRVM<RVMMethod, Integer> jtocOffsets = 075 new ImmutableEntryHashMapRVM<RVMMethod, Integer>(); 076 077 /** Cache of arrays of declared parameter annotations. */ 078 private static final ImmutableEntryHashMapRVM<RVMMethod, Annotation[][]> declaredParameterAnnotations = 079 new ImmutableEntryHashMapRVM<RVMMethod, Annotation[][]>(); 080 081 /** 082 * Construct a read method 083 * 084 * @param declaringClass the RVMClass object of the class that declared this field 085 * @param memRef the canonical memberReference for this method. 086 * @param modifiers modifiers associated with this method. 087 * @param exceptionTypes exceptions thrown by this method. 088 * @param signature generic type of this method. 089 * @param annotations array of runtime visible annotations 090 * @param parameterAnnotations array of runtime visible parameter annotations 091 * @param annotationDefault value for this annotation that appears 092 */ 093 protected RVMMethod(TypeReference declaringClass, MemberReference memRef, short modifiers, 094 TypeReference[] exceptionTypes, Atom signature, RVMAnnotation[] annotations, 095 RVMAnnotation[][] parameterAnnotations, Object annotationDefault) { 096 super(declaringClass, memRef, (short) (modifiers & APPLICABLE_TO_METHODS), signature, annotations); 097 if (parameterAnnotations != null) { 098 synchronized (RVMMethod.parameterAnnotations) { 099 RVMMethod.parameterAnnotations.put(this, parameterAnnotations); 100 } 101 } 102 if (exceptionTypes != null) { 103 synchronized (RVMMethod.exceptionTypes) { 104 RVMMethod.exceptionTypes.put(this, exceptionTypes); 105 } 106 } 107 if (annotationDefault != null) { 108 synchronized (annotationDefaults) { 109 annotationDefaults.put(this, annotationDefault); 110 } 111 } 112 } 113 114 /** 115 * @return the parameter annotations for this method 116 */ 117 @Pure 118 private RVMAnnotation[][] getParameterAnnotations() { 119 synchronized (parameterAnnotations) { 120 return parameterAnnotations.get(this); 121 } 122 } 123 124 /** 125 * @return the annotation default value for an annotation method 126 */ 127 @Pure 128 public Object getAnnotationDefault() { 129 synchronized (annotationDefaults) { 130 Object value = annotationDefaults.get(this); 131 if (value instanceof TypeReference || value instanceof Object[]) { 132 value = RVMAnnotation.firstUse(value); 133 annotationDefaults.put(this, value); 134 } 135 return value; 136 } 137 } 138 139 /** 140 * Called from {@link ClassFileReader#readClass(TypeReference,DataInputStream)} to create an 141 * instance of a RVMMethod by reading the relevant data from the argument bytecode stream. 142 * 143 * @param declaringClass the TypeReference of the class being loaded 144 * @param constantPool the constantPool of the RVMClass object that's being constructed 145 * @param memRef the canonical memberReference for this member. 146 * @param modifiers modifiers associated with this member. 147 * @param input the DataInputStream to read the method's attributes from 148 * @throws IOException when the underlying stream throws an IOException or when 149 * the skipping of attributes does not work as expected 150 * @return the newly created method 151 */ 152 static RVMMethod readMethod(TypeReference declaringClass, int[] constantPool, MemberReference memRef, 153 short modifiers, DataInputStream input) throws IOException { 154 short tmp_localWords = 0; 155 short tmp_operandWords = 0; 156 byte[] tmp_bytecodes = null; 157 ExceptionHandlerMap tmp_exceptionHandlerMap = null; 158 TypeReference[] tmp_exceptionTypes = null; 159 int[] tmp_lineNumberMap = null; 160 LocalVariableTable tmp_localVariableTable = null; 161 Atom tmp_signature = null; 162 RVMAnnotation[] annotations = null; 163 RVMAnnotation[][] parameterAnnotations = null; 164 Object tmp_annotationDefault = null; 165 166 // Read the attributes 167 for (int i = 0, n = input.readUnsignedShort(); i < n; i++) { 168 Atom attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort()); 169 int attLength = input.readInt(); 170 171 // Only bother to interpret non-boring Method attributes 172 if (attName == RVMClassLoader.codeAttributeName) { 173 tmp_operandWords = input.readShort(); 174 tmp_localWords = input.readShort(); 175 tmp_bytecodes = new byte[input.readInt()]; 176 input.readFully(tmp_bytecodes); 177 tmp_exceptionHandlerMap = ExceptionHandlerMap.readExceptionHandlerMap(input, constantPool); 178 179 // Read the attributes portion of the code attribute 180 for (int j = 0, n2 = input.readUnsignedShort(); j < n2; j++) { 181 attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort()); 182 attLength = input.readInt(); 183 184 if (attName == RVMClassLoader.lineNumberTableAttributeName) { 185 int cnt = input.readUnsignedShort(); 186 if (cnt != 0) { 187 tmp_lineNumberMap = new int[cnt]; 188 for (int k = 0; k < cnt; k++) { 189 int startPC = input.readUnsignedShort(); 190 int lineNumber = input.readUnsignedShort(); 191 tmp_lineNumberMap[k] = (lineNumber << BITS_IN_SHORT) | startPC; 192 } 193 } 194 } else if (attName == RVMClassLoader.localVariableTableAttributeName) { 195 tmp_localVariableTable = LocalVariableTable.readLocalVariableTable(input, constantPool); 196 } else { 197 // All other entries in the attribute portion of the code attribute are boring. 198 int skippedAmount = input.skipBytes(attLength); 199 if (skippedAmount != attLength) { 200 throw new IOException("Unexpected short skip"); 201 } 202 } 203 } 204 } else if (attName == RVMClassLoader.exceptionsAttributeName) { 205 int cnt = input.readUnsignedShort(); 206 if (cnt != 0) { 207 tmp_exceptionTypes = new TypeReference[cnt]; 208 for (int j = 0, m = tmp_exceptionTypes.length; j < m; ++j) { 209 tmp_exceptionTypes[j] = ClassFileReader.getTypeRef(constantPool, input.readUnsignedShort()); 210 } 211 } 212 } else if (attName == RVMClassLoader.syntheticAttributeName) { 213 modifiers |= ACC_SYNTHETIC; 214 } else if (attName == RVMClassLoader.signatureAttributeName) { 215 tmp_signature = ClassFileReader.getUtf(constantPool, input.readUnsignedShort()); 216 } else if (attName == RVMClassLoader.runtimeVisibleAnnotationsAttributeName) { 217 annotations = AnnotatedElement.readAnnotations(constantPool, input, declaringClass.getClassLoader()); 218 } else if (attName == RVMClassLoader.runtimeVisibleParameterAnnotationsAttributeName) { 219 int numParameters = input.readByte() & 0xFF; 220 parameterAnnotations = new RVMAnnotation[numParameters][]; 221 for (int a = 0; a < numParameters; ++a) { 222 parameterAnnotations[a] = AnnotatedElement.readAnnotations(constantPool, input, declaringClass.getClassLoader()); 223 } 224 } else if (attName == RVMClassLoader.annotationDefaultAttributeName) { 225 try { 226 tmp_annotationDefault = RVMAnnotation.readValue(memRef.asMethodReference().getReturnType(), constantPool, input, declaringClass.getClassLoader()); 227 } catch (ClassNotFoundException e) { 228 throw new Error(e); 229 } 230 } else { 231 // all other method attributes are boring 232 int skippedAmount = input.skipBytes(attLength); 233 if (skippedAmount != attLength) { 234 throw new IOException("Unexpected short skip"); 235 } 236 } 237 } 238 RVMMethod method; 239 if ((modifiers & ACC_NATIVE) != 0) { 240 method = 241 new NativeMethod(declaringClass, 242 memRef, 243 modifiers, 244 tmp_exceptionTypes, 245 tmp_signature, 246 annotations, 247 parameterAnnotations, 248 tmp_annotationDefault); 249 } else if ((modifiers & ACC_ABSTRACT) != 0) { 250 method = 251 new AbstractMethod(declaringClass, 252 memRef, 253 modifiers, 254 tmp_exceptionTypes, 255 tmp_signature, 256 annotations, 257 parameterAnnotations, 258 tmp_annotationDefault); 259 260 } else { 261 method = 262 new NormalMethod(declaringClass, 263 memRef, 264 modifiers, 265 tmp_exceptionTypes, 266 tmp_localWords, 267 tmp_operandWords, 268 tmp_bytecodes, 269 tmp_exceptionHandlerMap, 270 tmp_lineNumberMap, 271 tmp_localVariableTable, 272 constantPool, 273 tmp_signature, 274 annotations, 275 parameterAnnotations, 276 tmp_annotationDefault); 277 } 278 return method; 279 } 280 281 /** 282 * @return {@code true} if this method is a class initializer 283 */ 284 @Uninterruptible 285 public final boolean isClassInitializer() { 286 return getName() == RVMClassLoader.StandardClassInitializerMethodName; 287 } 288 289 /** 290 * @return {@code true} if this method is an object initializer 291 */ 292 @Uninterruptible 293 public final boolean isObjectInitializer() { 294 return getName() == RVMClassLoader.StandardObjectInitializerMethodName; 295 } 296 297 /** 298 * @return {@code true} if this method is a a compiler-generated 299 * object initializer helper 300 */ 301 @Uninterruptible 302 public final boolean isObjectInitializerHelper() { 303 return getName() == RVMClassLoader.StandardObjectInitializerHelperMethodName; 304 } 305 306 @Uninterruptible 307 public final TypeReference getReturnType() { 308 return memRef.asMethodReference().getReturnType(); 309 } 310 311 /** 312 * @return the types of this method's parameters. 313 * Note: does *not* include implicit "this" parameter, if any. 314 */ 315 @Uninterruptible 316 public final TypeReference[] getParameterTypes() { 317 return memRef.asMethodReference().getParameterTypes(); 318 } 319 320 /** 321 * @return space required by this method for its parameters, in words. 322 * Note: does *not* include implicit "this" parameter, if any. 323 */ 324 @Uninterruptible 325 public final int getParameterWords() { 326 return memRef.asMethodReference().getParameterWords(); 327 } 328 329 /** 330 * @return {@code true} if machine code has been generated 331 * for this method's bytecodes 332 */ 333 public final boolean isCompiled() { 334 return currentCompiledMethod != null; 335 } 336 337 /** 338 * Get the current compiled method for this method. 339 * Will return null if there is no current compiled method! 340 * <p> 341 * We make this method Unpreemptible to avoid a race-condition 342 * in Reflection.invoke. 343 * @return compiled method or {@code null} if none exists 344 */ 345 @Unpreemptible 346 public final synchronized CompiledMethod getCurrentCompiledMethod() { 347 return currentCompiledMethod; 348 } 349 350 /** 351 * @return {@code true} if this method is declared as statically 352 * dispatched 353 */ 354 @Uninterruptible 355 public final boolean isStatic() { 356 return (modifiers & ACC_STATIC) != 0; 357 } 358 359 /** 360 * @return {@code true} if this method is declared as non-overridable 361 * by subclasses 362 */ 363 @Uninterruptible 364 public final boolean isFinal() { 365 return (modifiers & ACC_FINAL) != 0; 366 } 367 368 /** 369 * @return {@code true} if this method is guarded by 370 * monitorenter/monitorexit 371 */ 372 @Uninterruptible 373 public final boolean isSynchronized() { 374 return (modifiers & ACC_SYNCHRONIZED) != 0; 375 } 376 377 /** 378 * @return {@code true} if this method is not implemented in java 379 */ 380 @Uninterruptible 381 public final boolean isNative() { 382 return (modifiers & ACC_NATIVE) != 0; 383 } 384 385 /** 386 * @return {@code true} if IEEE 754 rules need to be strictly 387 * enforced for this method 388 */ 389 public final boolean isStrictFP() { 390 return (modifiers & ACC_STRICT) != 0; 391 } 392 393 /** 394 * @return {@code true} if this is a method that's not implemented in Java 395 * and use C not JNI calling convention 396 * @see org.jikesrvm.runtime.SysCall 397 */ 398 public final boolean isSysCall() { 399 return isNative() && isStatic() && isAnnotationDeclared(TypeReference.SysCall); 400 } 401 402 /** 403 * @return {@code true} if this is a specialized method invoke 404 * @see SpecializedMethod 405 * @see SpecializedMethodManager 406 */ 407 public final boolean isSpecializedInvoke() { 408 return isAnnotationDeclared(TypeReference.SpecializedMethodInvoke); 409 } 410 411 /** 412 * @return {@code true} if this method needs to be implemented by subclasses 413 */ 414 @Uninterruptible 415 public final boolean isAbstract() { 416 return (modifiers & ACC_ABSTRACT) != 0; 417 } 418 419 /** 420 * @return {@code true} if this method is not present in the source code file 421 * (e.g. because it has been added by a Java compiler like javac) 422 */ 423 public boolean isSynthetic() { 424 return (modifiers & ACC_SYNTHETIC) != 0; 425 } 426 427 /** 428 * @return {@code true} if this method is a bridge method. Bridge methods are 429 * generated in some cases of generics and inheritance. 430 */ 431 public boolean isBridge() { 432 return (modifiers & BRIDGE) != 0; 433 } 434 435 /** 436 * @return {@code true} if ts this a varargs method taking a variable number 437 * of arguments 438 */ 439 public boolean isVarArgs() { 440 return (modifiers & VARARGS) != 0; 441 } 442 443 /** 444 * Exceptions thrown by this method - 445 * something like <code>{ "java/lang/IOException", "java/lang/EOFException" }</code> 446 * @return exception info or {@code null} if this method doesn't throw any 447 * exceptions 448 */ 449 @Pure 450 public final TypeReference[] getExceptionTypes() { 451 synchronized (exceptionTypes) { 452 return exceptionTypes.get(this); 453 } 454 } 455 456 /** 457 * Is this method interruptible? 458 * In other words, should the compiler insert yieldpoints 459 * in method prologue, epilogue, and backwards branches. 460 * Also, only methods that are Interruptible have stackoverflow checks 461 * in the method prologue (since there is no mechanism for handling a stackoverflow 462 * that doesn't violate the uninterruptiblity of the method). 463 * To determine if a method is interruptible, the following conditions 464 * are checked (<em>in order</em>): 465 * <ul> 466 * <li> If it is a <code><clinit></code> or <code><init></code> method then it is interruptible. 467 * <li> If is the synthetic 'this' method used by jikes to 468 * factor out default initializers for <code><init></code> methods then it is interruptible. 469 * <li> If it is annotated with <CODE>@Interruptible</CODE> it is interruptible. 470 * <li> If it is annotated with <CODE>@Preemptible</CODE> it is interruptible. 471 * <li> If it is annotated with <CODE>@Uninterruptible</CODE> it is not interruptible. 472 * <li> If it is annotated with <CODE>@UninterruptibleNoWarn</CODE> it is not interruptible. 473 * <li> If it is annotated with <CODE>@Unpreemptible</CODE> it is not interruptible. 474 * <li> If its declaring class is annotated with <CODE>@Uninterruptible</CODE> 475 * or <CODE>@Unpreemptible</CODE> it is not interruptible. 476 * </ul> 477 * 478 * @return {@code true} if and only if this method is interruptible 479 */ 480 public final boolean isInterruptible() { 481 if (isClassInitializer() || isObjectInitializer()) return true; 482 if (isObjectInitializerHelper()) return true; 483 if (hasInterruptibleAnnotation()) return true; 484 if (hasPreemptibleAnnotation()) return true; 485 if (hasUninterruptibleNoWarnAnnotation()) return false; 486 if (hasUninterruptibleAnnotation()) return false; 487 if (hasUnpreemptibleAnnotation()) return false; 488 if (hasUnpreemptibleNoWarnAnnotation()) return false; 489 if (getDeclaringClass().hasUnpreemptibleAnnotation()) return false; 490 return !getDeclaringClass().hasUninterruptibleAnnotation(); 491 } 492 493 /** 494 * Is the method Unpreemptible? See the comment in {@link #isInterruptible} 495 * 496 * @return {@code true} if and only if this method is unpreemptible 497 */ 498 public final boolean isUnpreemptible() { 499 if (isClassInitializer() || isObjectInitializer()) return false; 500 if (isObjectInitializerHelper()) return false; 501 if (hasInterruptibleAnnotation()) return false; 502 if (hasPreemptibleAnnotation()) return false; 503 if (hasUninterruptibleAnnotation()) return false; 504 if (hasUninterruptibleNoWarnAnnotation()) return false; 505 if (hasUnpreemptibleAnnotation()) return true; 506 if (hasUnpreemptibleNoWarnAnnotation()) return true; 507 return getDeclaringClass().hasUnpreemptibleAnnotation(); 508 } 509 510 /** 511 * Is the method Uninterruptible? See the comment in {@link #isInterruptible} 512 * 513 * @return {@code true} if and only if this method is uninterruptible 514 */ 515 public final boolean isUninterruptible() { 516 if (isClassInitializer() || isObjectInitializer()) return false; 517 if (isObjectInitializerHelper()) return false; 518 if (hasInterruptibleAnnotation()) return false; 519 if (hasPreemptibleAnnotation()) return false; 520 if (hasUnpreemptibleAnnotation()) return false; 521 if (hasUnpreemptibleNoWarnAnnotation()) return false; 522 if (hasUninterruptibleAnnotation()) return true; 523 if (hasUninterruptibleNoWarnAnnotation()) return true; 524 return getDeclaringClass().hasUninterruptibleAnnotation(); 525 } 526 527 /** 528 * Is the method Pure? That is would it, without any side effects, return the 529 * same value given the same arguments? 530 * 531 * @return whether the method has a pure annotation 532 */ 533 public final boolean isPure() { 534 return hasPureAnnotation() || hasRuntimePureAnnotation(); 535 } 536 537 /** 538 * Is the method RuntimePure? This is the same as Pure at runtime but has a 539 * special return value at boot image writing time 540 * 541 * @return whether the method has a pure annotation 542 */ 543 public final boolean isRuntimePure() { 544 return hasRuntimePureAnnotation(); 545 } 546 547 /** 548 * Has this method been marked as forbidden to inline? 549 * ie., it is marked with the <CODE>NoInline</CODE> annotation or 550 * the <CODE>NoOptCompile</CODE> annotation? 551 * 552 * @return {@code true} if this method must not be inlined 553 */ 554 public final boolean hasNoInlinePragma() { 555 return (hasNoInlineAnnotation() || hasNoOptCompileAnnotation()); 556 } 557 558 /** 559 * @param field the field whose write access is supposed to be checked 560 * @return {@code true} if the method may write to a given field 561 */ 562 public boolean mayWrite(RVMField field) { 563 return true; // be conservative. native methods can write to anything 564 } 565 566 /** 567 * @return {@code true} if the method is the implementation of a runtime service 568 * that is called "under the covers" from the generated code and thus is not subject to 569 * inlining via the normal mechanisms. 570 */ 571 public boolean isRuntimeServiceMethod() { 572 return false; // only NormalMethods can be runtime service impls in Jikes RVM and they override this method 573 } 574 575 /** 576 * @return {@code true} if all allocation from this method must go to 577 * a non-moving space 578 * 579 * @see org.vmmagic.pragma.NonMovingAllocation 580 */ 581 public boolean isNonMovingAllocation() { 582 return hasNonMovingAllocationAnnotation(); 583 } 584 585 //------------------------------------------------------------------// 586 // Section 2. // 587 // The following are available after the declaring class has been // 588 // "resolved". // 589 //------------------------------------------------------------------// 590 591 /** 592 * Get the code array that corresponds to the entry point (prologue) 593 * for the method. 594 * 595 * @return the code array for the method 596 */ 597 public final synchronized CodeArray getCurrentEntryCodeArray() { 598 RVMClass declaringClass = getDeclaringClass(); 599 if (VM.VerifyAssertions) VM._assert(declaringClass.isResolved()); 600 if (isCompiled()) { 601 return currentCompiledMethod.getEntryCodeArray(); 602 } else if (!VM.writingBootImage || isNative()) { 603 if (!isStatic() && !isObjectInitializer() && !isPrivate()) { 604 // A non-private virtual method. 605 if (declaringClass.isJavaLangObjectType() || 606 declaringClass.getSuperClass().findVirtualMethod(getName(), getDescriptor()) == null) { 607 // The root method of a virtual method family can use the lazy method invoker directly. 608 return Entrypoints.lazyMethodInvokerMethod.getCurrentEntryCodeArray(); 609 } else { 610 // All other virtual methods in the family must use unique stubs to 611 // ensure correct operation of the method test (guarded inlining of virtual calls). 612 // It is TIBs job to marshall between the actual trampoline and this marker. 613 return LazyCompilationTrampoline.getInstructions(); 614 } 615 } else { 616 // We'll never do a method test against this method. 617 // Therefore we can use the lazy method invoker directly. 618 return Entrypoints.lazyMethodInvokerMethod.getCurrentEntryCodeArray(); 619 } 620 } else { 621 compile(); 622 return currentCompiledMethod.getEntryCodeArray(); 623 } 624 } 625 626 /** 627 * Generate machine code for this method if valid 628 * machine code doesn't already exist. 629 */ 630 public final synchronized void compile() { 631 if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isResolved()); 632 if (isCompiled()) return; 633 634 if (VM.TraceClassLoading && VM.runningVM) VM.sysWrite("RVMMethod: (begin) compiling " + this + "\n"); 635 636 CompiledMethod cm = genCode(); 637 638 // Ensure that cm wasn't invalidated while it was being compiled. 639 synchronized (cm) { 640 if (cm.isInvalid()) { 641 CompiledMethods.setCompiledMethodObsolete(cm); 642 } else { 643 currentCompiledMethod = cm; 644 } 645 } 646 647 if (VM.TraceClassLoading && VM.runningVM) VM.sysWrite("RVMMethod: (end) compiling " + this + "\n"); 648 } 649 650 /** 651 * Generates the code for this method. 652 * 653 * @return an object representing the compiled method 654 */ 655 protected abstract CompiledMethod genCode(); 656 657 //----------------------------------------------------------------// 658 // Section 3. // 659 // The following are available after the declaring class has been // 660 // "instantiated". // 661 //----------------------------------------------------------------// 662 663 /** 664 * Change machine code that will be used by future executions of this method 665 * (ie. optimized <-> non-optimized)<p> 666 * 667 * Side effect: updates JTOC or method dispatch tables 668 * ("type information blocks") 669 * for this class and its subclasses 670 * 671 * @param compiledMethod new machine code 672 * 673 */ 674 public final synchronized void replaceCompiledMethod(CompiledMethod compiledMethod) { 675 if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated()); 676 // If we're replacing with a non-null compiledMethod, ensure that is still valid! 677 if (compiledMethod != null) { 678 synchronized (compiledMethod) { 679 if (compiledMethod.isInvalid()) return; 680 } 681 } 682 683 // Grab version that is being replaced 684 CompiledMethod oldCompiledMethod = currentCompiledMethod; 685 currentCompiledMethod = compiledMethod; 686 687 // Install the new method in JTOC/TIB. If virtual, will also replace in 688 // all subclasses that inherited the method. 689 getDeclaringClass().updateMethod(this); 690 691 // Replace constant-ified virtual method in JTOC if necessary 692 Offset jtocOffset = getJtocOffset(); 693 if (jtocOffset.NE(Offset.zero())) { 694 Statics.setSlotContents(jtocOffset, getCurrentEntryCodeArray()); 695 } 696 697 // Now that we've updated the JTOC/TIB, old version is obsolete 698 if (oldCompiledMethod != null) { 699 CompiledMethods.setCompiledMethodObsolete(oldCompiledMethod); 700 } 701 } 702 703 /** 704 * Invalidates the given compiled method if it is the current compiled code 705 * for this method. 706 * 707 * @param cm the compiled method to try to invalidate 708 */ 709 public final synchronized void invalidateCompiledMethod(CompiledMethod cm) { 710 if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated()); 711 if (currentCompiledMethod == cm) { 712 replaceCompiledMethod(null); 713 } 714 } 715 716 /** 717 * Gets the offset used to hold a JTOC addressable version of the current entry 718 * code array. 719 * 720 * @return the JTOC offset 721 */ 722 @Pure 723 private Offset getJtocOffset() { 724 Integer offAsInt; 725 synchronized (jtocOffsets) { 726 offAsInt = jtocOffsets.get(this); 727 } 728 if (offAsInt == null) { 729 return Offset.zero(); 730 } else { 731 return Offset.fromIntSignExtend(offAsInt.intValue()); 732 } 733 } 734 735 /** 736 * Finds or create a JTOC offset for this method. 737 * 738 * @return the JTOC offset 739 */ 740 public final synchronized Offset findOrCreateJtocOffset() { 741 if (VM.VerifyAssertions) VM._assert(!isStatic() && !isObjectInitializer()); 742 Offset jtocOffset = getJtocOffset();; 743 if (jtocOffset.EQ(Offset.zero())) { 744 jtocOffset = Statics.allocateReferenceSlot(true); 745 Statics.setSlotContents(jtocOffset, getCurrentEntryCodeArray()); 746 synchronized (jtocOffsets) { 747 jtocOffsets.put(this, Integer.valueOf(jtocOffset.toInt())); 748 } 749 } 750 return jtocOffset; 751 } 752 753 /** 754 * @return the parameter annotations for this method. 755 */ 756 @Pure 757 public final Annotation[][] getDeclaredParameterAnnotations() { 758 Annotation[][] result; 759 synchronized (declaredParameterAnnotations) { 760 result = declaredParameterAnnotations.get(this); 761 } 762 if (result == null) { 763 RVMAnnotation[][] parameterAnnotations = getParameterAnnotations(); 764 result = new Annotation[parameterAnnotations.length][]; 765 for (int a = 0; a < result.length; ++a) { 766 result[a] = toAnnotations(parameterAnnotations[a]); 767 } 768 synchronized (declaredParameterAnnotations) { 769 declaredParameterAnnotations.put(this, result); 770 } 771 } 772 return result; 773 } 774 775 /** Map from a method to a reflective method capable of invoking it */ 776 private static final ImmutableEntryHashMapRVM<RVMMethod, ReflectionBase> invokeMethods = 777 Reflection.bytecodeReflection || Reflection.cacheInvokerInJavaLangReflect ? 778 new ImmutableEntryHashMapRVM<RVMMethod, ReflectionBase>(30) : null; 779 780 /** 781 * @return an instance of an object capable of reflectively invoking this method 782 */ 783 @RuntimePure 784 @SuppressWarnings("unchecked") 785 public synchronized ReflectionBase getInvoker() { 786 if (!VM.runningVM) { 787 return null; 788 } 789 ReflectionBase invoker; 790 if (invokeMethods != null) { 791 synchronized (RVMMethod.class) { 792 invoker = invokeMethods.get(this); 793 } 794 } else { 795 invoker = null; 796 } 797 if (invoker == null) { 798 Class<ReflectionBase> reflectionClass = (Class<ReflectionBase>)RVMClass.createReflectionClass(this); 799 if (reflectionClass != null) { 800 try { 801 invoker = reflectionClass.newInstance(); 802 } catch (Throwable e) { 803 throw new Error(e); 804 } 805 } else { 806 invoker = ReflectionBase.nullInvoker; 807 } 808 if (invokeMethods != null) { 809 synchronized (RVMMethod.class) { 810 invokeMethods.put(this, invoker); 811 } 812 } 813 } 814 return invoker; 815 } 816 817 /** 818 * Create a method to act as a default constructor (just return) 819 * @param klass class for method 820 * @param memRef reference for default constructor 821 * @return method normal (bytecode containing) method that just returns 822 */ 823 static RVMMethod createDefaultConstructor(TypeReference klass, MemberReference memRef) { 824 return new NormalMethod(klass, 825 memRef, 826 (short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC), 827 null, 828 (short) 1, 829 (short) 0, 830 new byte[]{(byte)JBC_return}, 831 null, 832 null, 833 null, 834 new int[0], 835 null, 836 null, 837 null, 838 null); 839 } 840 841 /** 842 * Create a method for reflectively invoking this method 843 * 844 * @param reflectionClass the class this method will belong to 845 * @param constantPool for the class 846 * @param memRef the member reference corresponding to this method 847 * @return the created method 848 */ 849 RVMMethod createReflectionMethod(TypeReference reflectionClass, int[] constantPool, 850 MethodReference memRef) { 851 TypeReference[] parameters = getParameterTypes(); 852 int numParams = parameters.length; 853 byte[] bytecodes; 854 boolean interfaceCall = false; 855 int curBC = 0; 856 if (!isStatic()) { 857 if (!getDeclaringClass().isInterface()) { 858 // virtual call 859 bytecodes = new byte[8 * numParams + 8]; 860 } else { 861 // interface call 862 bytecodes = new byte[8 * numParams + 10]; 863 interfaceCall = true; 864 } 865 bytecodes[curBC] = JBC_aload_1; 866 curBC++; 867 } else { 868 // static call 869 bytecodes = new byte[8 * numParams + 7]; 870 } 871 for (int i = 0; i < numParams; i++) { 872 if (parameters[i].isVoidType()) { 873 bytecodes[curBC] = 874 bytecodes[curBC + 1] = 875 bytecodes[curBC + 2] = 876 bytecodes[curBC + 3] = 877 bytecodes[curBC + 4] = 878 bytecodes[curBC + 5] = 879 bytecodes[curBC + 6] = 880 bytecodes[curBC + 7] = 881 (byte)JBC_nop; 882 continue; 883 } 884 bytecodes[curBC] = (byte)JBC_aload_2; 885 bytecodes[curBC + 1] = (byte)JBC_sipush; 886 bytecodes[curBC + 2] = (byte)(i >>> 8); 887 bytecodes[curBC + 3] = (byte)i; 888 bytecodes[curBC + 4] = (byte)JBC_aaload; 889 if (!parameters[i].isPrimitiveType()) { 890 bytecodes[curBC + 5] = (byte)JBC_checkcast; 891 if (VM.VerifyAssertions) VM._assert(parameters[i].getId() != 0); 892 constantPool[i + 1] = ClassFileReader.packCPEntry(CP_CLASS, parameters[i].getId()); 893 bytecodes[curBC + 6] = (byte)((i + 1) >>> 8); 894 bytecodes[curBC + 7] = (byte)(i + 1); 895 } else if (parameters[i].isWordLikeType()) { 896 bytecodes[curBC + 5] = 897 bytecodes[curBC + 6] = 898 bytecodes[curBC + 7] = 899 (byte)JBC_nop; 900 } else { 901 bytecodes[curBC + 5] = (byte)JBC_invokestatic; 902 MemberReference unboxMethod; 903 if (parameters[i].isBooleanType()) { 904 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 905 Atom.findOrCreateUnicodeAtom("unboxAsBoolean"), 906 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)Z")); 907 } else if (parameters[i].isByteType()) { 908 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 909 Atom.findOrCreateUnicodeAtom("unboxAsByte"), 910 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)B")); 911 } else if (parameters[i].isShortType()) { 912 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 913 Atom.findOrCreateUnicodeAtom("unboxAsShort"), 914 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)S")); 915 } else if (parameters[i].isCharType()) { 916 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 917 Atom.findOrCreateUnicodeAtom("unboxAsChar"), 918 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)C")); 919 } else if (parameters[i].isIntType()) { 920 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 921 Atom.findOrCreateUnicodeAtom("unboxAsInt"), 922 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)I")); 923 } else if (parameters[i].isLongType()) { 924 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 925 Atom.findOrCreateUnicodeAtom("unboxAsLong"), 926 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)J")); 927 } else if (parameters[i].isFloatType()) { 928 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 929 Atom.findOrCreateUnicodeAtom("unboxAsFloat"), 930 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)F")); 931 } else { 932 if (VM.VerifyAssertions) VM._assert(parameters[i].isDoubleType()); 933 unboxMethod = MethodReference.findOrCreate(baseReflectionClass, 934 Atom.findOrCreateUnicodeAtom("unboxAsDouble"), 935 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)D")); 936 } 937 constantPool[i + 1] = ClassFileReader.packCPEntry(CP_MEMBER, unboxMethod.getId()); 938 bytecodes[curBC + 6] = (byte)((i + 1) >>> 8); 939 bytecodes[curBC + 7] = (byte)(i + 1); 940 } 941 curBC += 8; 942 } 943 if (isStatic()) { 944 bytecodes[curBC] = (byte)JBC_invokestatic; 945 } else if (isObjectInitializer() || isPrivate()) { 946 bytecodes[curBC] = (byte)JBC_invokespecial; 947 } else if (interfaceCall) { 948 bytecodes[curBC] = (byte)JBC_invokeinterface; 949 } else { 950 bytecodes[curBC] = (byte)JBC_invokevirtual; 951 } 952 constantPool[numParams + 1] = ClassFileReader.packCPEntry(CP_MEMBER, getId()); 953 bytecodes[curBC + 1] = (byte)((numParams + 1) >>> 8); 954 bytecodes[curBC + 2] = (byte)(numParams + 1); 955 if (interfaceCall) { 956 // invokeinterface bytecodes are historically longer than others 957 curBC += 2; 958 } 959 TypeReference returnType = getReturnType(); 960 if (!returnType.isPrimitiveType() || returnType.isWordLikeType()) { 961 bytecodes[curBC + 3] = (byte)JBC_nop; 962 bytecodes[curBC + 4] = (byte)JBC_nop; 963 bytecodes[curBC + 5] = (byte)JBC_nop; 964 } else if (returnType.isVoidType()) { 965 bytecodes[curBC + 3] = (byte)JBC_aconst_null; 966 bytecodes[curBC + 4] = (byte)JBC_nop; 967 bytecodes[curBC + 5] = (byte)JBC_nop; 968 } else { 969 MemberReference boxMethod; 970 if (returnType.isBooleanType()) { 971 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 972 Atom.findOrCreateUnicodeAtom("boxAsBoolean"), 973 Atom.findOrCreateUnicodeAtom("(Z)Ljava/lang/Object;")); 974 } else if (returnType.isByteType()) { 975 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 976 Atom.findOrCreateUnicodeAtom("boxAsByte"), 977 Atom.findOrCreateUnicodeAtom("(B)Ljava/lang/Object;")); 978 } else if (returnType.isShortType()) { 979 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 980 Atom.findOrCreateUnicodeAtom("boxAsShort"), 981 Atom.findOrCreateUnicodeAtom("(S)Ljava/lang/Object;")); 982 } else if (returnType.isCharType()) { 983 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 984 Atom.findOrCreateUnicodeAtom("boxAsChar"), 985 Atom.findOrCreateUnicodeAtom("(C)Ljava/lang/Object;")); 986 } else if (returnType.isIntType()) { 987 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 988 Atom.findOrCreateUnicodeAtom("boxAsInt"), 989 Atom.findOrCreateUnicodeAtom("(I)Ljava/lang/Object;")); 990 } else if (returnType.isLongType()) { 991 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 992 Atom.findOrCreateUnicodeAtom("boxAsLong"), 993 Atom.findOrCreateUnicodeAtom("(J)Ljava/lang/Object;")); 994 } else if (returnType.isFloatType()) { 995 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 996 Atom.findOrCreateUnicodeAtom("boxAsFloat"), 997 Atom.findOrCreateUnicodeAtom("(F)Ljava/lang/Object;")); 998 } else { 999 if (VM.VerifyAssertions) VM._assert(returnType.isDoubleType()); 1000 boxMethod = MethodReference.findOrCreate(baseReflectionClass, 1001 Atom.findOrCreateUnicodeAtom("boxAsDouble"), 1002 Atom.findOrCreateUnicodeAtom("(D)Ljava/lang/Object;")); 1003 } 1004 constantPool[numParams + 2] = ClassFileReader.packCPEntry(CP_MEMBER, boxMethod.getId()); 1005 bytecodes[curBC + 3] = (byte)JBC_invokestatic; 1006 bytecodes[curBC + 4] = (byte)((numParams + 2) >>> 8); 1007 bytecodes[curBC + 5] = (byte)(numParams + 2); 1008 } 1009 bytecodes[curBC + 6] = (byte)JBC_areturn; 1010 return new NormalMethod(reflectionClass, 1011 memRef, 1012 (short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC), 1013 null, 1014 (short) 3, 1015 (short) (getParameterWords() + 2), 1016 bytecodes, 1017 null, 1018 null, 1019 null, 1020 constantPool, 1021 null, 1022 null, 1023 null, 1024 null); 1025 } 1026}