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