/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.classloader;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import org.jikesrvm.ArchitectureSpecific.CodeArray;
import org.jikesrvm.ArchitectureSpecific.LazyCompilationTrampoline;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.runtime.Entrypoints;
import org.jikesrvm.runtime.Reflection;
import org.jikesrvm.runtime.ReflectionBase;
import org.jikesrvm.runtime.Statics;
import org.jikesrvm.util.HashMapRVM;
import org.jikesrvm.util.ImmutableEntryHashMapRVM;
import org.vmmagic.pragma.Pure;
import org.vmmagic.pragma.RuntimePure;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.pragma.Unpreemptible;
import org.vmmagic.unboxed.Offset;
import static org.jikesrvm.classloader.TypeReference.baseReflectionClass;
/**
* A method of a java class corresponding to a method_info structure
* in the class file. A method is read from a class file using the
* {@link #readMethod} method.
*/
public abstract class RVMMethod extends RVMMember implements BytecodeConstants {
/**
* current compiled method for this method
*/
protected CompiledMethod currentCompiledMethod;
/**
* exceptions this method might throw (null --> none)
*/
private static final ImmutableEntryHashMapRVM<RVMMethod, TypeReference[]> exceptionTypes =
new ImmutableEntryHashMapRVM<RVMMethod, TypeReference[]>();
/**
* Method parameter annotations from the class file that are
* described as runtime visible. These annotations are available to
* the reflection API.
*/
private static final ImmutableEntryHashMapRVM<RVMMethod, RVMAnnotation[][]> parameterAnnotations =
new ImmutableEntryHashMapRVM<RVMMethod, RVMAnnotation[][]>();
/**
* A table mapping to values present in the method info tables of annotation
* types. It represents the default result from an annotation method.
*/
private static final HashMapRVM<RVMMethod, Object> annotationDefaults =
new HashMapRVM<RVMMethod, Object>();
/**
* The offsets of virtual methods in the jtoc, if it's been placed
* there by constant propagation.
*/
private static final ImmutableEntryHashMapRVM<RVMMethod, Integer> jtocOffsets =
new ImmutableEntryHashMapRVM<RVMMethod, Integer>();
/** Cache of arrays of declared parameter annotations. */
private static final ImmutableEntryHashMapRVM<RVMMethod, Annotation[][]> declaredParameterAnnotations =
new ImmutableEntryHashMapRVM<RVMMethod, Annotation[][]>();
/**
* Construct a read method
*
* @param declaringClass the RVMClass object of the class that declared this field
* @param memRef the canonical memberReference for this method.
* @param modifiers modifiers associated with this method.
* @param exceptionTypes exceptions thrown by this method.
* @param signature generic type of this method.
* @param annotations array of runtime visible annotations
* @param parameterAnnotations array of runtime visible parameter annotations
* @param annotationDefault value for this annotation that appears
*/
protected RVMMethod(TypeReference declaringClass, MemberReference memRef, short modifiers,
TypeReference[] exceptionTypes, Atom signature, RVMAnnotation[] annotations,
RVMAnnotation[][] parameterAnnotations, Object annotationDefault) {
super(declaringClass, memRef, (short) (modifiers & APPLICABLE_TO_METHODS), signature, annotations);
if (parameterAnnotations != null) {
synchronized(RVMMethod.parameterAnnotations) {
RVMMethod.parameterAnnotations.put(this, parameterAnnotations);
}
}
if (exceptionTypes != null) {
synchronized(RVMMethod.exceptionTypes) {
RVMMethod.exceptionTypes.put(this, exceptionTypes);
}
}
if (annotationDefault != null) {
synchronized(annotationDefaults) {
annotationDefaults.put(this, annotationDefault);
}
}
}
/**
* Get the parameter annotations for this method
*/
@Pure
private RVMAnnotation[][] getParameterAnnotations() {
synchronized(parameterAnnotations) {
return parameterAnnotations.get(this);
}
}
/**
* Get the annotation default value for an annotation method
*/
@Pure
public Object getAnnotationDefault() {
synchronized(annotationDefaults) {
Object value = annotationDefaults.get(this);
if (value instanceof TypeReference || value instanceof Object[]) {
value = RVMAnnotation.firstUse(value);
annotationDefaults.put(this, value);
}
return value;
}
}
/**
* Called from {@link ClassFileReader#readClass(TypeReference,DataInputStream)} to create an
* instance of a RVMMethod by reading the relevant data from the argument bytecode stream.
*
* @param declaringClass the TypeReference of the class being loaded
* @param constantPool the constantPool of the RVMClass object that's being constructed
* @param memRef the canonical memberReference for this member.
* @param modifiers modifiers associated with this member.
* @param input the DataInputStream to read the method's attributes from
*/
static RVMMethod readMethod(TypeReference declaringClass, int[] constantPool, MemberReference memRef,
short modifiers, DataInputStream input) throws IOException {
short tmp_localWords = 0;
short tmp_operandWords = 0;
byte[] tmp_bytecodes = null;
ExceptionHandlerMap tmp_exceptionHandlerMap = null;
TypeReference[] tmp_exceptionTypes = null;
int[] tmp_lineNumberMap = null;
LocalVariableTable tmp_localVariableTable = null;
Atom tmp_signature = null;
RVMAnnotation[] annotations = null;
RVMAnnotation[][] parameterAnnotations = null;
Object tmp_annotationDefault = null;
// Read the attributes
for (int i = 0, n = input.readUnsignedShort(); i < n; i++) {
Atom attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
int attLength = input.readInt();
// Only bother to interpret non-boring Method attributes
if (attName == RVMClassLoader.codeAttributeName) {
tmp_operandWords = input.readShort();
tmp_localWords = input.readShort();
tmp_bytecodes = new byte[input.readInt()];
input.readFully(tmp_bytecodes);
tmp_exceptionHandlerMap = ExceptionHandlerMap.readExceptionHandlerMap(input, constantPool);
// Read the attributes portion of the code attribute
for (int j = 0, n2 = input.readUnsignedShort(); j < n2; j++) {
attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
attLength = input.readInt();
if (attName == RVMClassLoader.lineNumberTableAttributeName) {
int cnt = input.readUnsignedShort();
if (cnt != 0) {
tmp_lineNumberMap = new int[cnt];
for (int k = 0; k < cnt; k++) {
int startPC = input.readUnsignedShort();
int lineNumber = input.readUnsignedShort();
tmp_lineNumberMap[k] = (lineNumber << BITS_IN_SHORT) | startPC;
}
}
} else if (attName == RVMClassLoader.localVariableTableAttributeName) {
tmp_localVariableTable = LocalVariableTable.readLocalVariableTable(input, constantPool);
} else {
// All other entries in the attribute portion of the code attribute are boring.
int skippedAmount = input.skipBytes(attLength);
if (skippedAmount != attLength) {
throw new IOException("Unexpected short skip");
}
}
}
} else if (attName == RVMClassLoader.exceptionsAttributeName) {
int cnt = input.readUnsignedShort();
if (cnt != 0) {
tmp_exceptionTypes = new TypeReference[cnt];
for (int j = 0, m = tmp_exceptionTypes.length; j < m; ++j) {
tmp_exceptionTypes[j] = ClassFileReader.getTypeRef(constantPool, input.readUnsignedShort());
}
}
} else if (attName == RVMClassLoader.syntheticAttributeName) {
modifiers |= ACC_SYNTHETIC;
} else if (attName == RVMClassLoader.signatureAttributeName) {
tmp_signature = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
} else if (attName == RVMClassLoader.runtimeVisibleAnnotationsAttributeName) {
annotations = AnnotatedElement.readAnnotations(constantPool, input, declaringClass.getClassLoader());
} else if (attName == RVMClassLoader.runtimeVisibleParameterAnnotationsAttributeName) {
int numParameters = input.readByte() & 0xFF;
parameterAnnotations = new RVMAnnotation[numParameters][];
for (int a = 0; a < numParameters; ++a) {
parameterAnnotations[a] = AnnotatedElement.readAnnotations(constantPool, input, declaringClass.getClassLoader());
}
} else if (attName == RVMClassLoader.annotationDefaultAttributeName) {
try {
tmp_annotationDefault = RVMAnnotation.readValue(memRef.asMethodReference().getReturnType(), constantPool, input, declaringClass.getClassLoader());
} catch (ClassNotFoundException e) {
throw new Error(e);
}
} else {
// all other method attributes are boring
int skippedAmount = input.skipBytes(attLength);
if (skippedAmount != attLength) {
throw new IOException("Unexpected short skip");
}
}
}
RVMMethod method;
if ((modifiers & ACC_NATIVE) != 0) {
method =
new NativeMethod(declaringClass,
memRef,
modifiers,
tmp_exceptionTypes,
tmp_signature,
annotations,
parameterAnnotations,
tmp_annotationDefault);
} else if ((modifiers & ACC_ABSTRACT) != 0) {
method =
new AbstractMethod(declaringClass,
memRef,
modifiers,
tmp_exceptionTypes,
tmp_signature,
annotations,
parameterAnnotations,
tmp_annotationDefault);
} else {
method =
new NormalMethod(declaringClass,
memRef,
modifiers,
tmp_exceptionTypes,
tmp_localWords,
tmp_operandWords,
tmp_bytecodes,
tmp_exceptionHandlerMap,
tmp_lineNumberMap,
tmp_localVariableTable,
constantPool,
tmp_signature,
annotations,
parameterAnnotations,
tmp_annotationDefault);
}
return method;
}
/**
* Create a copy of the method that occurs in the annotation
* interface. The method body will contain a read of the field at
* the constant pool index specified.
*
* @param annotationClass the class this method belongs to
* @param constantPool for the class
* @param memRef the member reference corresponding to this method
* @param interfaceMethod the interface method that will copied to
* produce the annotation method
* @param constantPoolIndex the index of the field that will be
* returned by this method
* @return the created method
*/
static RVMMethod createAnnotationMethod(TypeReference annotationClass, int[] constantPool,
MemberReference memRef, RVMMethod interfaceMethod,
int constantPoolIndex) {
byte[] bytecodes =
new byte[]{
(byte) JBC_aload_0,
(byte) JBC_getfield, (byte) (constantPoolIndex >>> 8), (byte) constantPoolIndex,
// Xreturn
(byte) typeRefToReturnBytecode(interfaceMethod.getReturnType())};
return new NormalMethod(annotationClass,
memRef,
(short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
null,
(short) 1,
(short) 2,
bytecodes,
null,
null,
null,
constantPool,
null,
null,
null,
null);
}
/**
* Create a method to initialise the annotation class
*
* @param aClass the class this method belongs to
* @param constantPool for the class
* @param memRef the member reference corresponding to this method
* @param objectInitIndex an index into the constant pool for a
* method reference to java.lang.Object.<init>
* @param aFields
* @param aMethods
* @return the created method
*/
static RVMMethod createAnnotationInit(TypeReference aClass, int[] constantPool, MemberReference memRef,
int objectInitIndex, RVMField[] aFields, RVMMethod[] aMethods,
int[] defaultConstants) {
byte[] bytecode = new byte[6 + (defaultConstants.length * 7)];
bytecode[0] = (byte) JBC_aload_0; // stack[0] = this
bytecode[1] = (byte) JBC_aload_1; // stack[1] = instanceof RVMAnnotation
bytecode[2] = (byte) JBC_invokespecial;
bytecode[3] = (byte) (objectInitIndex >>> 8);
bytecode[4] = (byte) objectInitIndex;
for (int i = 0, j = 0; i < aMethods.length; i++) {
Object value = aMethods[i].getAnnotationDefault();
if (value != null) {
bytecode[(j * 7) + 5 + 0] = (byte) JBC_aload_0; // stack[0] = this
byte literalType = ClassFileReader.getLiteralDescription(constantPool, defaultConstants[j]);
if (literalType != CP_LONG && literalType != CP_DOUBLE) {
bytecode[(j * 7) + 5 + 1] = (byte) JBC_ldc_w; // stack[1] = value
} else {
bytecode[(j * 7) + 5 + 1] = (byte) JBC_ldc2_w;// stack[1&2] = value
}
bytecode[(j * 7) + 5 + 2] = (byte) (defaultConstants[j] >>> 8);
bytecode[(j * 7) + 5 + 3] = (byte) defaultConstants[j];
bytecode[(j * 7) + 5 + 4] = (byte) JBC_putfield;
bytecode[(j * 7) + 5 + 5] = (byte) (i >>> 8);
bytecode[(j * 7) + 5 + 6] = (byte) i;
j++;
}
}
bytecode[bytecode.length - 1] = (byte) JBC_return;
return new NormalMethod(aClass,
memRef,
(short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
null,
(short) 2,
(short) 3,
bytecode,
null,
null,
null,
constantPool,
null,
null,
null,
null);
}
/**
* What would be the appropriate return bytecode for the given type
* reference?
*/
private static int typeRefToReturnBytecode(TypeReference tr) {
if (!tr.isPrimitiveType()) {
return JBC_areturn;
} else {
Primitive pt = (Primitive) tr.peekType();
if ((pt == RVMType.BooleanType) ||
(pt == RVMType.ByteType) ||
(pt == RVMType.ShortType) ||
(pt == RVMType.CharType) ||
(pt == RVMType.IntType)) {
return JBC_ireturn;
} else if (pt == RVMType.LongType) {
return JBC_lreturn;
} else if (pt == RVMType.FloatType) {
return JBC_freturn;
} else if (pt == RVMType.DoubleType) {
return JBC_dreturn;
} else {
VM._assert(false);
return -1;
}
}
}
/**
* Is this method a class initializer?
*/
@Uninterruptible
public final boolean isClassInitializer() {
return getName() == RVMClassLoader.StandardClassInitializerMethodName;
}
/**
* Is this method an object initializer?
*/
@Uninterruptible
public final boolean isObjectInitializer() {
return getName() == RVMClassLoader.StandardObjectInitializerMethodName;
}
/**
* Is this method a compiler-generated object initializer helper?
*/
@Uninterruptible
public final boolean isObjectInitializerHelper() {
return getName() == RVMClassLoader.StandardObjectInitializerHelperMethodName;
}
/**
* Type of this method's return value.
*/
@Uninterruptible
public final TypeReference getReturnType() {
return memRef.asMethodReference().getReturnType();
}
/**
* Type of this method's parameters.
* Note: does *not* include implicit "this" parameter, if any.
*/
@Uninterruptible
public final TypeReference[] getParameterTypes() {
return memRef.asMethodReference().getParameterTypes();
}
/**
* Space required by this method for its parameters, in words.
* Note: does *not* include implicit "this" parameter, if any.
*/
@Uninterruptible
public final int getParameterWords() {
return memRef.asMethodReference().getParameterWords();
}
/**
* Has machine code been generated for this method's bytecodes?
*/
public final boolean isCompiled() {
return currentCompiledMethod != null;
}
/**
* Get the current compiled method for this method.
* Will return null if there is no current compiled method!
*
* We make this method Unpreemptible to avoid a race-condition
* in Reflection.invoke.
* @return compiled method
*/
@Unpreemptible
public final synchronized CompiledMethod getCurrentCompiledMethod() {
return currentCompiledMethod;
}
/**
* Declared as statically dispatched?
*/
@Uninterruptible
public final boolean isStatic() {
return (modifiers & ACC_STATIC) != 0;
}
/**
* Declared as non-overridable by subclasses?
*/
@Uninterruptible
public final boolean isFinal() {
return (modifiers & ACC_FINAL) != 0;
}
/**
* Guarded by monitorenter/monitorexit?
*/
@Uninterruptible
public final boolean isSynchronized() {
return (modifiers & ACC_SYNCHRONIZED) != 0;
}
/**
* Not implemented in java?
*/
@Uninterruptible
public final boolean isNative() {
return (modifiers & ACC_NATIVE) != 0;
}
/**
* Strict enforcement of IEEE 754 rules?
*/
public final boolean isStrictFP() {
return (modifiers & ACC_STRICT) != 0;
}
/**
* Not implemented in Java and use C not JNI calling convention
*/
public final boolean isSysCall() {
return isNative() && isStatic() && isAnnotationDeclared(TypeReference.SysCall);
}
/**
* Not implemented in Java and use C not JNI calling convention
*/
public final boolean isSpecializedInvoke() {
return isAnnotationDeclared(TypeReference.SpecializedMethodInvoke);
}
/**
* Implemented in subclass?
*/
@Uninterruptible
public final boolean isAbstract() {
return (modifiers & ACC_ABSTRACT) != 0;
}
/**
* Not present in source code file?
*/
public boolean isSynthetic() {
return (modifiers & ACC_SYNTHETIC) != 0;
}
/**
* Is this method a bridge method? Bridge methods are generated in some cases
* of generics and inheritance.
*/
public boolean isBridge() {
return (modifiers & BRIDGE) != 0;
}
/**
* Is this a varargs method taking a variable number of arguments?
*/
public boolean isVarArgs() {
return (modifiers & VARARGS) != 0;
}
/**
* Exceptions thrown by this method -
* something like { "java/lang/IOException", "java/lang/EOFException" }
* @return info (null --> method doesn't throw any exceptions)
*/
@Pure
public final TypeReference[] getExceptionTypes() {
synchronized(exceptionTypes) {
return exceptionTypes.get(this);
}
}
/**
* Is this method interruptible?
* In other words, should the compiler insert yieldpoints
* in method prologue, epilogue, and backwards branches.
* Also, only methods that are Interruptible have stackoverflow checks
* in the method prologue (since there is no mechanism for handling a stackoverflow
* that doesn't violate the uninterruptiblity of the method).
* To determine if a method is interruptible, the following conditions
* are checked (<em>in order</em>):
* <ul>
* <li> If it is a <clinit> or <init> method then it is interruptible.
* <li> If is the synthetic 'this' method used by jikes to
* factor out default initializers for <init> methods then it is interruptible.
* <li> If it is annotated with <CODE>Interruptible</CODE> it is interruptible.
* <li> If it is annotated with <CODE>Preemptible</CODE> it is interruptible.
* <li> If it is annotated with <CODE>Uninterruptible</CODE> it is not interruptible.
* <li> If it is annotated with <CODE>UninterruptibleNoWarn</CODE> it is not interruptible.
* <li> If it is annotated with <CODE>Unpreemptible</CODE> it is not interruptible.
* <li> If its declaring class is annotated with <CODE>Uninterruptible</CODE>
* or <CODE>Unpreemptible</CODE> it is not interruptible.
* </ul>
*/
public final boolean isInterruptible() {
if (isClassInitializer() || isObjectInitializer()) return true;
if (isObjectInitializerHelper()) return true;
if (hasInterruptibleAnnotation()) return true;
if (hasPreemptibleAnnotation()) return true;
if (hasUninterruptibleNoWarnAnnotation()) return false;
if (hasUninterruptibleAnnotation()) return false;
if (hasUnpreemptibleAnnotation()) return false;
if (hasUnpreemptibleNoWarnAnnotation()) return false;
if (getDeclaringClass().hasUnpreemptibleAnnotation()) return false;
return !getDeclaringClass().hasUninterruptibleAnnotation();
}
/**
* Is the method Unpreemptible? See the comment in {@link #isInterruptible}
*/
public final boolean isUnpreemptible() {
if (isClassInitializer() || isObjectInitializer()) return false;
if (isObjectInitializerHelper()) return false;
if (hasInterruptibleAnnotation()) return false;
if (hasPreemptibleAnnotation()) return false;
if (hasUninterruptibleAnnotation()) return false;
if (hasUninterruptibleNoWarnAnnotation()) return false;
if (hasUnpreemptibleAnnotation()) return true;
if (hasUnpreemptibleNoWarnAnnotation()) return true;
return getDeclaringClass().hasUnpreemptibleAnnotation();
}
/**
* Is the method Uninterruptible? See the comment in {@link #isInterruptible}
*/
public final boolean isUninterruptible() {
if (isClassInitializer() || isObjectInitializer()) return false;
if (isObjectInitializerHelper()) return false;
if (hasInterruptibleAnnotation()) return false;
if (hasPreemptibleAnnotation()) return false;
if (hasUnpreemptibleAnnotation()) return false;
if (hasUnpreemptibleNoWarnAnnotation()) return false;
if (hasUninterruptibleAnnotation()) return true;
if (hasUninterruptibleNoWarnAnnotation()) return true;
return getDeclaringClass().hasUninterruptibleAnnotation();
}
/**
* Is the method Pure? That is would it, without any side effects, return the
* same value given the same arguments?
*
* @return whether the method has a pure annotation
*/
public final boolean isPure() {
return hasPureAnnotation() || hasRuntimePureAnnotation();
}
/**
* Is the method RuntimePure? This is the same as Pure at runtime but has a
* special return value at boot image writing time
*
* @return whether the method has a pure annotation
*/
public final boolean isRuntimePure() {
return hasRuntimePureAnnotation();
}
/**
* Has this method been marked as forbidden to inline?
* ie., it is marked with the <CODE>NoInline</CODE> annotation or
* the <CODE>NoOptCompile</CODE> annotation?
*/
public final boolean hasNoInlinePragma() {
return (hasNoInlineAnnotation() || hasNoOptCompileAnnotation());
}
/**
* @return true if the method may write to a given field
*/
public boolean mayWrite(RVMField field) {
return true; // be conservative. native methods can write to anything
}
/**
* @return true if the method is the implementation of a runtime service
* that is called "under the covers" from the generated code and thus is not subject to
* inlining via the normal mechanisms.
*/
public boolean isRuntimeServiceMethod() {
return false; // only NormalMethods can be runtime service impls in Jikes RVM and they override this method
}
/**
* Should all allocation from this method go to a non-moving space?
*/
public boolean isNonMovingAllocation() {
return hasNonMovingAllocationAnnotation();
}
//------------------------------------------------------------------//
// Section 2. //
// The following are available after the declaring class has been //
// "resolved". //
//------------------------------------------------------------------//
/**
* Get the code array that corresponds to the entry point (prologue) for the method.
*/
public final synchronized CodeArray getCurrentEntryCodeArray() {
RVMClass declaringClass = getDeclaringClass();
if (VM.VerifyAssertions) VM._assert(declaringClass.isResolved());
if (isCompiled()) {
return currentCompiledMethod.getEntryCodeArray();
} else if (!VM.writingBootImage || isNative()) {
if (!isStatic() && !isObjectInitializer() && !isPrivate()) {
// A non-private virtual method.
if (declaringClass.isJavaLangObjectType() ||
declaringClass.getSuperClass().findVirtualMethod(getName(), getDescriptor()) == null) {
// The root method of a virtual method family can use the lazy method invoker directly.
return Entrypoints.lazyMethodInvokerMethod.getCurrentEntryCodeArray();
} else {
// All other virtual methods in the family must use unique stubs to
// ensure correct operation of the method test (guarded inlining of virtual calls).
// It is TIBs job to marshall between the actual trampoline and this marker.
return LazyCompilationTrampoline.instructions;
}
} else {
// We'll never do a method test against this method.
// Therefore we can use the lazy method invoker directly.
return Entrypoints.lazyMethodInvokerMethod.getCurrentEntryCodeArray();
}
} else {
compile();
return currentCompiledMethod.getEntryCodeArray();
}
}
/**
* Generate machine code for this method if valid
* machine code doesn't already exist.
* Return the resulting CompiledMethod object.
*/
public final synchronized void compile() {
if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isResolved());
if (isCompiled()) return;
if (VM.TraceClassLoading && VM.runningVM) VM.sysWrite("RVMMethod: (begin) compiling " + this + "\n");
CompiledMethod cm = genCode();
// Ensure that cm wasn't invalidated while it was being compiled.
synchronized (cm) {
if (cm.isInvalid()) {
CompiledMethods.setCompiledMethodObsolete(cm);
} else {
currentCompiledMethod = cm;
}
}
if (VM.TraceClassLoading && VM.runningVM) VM.sysWrite("RVMMethod: (end) compiling " + this + "\n");
}
protected abstract CompiledMethod genCode();
//----------------------------------------------------------------//
// Section 3. //
// The following are available after the declaring class has been //
// "instantiated". //
//----------------------------------------------------------------//
/**
* Change machine code that will be used by future executions of this method
* (ie. optimized <-> non-optimized)
* @param compiledMethod new machine code
* Side effect: updates jtoc or method dispatch tables
* ("type information blocks")
* for this class and its subclasses
*/
public final synchronized void replaceCompiledMethod(CompiledMethod compiledMethod) {
if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated());
// If we're replacing with a non-null compiledMethod, ensure that is still valid!
if (compiledMethod != null) {
synchronized (compiledMethod) {
if (compiledMethod.isInvalid()) return;
}
}
// Grab version that is being replaced
CompiledMethod oldCompiledMethod = currentCompiledMethod;
currentCompiledMethod = compiledMethod;
// Install the new method in jtoc/tib. If virtual, will also replace in
// all subclasses that inherited the method.
getDeclaringClass().updateMethod(this);
// Replace constant-ified virtual method in JTOC if necessary
Offset jtocOffset = getJtocOffset();
if (jtocOffset.NE(Offset.zero())) {
Statics.setSlotContents(jtocOffset, getCurrentEntryCodeArray());
}
// Now that we've updated the jtoc/tib, old version is obsolete
if (oldCompiledMethod != null) {
CompiledMethods.setCompiledMethodObsolete(oldCompiledMethod);
}
}
/**
* If CM is the current compiled code for this, then invaldiate it.
*/
public final synchronized void invalidateCompiledMethod(CompiledMethod cm) {
if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated());
if (currentCompiledMethod == cm) {
replaceCompiledMethod(null);
}
}
/**
* Get the offset used to hold a JTOC addressable version of the current entry
* code array
*/
@Pure
private Offset getJtocOffset() {
Integer offAsInt;
synchronized (jtocOffsets) {
offAsInt = jtocOffsets.get(this);
}
if (offAsInt == null) {
return Offset.zero();
} else {
return Offset.fromIntSignExtend(offAsInt.intValue());
}
}
/**
* Find or create a jtoc offset for this method
*/
public final synchronized Offset findOrCreateJtocOffset() {
if (VM.VerifyAssertions) VM._assert(!isStatic() && !isObjectInitializer());
Offset jtocOffset = getJtocOffset();;
if (jtocOffset.EQ(Offset.zero())) {
jtocOffset = Statics.allocateReferenceSlot(true);
Statics.setSlotContents(jtocOffset, getCurrentEntryCodeArray());
synchronized(jtocOffsets) {
jtocOffsets.put(this, Integer.valueOf(jtocOffset.toInt()));
}
}
return jtocOffset;
}
/**
* Returns the parameter annotations for this method.
*/
@Pure
public final Annotation[][] getDeclaredParameterAnnotations() {
Annotation[][] result;
synchronized(declaredParameterAnnotations) {
result = declaredParameterAnnotations.get(this);
}
if (result == null) {
RVMAnnotation[][] parameterAnnotations = getParameterAnnotations();
result = new Annotation[parameterAnnotations.length][];
for (int a = 0; a < result.length; ++a) {
result[a] = toAnnotations(parameterAnnotations[a]);
}
synchronized(declaredParameterAnnotations) {
declaredParameterAnnotations.put(this, result);
}
}
return result;
}
/** Map from a method to a reflective method capable of invoking it */
private static final ImmutableEntryHashMapRVM<RVMMethod, ReflectionBase> invokeMethods =
Reflection.bytecodeReflection || Reflection.cacheInvokerInJavaLangReflect ?
new ImmutableEntryHashMapRVM<RVMMethod, ReflectionBase>(30) : null;
/**
* Get an instance of an object capable of reflectively invoking this method
*/
@RuntimePure
@SuppressWarnings("unchecked")
public synchronized ReflectionBase getInvoker() {
if (!VM.runningVM) {
return null;
}
ReflectionBase invoker;
if (invokeMethods != null) {
synchronized(RVMMethod.class) {
invoker = invokeMethods.get(this);
}
} else {
invoker = null;
}
if (invoker == null) {
Class<ReflectionBase> reflectionClass = (Class<ReflectionBase>)RVMClass.createReflectionClass(this);
if (reflectionClass != null) {
try {
invoker = reflectionClass.newInstance();
} catch (Throwable e) {
throw new Error(e);
}
} else {
invoker = ReflectionBase.nullInvoker;
}
if (invokeMethods != null) {
synchronized(RVMMethod.class) {
invokeMethods.put(this, invoker);
}
}
}
return invoker;
}
/**
* Create a method to act as a default constructor (just return)
* @param klass class for method
* @param memRef reference for default constructor
* @return method normal (bytecode containing) method that just returns
*/
static RVMMethod createDefaultConstructor(TypeReference klass, MemberReference memRef) {
return new NormalMethod(klass,
memRef,
(short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
null,
(short) 1,
(short) 0,
new byte[]{(byte)JBC_return},
null,
null,
null,
new int[0],
null,
null,
null,
null);
}
/**
* Create a method for reflectively invoking this method
*
* @param reflectionClass the class this method will belong to
* @param constantPool for the class
* @param memRef the member reference corresponding to this method
* @return the created method
*/
RVMMethod createReflectionMethod(TypeReference reflectionClass, int[] constantPool,
MethodReference memRef) {
TypeReference[] parameters = getParameterTypes();
int numParams = parameters.length;
byte[] bytecodes;
boolean interfaceCall = false;
int curBC = 0;
if (!isStatic()) {
if (!getDeclaringClass().isInterface()) {
// virtual call
bytecodes = new byte[8 * numParams + 8];
} else {
// interface call
bytecodes = new byte[8 * numParams + 10];
interfaceCall = true;
}
bytecodes[curBC] = JBC_aload_1;
curBC++;
} else {
// static call
bytecodes = new byte[8 * numParams + 7];
}
for (int i=0; i < numParams; i++) {
if (parameters[i].isVoidType()) {
bytecodes[curBC] =
bytecodes[curBC+1] =
bytecodes[curBC+2] =
bytecodes[curBC+3] =
bytecodes[curBC+4] =
bytecodes[curBC+5] =
bytecodes[curBC+6] =
bytecodes[curBC+7] =
(byte)JBC_nop;
continue;
}
bytecodes[curBC] = (byte)JBC_aload_2;
bytecodes[curBC+1] = (byte)JBC_sipush;
bytecodes[curBC+2] = (byte)(i >>> 8);
bytecodes[curBC+3] = (byte)i;
bytecodes[curBC+4] = (byte)JBC_aaload;
if (!parameters[i].isPrimitiveType()) {
bytecodes[curBC+5] = (byte)JBC_checkcast;
if (VM.VerifyAssertions) VM._assert(parameters[i].getId() != 0);
constantPool[i+1] = ClassFileReader.packCPEntry(CP_CLASS, parameters[i].getId());
bytecodes[curBC+6] = (byte)((i+1) >>> 8);
bytecodes[curBC+7] = (byte)(i+1);
} else if (parameters[i].isWordLikeType()) {
bytecodes[curBC+5] =
bytecodes[curBC+6] =
bytecodes[curBC+7] =
(byte)JBC_nop;
} else {
bytecodes[curBC+5] = (byte)JBC_invokestatic;
MemberReference unboxMethod;
if (parameters[i].isBooleanType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsBoolean"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)Z"));
} else if (parameters[i].isByteType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsByte"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)B"));
} else if (parameters[i].isShortType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsShort"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)S"));
} else if (parameters[i].isCharType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsChar"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)C"));
} else if (parameters[i].isIntType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsInt"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)I"));
} else if (parameters[i].isLongType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsLong"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)J"));
} else if (parameters[i].isFloatType()) {
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsFloat"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)F"));
} else {
if (VM.VerifyAssertions) VM._assert(parameters[i].isDoubleType());
unboxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("unboxAsDouble"),
Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)D"));
}
constantPool[i+1] = ClassFileReader.packCPEntry(CP_MEMBER, unboxMethod.getId());
bytecodes[curBC+6] = (byte)((i+1) >>> 8);
bytecodes[curBC+7] = (byte)(i+1);
}
curBC+=8;
}
if (isStatic()) {
bytecodes[curBC] = (byte)JBC_invokestatic;
} else if (isObjectInitializer() || isPrivate()) {
bytecodes[curBC] = (byte)JBC_invokespecial;
} else if (interfaceCall) {
bytecodes[curBC] = (byte)JBC_invokeinterface;
} else {
bytecodes[curBC] = (byte)JBC_invokevirtual;
}
constantPool[numParams+1] = ClassFileReader.packCPEntry(CP_MEMBER, getId());
bytecodes[curBC+1] = (byte)((numParams+1) >>> 8);
bytecodes[curBC+2] = (byte)(numParams+1);
if (interfaceCall) {
// invokeinterface bytecodes are historically longer than others
curBC+=2;
}
TypeReference returnType = getReturnType();
if (!returnType.isPrimitiveType() || returnType.isWordLikeType()) {
bytecodes[curBC+3] = (byte)JBC_nop;
bytecodes[curBC+4] = (byte)JBC_nop;
bytecodes[curBC+5] = (byte)JBC_nop;
} else if (returnType.isVoidType()) {
bytecodes[curBC+3] = (byte)JBC_aconst_null;
bytecodes[curBC+4] = (byte)JBC_nop;
bytecodes[curBC+5] = (byte)JBC_nop;
} else {
MemberReference boxMethod;
if (returnType.isBooleanType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsBoolean"),
Atom.findOrCreateUnicodeAtom("(Z)Ljava/lang/Object;"));
} else if (returnType.isByteType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsByte"),
Atom.findOrCreateUnicodeAtom("(B)Ljava/lang/Object;"));
} else if (returnType.isShortType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsShort"),
Atom.findOrCreateUnicodeAtom("(S)Ljava/lang/Object;"));
} else if (returnType.isCharType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsChar"),
Atom.findOrCreateUnicodeAtom("(C)Ljava/lang/Object;"));
} else if (returnType.isIntType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsInt"),
Atom.findOrCreateUnicodeAtom("(I)Ljava/lang/Object;"));
} else if (returnType.isLongType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsLong"),
Atom.findOrCreateUnicodeAtom("(J)Ljava/lang/Object;"));
} else if (returnType.isFloatType()) {
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsFloat"),
Atom.findOrCreateUnicodeAtom("(F)Ljava/lang/Object;"));
} else {
if (VM.VerifyAssertions) VM._assert(returnType.isDoubleType());
boxMethod = MethodReference.findOrCreate(baseReflectionClass,
Atom.findOrCreateUnicodeAtom("boxAsDouble"),
Atom.findOrCreateUnicodeAtom("(D)Ljava/lang/Object;"));
}
constantPool[numParams+2] = ClassFileReader.packCPEntry(CP_MEMBER, boxMethod.getId());
bytecodes[curBC+3] = (byte)JBC_invokestatic;
bytecodes[curBC+4] = (byte)((numParams+2) >>> 8);
bytecodes[curBC+5] = (byte)(numParams+2);
}
bytecodes[curBC+6] = (byte)JBC_areturn;
return new NormalMethod(reflectionClass,
memRef,
(short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
null,
(short) 3,
(short) (getParameterWords() + 2),
bytecodes,
null,
null,
null,
constantPool,
null,
null,
null,
null);
}
}