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