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>&lt;clinit&gt;</code> or <code>&lt;init&gt;</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>&lt;init&gt;</code> methods then it is interruptible.
576       * <li> If it is annotated with <CODE>&#064;Interruptible</CODE> it is interruptible.
577       * <li> If it is annotated with <CODE>&#064;Preemptible</CODE> it is interruptible.
578       * <li> If it is annotated with <CODE>&#064;Uninterruptible</CODE> it is not interruptible.
579       * <li> If it is annotated with <CODE>&#064;UninterruptibleNoWarn</CODE> it is not interruptible.
580       * <li> If it is annotated with <CODE>&#064;Unpreemptible</CODE> it is not interruptible.
581       * <li> If its declaring class is annotated with <CODE>&#064;Uninterruptible</CODE>
582       *      or <CODE>&#064;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    }