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