/* * 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.compilers.common.CodeArray; import org.jikesrvm.compilers.common.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; import static org.jikesrvm.runtime.JavaSizeConstants.BITS_IN_SHORT; import static org.jikesrvm.classloader.BytecodeConstants.*; import static org.jikesrvm.classloader.ClassLoaderConstants.*; /** * 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 { /** * 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); } } } /** * @return the parameter annotations for this method */ @Pure private RVMAnnotation[][] getParameterAnnotations() { synchronized (parameterAnnotations) { return parameterAnnotations.get(this); } } /** * @return 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 * @throws IOException when the underlying stream throws an IOException or when * the skipping of attributes does not work as expected * @return the newly created method */ 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; } /** * @return {@code true} if this method is a class initializer */ @Uninterruptible public final boolean isClassInitializer() { return getName() == RVMClassLoader.StandardClassInitializerMethodName; } /** * @return {@code true} if this method is an object initializer */ @Uninterruptible public final boolean isObjectInitializer() { return getName() == RVMClassLoader.StandardObjectInitializerMethodName; } /** * @return {@code true} if this method is a a compiler-generated * object initializer helper */ @Uninterruptible public final boolean isObjectInitializerHelper() { return getName() == RVMClassLoader.StandardObjectInitializerHelperMethodName; } @Uninterruptible public final TypeReference getReturnType() { return memRef.asMethodReference().getReturnType(); } /** * @return the types of this method's parameters. * Note: does *not* include implicit "this" parameter, if any. */ @Uninterruptible public final TypeReference[] getParameterTypes() { return memRef.asMethodReference().getParameterTypes(); } /** * @return 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(); } /** * @return {@code true} if machine code has 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! * <p> * We make this method Unpreemptible to avoid a race-condition * in Reflection.invoke. * @return compiled method or {@code null} if none exists */ @Unpreemptible public final synchronized CompiledMethod getCurrentCompiledMethod() { return currentCompiledMethod; } /** * @return {@code true} if this method is declared as statically * dispatched */ @Uninterruptible public final boolean isStatic() { return (modifiers & ACC_STATIC) != 0; } /** * @return {@code true} if this method is declared as non-overridable * by subclasses */ @Uninterruptible public final boolean isFinal() { return (modifiers & ACC_FINAL) != 0; } /** * @return {@code true} if this method is guarded by * monitorenter/monitorexit */ @Uninterruptible public final boolean isSynchronized() { return (modifiers & ACC_SYNCHRONIZED) != 0; } /** * @return {@code true} if this method is not implemented in java */ @Uninterruptible public final boolean isNative() { return (modifiers & ACC_NATIVE) != 0; } /** * @return {@code true} if IEEE 754 rules need to be strictly * enforced for this method */ public final boolean isStrictFP() { return (modifiers & ACC_STRICT) != 0; } /** * @return {@code true} if this is a method that's not implemented in Java * and use C not JNI calling convention * @see org.jikesrvm.runtime.SysCall */ public final boolean isSysCall() { return isNative() && isStatic() && isAnnotationDeclared(TypeReference.SysCall); } /** * @return {@code true} if this is a specialized method invoke * @see SpecializedMethod * @see SpecializedMethodManager */ public final boolean isSpecializedInvoke() { return isAnnotationDeclared(TypeReference.SpecializedMethodInvoke); } /** * @return {@code true} if this method needs to be implemented by subclasses */ @Uninterruptible public final boolean isAbstract() { return (modifiers & ACC_ABSTRACT) != 0; } /** * @return {@code true} if this method is not present in the source code file * (e.g. because it has been added by a Java compiler like javac) */ public boolean isSynthetic() { return (modifiers & ACC_SYNTHETIC) != 0; } /** * @return {@code true} if this method is a bridge method. Bridge methods are * generated in some cases of generics and inheritance. */ public boolean isBridge() { return (modifiers & BRIDGE) != 0; } /** * @return {@code true} if ts this a varargs method taking a variable number * of arguments */ public boolean isVarArgs() { return (modifiers & VARARGS) != 0; } /** * Exceptions thrown by this method - * something like <code>{ "java/lang/IOException", "java/lang/EOFException" }</code> * @return exception info or {@code null} if this 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 <code><clinit></code> or <code><init></code> method then it is interruptible. * <li> If is the synthetic 'this' method used by jikes to * factor out default initializers for <code><init></code> 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> * * @return {@code true} if and only if this method is interruptible */ 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} * * @return {@code true} if and only if this method is unpreemptible */ 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} * * @return {@code true} if and only if this method is uninterruptible */ 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? * * @return {@code true} if this method must not be inlined */ public final boolean hasNoInlinePragma() { return (hasNoInlineAnnotation() || hasNoOptCompileAnnotation()); } /** * @param field the field whose write access is supposed to be checked * @return {@code 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 {@code 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 } /** * @return {@code true} if all allocation from this method must go to * a non-moving space * * @see org.vmmagic.pragma.NonMovingAllocation */ 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. * * @return the code array 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.getInstructions(); } } 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. */ public final synchronized void compile() { if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isResolved()); if (isCompiled()) return; if (VM.TraceClassLoading && VM.runningVM) VM.sysWriteln("RVMMethod: (begin) compiling " + this); 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.sysWriteln("RVMMethod: (end) compiling " + this); } /** * Generates the code for this method. * * @return an object representing the compiled method */ 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)<p> * * Side effect: updates JTOC or method dispatch tables * ("type information blocks") * for this class and its subclasses * * @param compiledMethod new machine code * */ 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); } } /** * Invalidates the given compiled method if it is the current compiled code * for this method. * * @param cm the compiled method to try to invalidate */ public final synchronized void invalidateCompiledMethod(CompiledMethod cm) { if (VM.VerifyAssertions) VM._assert(getDeclaringClass().isInstantiated()); if (currentCompiledMethod == cm) { replaceCompiledMethod(null); } } /** * Gets the offset used to hold a JTOC addressable version of the current entry * code array. * * @return the JTOC offset */ @Pure private Offset getJtocOffset() { Integer offAsInt; synchronized (jtocOffsets) { offAsInt = jtocOffsets.get(this); } if (offAsInt == null) { return Offset.zero(); } else { return Offset.fromIntSignExtend(offAsInt.intValue()); } } /** * Finds or create a JTOC offset for this method. * * @return the JTOC offset */ 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; } /** * @return 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(); if (parameterAnnotations == null) { result = new Annotation[getParameterTypes().length][]; for (int a = 0; a < result.length; a++) { result[a] = new Annotation[0]; } } else { 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; /** * @return 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); } }