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            VM._assert(false);
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 <clinit> or <init> method then it is interruptible.
574       * <li> If is the synthetic 'this' method used by jikes to
575       *      factor out default initializers for <init> 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 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 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       * Return the resulting CompiledMethod object.
722       */
723      public final synchronized void compile() {
724        if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isResolved());
725        if (isCompiled()) return;
726    
727        if (VM.TraceClassLoading && VM.runningVM) VM.sysWrite("RVMMethod: (begin) compiling " + this + "\n");
728    
729        CompiledMethod cm = genCode();
730    
731        // Ensure that cm wasn't invalidated while it was being compiled.
732        synchronized (cm) {
733          if (cm.isInvalid()) {
734            CompiledMethods.setCompiledMethodObsolete(cm);
735          } else {
736            currentCompiledMethod = cm;
737          }
738        }
739    
740        if (VM.TraceClassLoading && VM.runningVM) VM.sysWrite("RVMMethod: (end)   compiling " + this + "\n");
741      }
742    
743      protected abstract CompiledMethod genCode();
744    
745      //----------------------------------------------------------------//
746      //                        Section 3.                              //
747      // The following are available after the declaring class has been //
748      // "instantiated".                                                //
749      //----------------------------------------------------------------//
750    
751      /**
752       * Change machine code that will be used by future executions of this method
753       * (ie. optimized <-> non-optimized)
754       * @param compiledMethod new machine code
755       * Side effect: updates jtoc or method dispatch tables
756       * ("type information blocks")
757       *              for this class and its subclasses
758       */
759      public final synchronized void replaceCompiledMethod(CompiledMethod compiledMethod) {
760        if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated());
761        // If we're replacing with a non-null compiledMethod, ensure that is still valid!
762        if (compiledMethod != null) {
763          synchronized (compiledMethod) {
764            if (compiledMethod.isInvalid()) return;
765          }
766        }
767    
768        // Grab version that is being replaced
769        CompiledMethod oldCompiledMethod = currentCompiledMethod;
770        currentCompiledMethod = compiledMethod;
771    
772        // Install the new method in jtoc/tib. If virtual, will also replace in
773        // all subclasses that inherited the method.
774        getDeclaringClass().updateMethod(this);
775    
776        // Replace constant-ified virtual method in JTOC if necessary
777        Offset jtocOffset = getJtocOffset();
778        if (jtocOffset.NE(Offset.zero())) {
779          Statics.setSlotContents(jtocOffset, getCurrentEntryCodeArray());
780        }
781    
782        // Now that we've updated the jtoc/tib, old version is obsolete
783        if (oldCompiledMethod != null) {
784          CompiledMethods.setCompiledMethodObsolete(oldCompiledMethod);
785        }
786      }
787    
788      /**
789       * If CM is the current compiled code for this, then invaldiate it.
790       */
791      public final synchronized void invalidateCompiledMethod(CompiledMethod cm) {
792        if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated());
793        if (currentCompiledMethod == cm) {
794          replaceCompiledMethod(null);
795        }
796      }
797    
798      /**
799       * Get the offset used to hold a JTOC addressable version of the current entry
800       * code array
801       */
802      @Pure
803      private Offset getJtocOffset()  {
804        Integer offAsInt;
805        synchronized (jtocOffsets) {
806          offAsInt = jtocOffsets.get(this);
807        }
808        if (offAsInt == null) {
809          return Offset.zero();
810        } else {
811          return Offset.fromIntSignExtend(offAsInt.intValue());
812        }
813      }
814    
815      /**
816       * Find or create a jtoc offset for this method
817       */
818      public final synchronized Offset findOrCreateJtocOffset() {
819        if (VM.VerifyAssertions) VM._assert(!isStatic() && !isObjectInitializer());
820        Offset jtocOffset = getJtocOffset();;
821        if (jtocOffset.EQ(Offset.zero())) {
822          jtocOffset = Statics.allocateReferenceSlot(true);
823          Statics.setSlotContents(jtocOffset, getCurrentEntryCodeArray());
824          synchronized(jtocOffsets) {
825            jtocOffsets.put(this, Integer.valueOf(jtocOffset.toInt()));
826          }
827        }
828        return jtocOffset;
829      }
830    
831      /**
832       * Returns the parameter annotations for this method.
833       */
834      @Pure
835      public final Annotation[][] getDeclaredParameterAnnotations() {
836        Annotation[][] result;
837        synchronized(declaredParameterAnnotations) {
838          result = declaredParameterAnnotations.get(this);
839        }
840        if (result == null) {
841          RVMAnnotation[][] parameterAnnotations = getParameterAnnotations();
842          result = new Annotation[parameterAnnotations.length][];
843          for (int a = 0; a < result.length; ++a) {
844            result[a] = toAnnotations(parameterAnnotations[a]);
845          }
846          synchronized(declaredParameterAnnotations) {
847            declaredParameterAnnotations.put(this, result);
848          }
849        }
850        return result;
851      }
852    
853      /** Map from a method to a reflective method capable of invoking it */
854      private static final ImmutableEntryHashMapRVM<RVMMethod, ReflectionBase> invokeMethods =
855        Reflection.bytecodeReflection || Reflection.cacheInvokerInJavaLangReflect ?
856          new ImmutableEntryHashMapRVM<RVMMethod, ReflectionBase>(30) : null;
857    
858      /**
859       * Get an instance of an object capable of reflectively invoking this method
860       */
861      @RuntimePure
862      @SuppressWarnings("unchecked")
863      public synchronized ReflectionBase getInvoker() {
864        if (!VM.runningVM) {
865          return null;
866        }
867        ReflectionBase invoker;
868        if (invokeMethods != null) {
869          synchronized(RVMMethod.class) {
870            invoker = invokeMethods.get(this);
871          }
872        } else {
873          invoker = null;
874        }
875        if (invoker == null) {
876          Class<ReflectionBase> reflectionClass = (Class<ReflectionBase>)RVMClass.createReflectionClass(this);
877          if (reflectionClass != null) {
878            try {
879              invoker = reflectionClass.newInstance();
880            } catch (Throwable e) {
881              throw new Error(e);
882            }
883          } else {
884            invoker = ReflectionBase.nullInvoker;
885          }
886          if (invokeMethods != null) {
887            synchronized(RVMMethod.class) {
888              invokeMethods.put(this, invoker);
889            }
890          }
891        }
892        return invoker;
893      }
894    
895      /**
896       * Create a method to act as a default constructor (just return)
897       * @param klass class for method
898       * @param memRef reference for default constructor
899       * @return method normal (bytecode containing) method that just returns
900       */
901      static RVMMethod createDefaultConstructor(TypeReference klass, MemberReference memRef) {
902        return new NormalMethod(klass,
903                                   memRef,
904                                   (short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
905                                   null,
906                                   (short) 1,
907                                   (short) 0,
908                                   new byte[]{(byte)JBC_return},
909                                   null,
910                                   null,
911                                   null,
912                                   new int[0],
913                                   null,
914                                   null,
915                                   null,
916                                   null);
917      }
918    
919      /**
920       * Create a method for reflectively invoking this method
921       *
922       * @param reflectionClass the class this method will belong to
923       * @param constantPool for the class
924       * @param memRef the member reference corresponding to this method
925       * @return the created method
926       */
927      RVMMethod createReflectionMethod(TypeReference reflectionClass, int[] constantPool,
928                                       MethodReference memRef) {
929        TypeReference[] parameters = getParameterTypes();
930        int numParams = parameters.length;
931        byte[] bytecodes;
932        boolean interfaceCall = false;
933        int curBC = 0;
934        if (!isStatic()) {
935          if (!getDeclaringClass().isInterface()) {
936            // virtual call
937            bytecodes = new byte[8 * numParams + 8];
938          } else {
939            // interface call
940            bytecodes = new byte[8 * numParams + 10];
941            interfaceCall = true;
942          }
943          bytecodes[curBC] = JBC_aload_1;
944          curBC++;
945        } else {
946          // static call
947          bytecodes = new byte[8 * numParams + 7];
948        }
949        for (int i=0; i < numParams; i++) {
950          if (parameters[i].isVoidType()) {
951            bytecodes[curBC] =
952              bytecodes[curBC+1] =
953              bytecodes[curBC+2] =
954              bytecodes[curBC+3] =
955              bytecodes[curBC+4] =
956              bytecodes[curBC+5] =
957              bytecodes[curBC+6] =
958              bytecodes[curBC+7] =
959                (byte)JBC_nop;
960            continue;
961          }
962          bytecodes[curBC] = (byte)JBC_aload_2;
963          bytecodes[curBC+1] = (byte)JBC_sipush;
964          bytecodes[curBC+2] = (byte)(i >>> 8);
965          bytecodes[curBC+3] = (byte)i;
966          bytecodes[curBC+4] = (byte)JBC_aaload;
967          if (!parameters[i].isPrimitiveType()) {
968            bytecodes[curBC+5] = (byte)JBC_checkcast;
969            if (VM.VerifyAssertions) VM._assert(parameters[i].getId() != 0);
970            constantPool[i+1] = ClassFileReader.packCPEntry(CP_CLASS, parameters[i].getId());
971            bytecodes[curBC+6] = (byte)((i+1) >>> 8);
972            bytecodes[curBC+7] = (byte)(i+1);
973          } else if (parameters[i].isWordLikeType()) {
974            bytecodes[curBC+5] =
975              bytecodes[curBC+6] =
976              bytecodes[curBC+7] =
977                (byte)JBC_nop;
978          } else {
979            bytecodes[curBC+5] = (byte)JBC_invokestatic;
980            MemberReference unboxMethod;
981            if (parameters[i].isBooleanType()) {
982              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
983                                                            Atom.findOrCreateUnicodeAtom("unboxAsBoolean"),
984                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)Z"));
985            } else if (parameters[i].isByteType()) {
986              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
987                                                            Atom.findOrCreateUnicodeAtom("unboxAsByte"),
988                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)B"));
989            } else if (parameters[i].isShortType()) {
990              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
991                                                            Atom.findOrCreateUnicodeAtom("unboxAsShort"),
992                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)S"));
993            } else if (parameters[i].isCharType()) {
994              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
995                                                            Atom.findOrCreateUnicodeAtom("unboxAsChar"),
996                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)C"));
997            } else if (parameters[i].isIntType()) {
998              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
999                                                            Atom.findOrCreateUnicodeAtom("unboxAsInt"),
1000                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)I"));
1001            } else if (parameters[i].isLongType()) {
1002              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
1003                                                            Atom.findOrCreateUnicodeAtom("unboxAsLong"),
1004                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)J"));
1005            } else if (parameters[i].isFloatType()) {
1006              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
1007                                                            Atom.findOrCreateUnicodeAtom("unboxAsFloat"),
1008                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)F"));
1009            } else {
1010              if (VM.VerifyAssertions) VM._assert(parameters[i].isDoubleType());
1011              unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
1012                                                            Atom.findOrCreateUnicodeAtom("unboxAsDouble"),
1013                                                            Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)D"));
1014            }
1015            constantPool[i+1] = ClassFileReader.packCPEntry(CP_MEMBER, unboxMethod.getId());
1016            bytecodes[curBC+6] = (byte)((i+1) >>> 8);
1017            bytecodes[curBC+7] = (byte)(i+1);
1018          }
1019          curBC+=8;
1020        }
1021        if (isStatic()) {
1022          bytecodes[curBC] = (byte)JBC_invokestatic;
1023        } else if (isObjectInitializer() || isPrivate()) {
1024          bytecodes[curBC] = (byte)JBC_invokespecial;
1025        } else if (interfaceCall) {
1026          bytecodes[curBC] = (byte)JBC_invokeinterface;
1027        } else {
1028          bytecodes[curBC] = (byte)JBC_invokevirtual;
1029        }
1030        constantPool[numParams+1] = ClassFileReader.packCPEntry(CP_MEMBER, getId());
1031        bytecodes[curBC+1] = (byte)((numParams+1) >>> 8);
1032        bytecodes[curBC+2] = (byte)(numParams+1);
1033        if (interfaceCall) {
1034          // invokeinterface bytecodes are historically longer than others
1035          curBC+=2;
1036        }
1037        TypeReference returnType = getReturnType();
1038        if (!returnType.isPrimitiveType() || returnType.isWordLikeType()) {
1039          bytecodes[curBC+3] = (byte)JBC_nop;
1040          bytecodes[curBC+4] = (byte)JBC_nop;
1041          bytecodes[curBC+5] = (byte)JBC_nop;
1042        } else if (returnType.isVoidType()) {
1043          bytecodes[curBC+3] = (byte)JBC_aconst_null;
1044          bytecodes[curBC+4] = (byte)JBC_nop;
1045          bytecodes[curBC+5] = (byte)JBC_nop;
1046        } else {
1047          MemberReference boxMethod;
1048          if (returnType.isBooleanType()) {
1049            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1050                                                        Atom.findOrCreateUnicodeAtom("boxAsBoolean"),
1051                                                        Atom.findOrCreateUnicodeAtom("(Z)Ljava/lang/Object;"));
1052          } else if (returnType.isByteType()) {
1053            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1054                                                        Atom.findOrCreateUnicodeAtom("boxAsByte"),
1055                                                        Atom.findOrCreateUnicodeAtom("(B)Ljava/lang/Object;"));
1056          } else if (returnType.isShortType()) {
1057            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1058                                                        Atom.findOrCreateUnicodeAtom("boxAsShort"),
1059                                                        Atom.findOrCreateUnicodeAtom("(S)Ljava/lang/Object;"));
1060          } else if (returnType.isCharType()) {
1061            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1062                                                        Atom.findOrCreateUnicodeAtom("boxAsChar"),
1063                                                        Atom.findOrCreateUnicodeAtom("(C)Ljava/lang/Object;"));
1064          } else if (returnType.isIntType()) {
1065            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1066                                                        Atom.findOrCreateUnicodeAtom("boxAsInt"),
1067                                                        Atom.findOrCreateUnicodeAtom("(I)Ljava/lang/Object;"));
1068          } else if (returnType.isLongType()) {
1069            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1070                                                        Atom.findOrCreateUnicodeAtom("boxAsLong"),
1071                                                        Atom.findOrCreateUnicodeAtom("(J)Ljava/lang/Object;"));
1072          } else if (returnType.isFloatType()) {
1073            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1074                                                        Atom.findOrCreateUnicodeAtom("boxAsFloat"),
1075                                                        Atom.findOrCreateUnicodeAtom("(F)Ljava/lang/Object;"));
1076          } else {
1077            if (VM.VerifyAssertions) VM._assert(returnType.isDoubleType());
1078            boxMethod = MethodReference.findOrCreate(baseReflectionClass,
1079                                                        Atom.findOrCreateUnicodeAtom("boxAsDouble"),
1080                                                        Atom.findOrCreateUnicodeAtom("(D)Ljava/lang/Object;"));
1081          }
1082          constantPool[numParams+2] = ClassFileReader.packCPEntry(CP_MEMBER, boxMethod.getId());
1083          bytecodes[curBC+3] = (byte)JBC_invokestatic;
1084          bytecodes[curBC+4] = (byte)((numParams+2) >>> 8);
1085          bytecodes[curBC+5] = (byte)(numParams+2);
1086        }
1087        bytecodes[curBC+6] = (byte)JBC_areturn;
1088        return new NormalMethod(reflectionClass,
1089                                   memRef,
1090                                   (short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
1091                                   null,
1092                                   (short) 3,
1093                                   (short) (getParameterWords() + 2),
1094                                   bytecodes,
1095                                   null,
1096                                   null,
1097                                   null,
1098                                   constantPool,
1099                                   null,
1100                                   null,
1101                                   null,
1102                                   null);
1103      }
1104    }