001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.runtime; 014 015import static org.jikesrvm.VM.NOT_REACHED; 016import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_BYTE; 017import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_CHAR; 018import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_INT; 019import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_LONG; 020import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS; 021 022import org.jikesrvm.VM; 023import org.jikesrvm.architecture.AbstractRegisters; 024import org.jikesrvm.architecture.StackFrameLayout; 025import org.jikesrvm.classloader.DynamicTypeCheck; 026import org.jikesrvm.classloader.MemberReference; 027import org.jikesrvm.classloader.RVMArray; 028import org.jikesrvm.classloader.RVMClass; 029import org.jikesrvm.classloader.RVMField; 030import org.jikesrvm.classloader.RVMMethod; 031import org.jikesrvm.classloader.RVMType; 032import org.jikesrvm.classloader.TypeReference; 033import org.jikesrvm.compilers.common.CompiledMethod; 034import org.jikesrvm.compilers.common.CompiledMethods; 035import org.jikesrvm.mm.mminterface.Barriers; 036import org.jikesrvm.mm.mminterface.MemoryManager; 037import org.jikesrvm.objectmodel.ObjectModel; 038import org.jikesrvm.objectmodel.TIB; 039import org.jikesrvm.scheduler.RVMThread; 040import org.jikesrvm.util.Services; 041import org.vmmagic.pragma.Entrypoint; 042import org.vmmagic.pragma.Inline; 043import org.vmmagic.pragma.NoInline; 044import org.vmmagic.pragma.Pure; 045import org.vmmagic.pragma.Uninterruptible; 046import org.vmmagic.pragma.Unpreemptible; 047import org.vmmagic.pragma.UnpreemptibleNoWarn; 048import org.vmmagic.unboxed.Address; 049import org.vmmagic.unboxed.Offset; 050import org.vmmagic.unboxed.Word; 051 052/** 053 * Entrypoints into the runtime of the virtual machine. 054 * 055 * <p> These are "helper functions" called from machine code 056 * emitted by BaselineCompilerImpl. 057 * They implement functionality that cannot be mapped directly 058 * into a small inline 059 * sequence of machine instructions. See also: Linker. 060 * 061 * <p> Note #1: If you add, remove, or change the signature of 062 * any of these methods you may need to change Entrypoints to match. 063 * 064 * <p> Note #2: Code here must be carefully written to be gc-safe 065 * while manipulating 066 * stackframe and instruction addresses. 067 * 068 * <p> Any time we are holding interior pointers to objects that 069 * could be moved by a garbage 070 * collection cycle we must either avoid passing through gc-sites 071 * (by writing 072 * straight line code with no "non-magic" method invocations) or we 073 * must turn off the 074 * collector (so that a gc request initiated by another thread will 075 * not run until we're 076 * done manipulating the bare pointers). Furthermore, while 077 * the collector is turned off, 078 * we must be careful not to make any allocation requests ("new"). 079 * 080 * <p> The interior pointers that we must worry about are: 081 * <ul> 082 * <li> "ip" values that point to interiors of "code" objects 083 * <li> "fp" values that point to interior of "stack" objects 084 * </ul> 085 */ 086public class RuntimeEntrypoints { 087 088 private static final boolean traceAthrow = false; 089 // Trap codes for communication with C trap handler. 090 // 091 public static final int TRAP_UNKNOWN = -1; 092 public static final int TRAP_NULL_POINTER = 0; 093 public static final int TRAP_ARRAY_BOUNDS = 1; 094 public static final int TRAP_DIVIDE_BY_ZERO = 2; 095 public static final int TRAP_STACK_OVERFLOW = 3; 096 public static final int TRAP_CHECKCAST = 4; // opt-compiler 097 public static final int TRAP_REGENERATE = 5; // opt-compiler 098 public static final int TRAP_JNI_STACK = 6; // jni 099 public static final int TRAP_MUST_IMPLEMENT = 7; 100 public static final int TRAP_STORE_CHECK = 8; // opt-compiler 101 public static final int TRAP_STACK_OVERFLOW_FATAL = 9; // assertion checking 102 public static final int TRAP_UNREACHABLE_BYTECODE = 10; // IA32 baseline compiler assertion 103 104 private static final String UNREACHABLE_BC_MESSAGE = "Attempted to execute " + 105 "a bytecode that was determined to be unreachable!"; 106 107 //---------------------------------------------------------------// 108 // Type Checking. // 109 //---------------------------------------------------------------// 110 111 /** 112 * Test if object is instance of target class/array or 113 * implements target interface. 114 * @param object object to be tested 115 * @param targetID type reference id corresponding to target 116 * class/array/interface 117 * @return true iff is object instance of target type? 118 */ 119 @Entrypoint 120 static boolean instanceOf(Object object, int targetID) throws NoClassDefFoundError { 121 122 /* Here, LHS and RHS refer to the way we would treat these if they were 123 arguments to an assignment operator and we were testing for 124 assignment-compatibility. In Java, "rhs instanceof lhs" means that 125 the operation "lhs = rhs" would succeed. This of course is backwards 126 if one is looking at it from the point of view of the "instanceof" 127 operator. */ 128 TypeReference tRef = TypeReference.getTypeRef(targetID); 129 RVMType lhsType = tRef.peekType(); 130 if (lhsType == null) { 131 lhsType = tRef.resolve(); 132 } 133 if (!lhsType.isResolved()) { 134 lhsType.resolve(); // forces loading/resolution of super class/interfaces 135 } 136 137 /* Test for null only AFTER we have resolved the type of targetID. */ 138 if (object == null) { 139 return false; // null is not an instance of any type 140 } 141 142 RVMType rhsType = ObjectModel.getObjectType(object); 143 /* RHS must already be resolved, since we have a non-null object that is 144 an instance of RHS */ 145 if (VM.VerifyAssertions) VM._assert(rhsType.isResolved()); 146 if (VM.VerifyAssertions) VM._assert(lhsType.isResolved()); 147 148 return lhsType == rhsType || DynamicTypeCheck.instanceOfResolved(lhsType, rhsType); 149 } 150 151 /** 152 * Throw exception unless object is instance of target 153 * class/array or implements target interface. 154 * @param object object to be tested 155 * @param id of type reference corresponding to target class/array/interface 156 */ 157 @Entrypoint 158 static void checkcast(Object object, int id) throws ClassCastException, NoClassDefFoundError { 159 if (object == null) { 160 return; // null may be cast to any type 161 } 162 163 TypeReference tRef = TypeReference.getTypeRef(id); 164 RVMType lhsType = tRef.peekType(); 165 if (lhsType == null) { 166 lhsType = tRef.resolve(); 167 } 168 RVMType rhsType = ObjectModel.getObjectType(object); 169 if (lhsType == rhsType) { 170 return; // exact match 171 } 172 173 // not an exact match, do more involved lookups 174 // 175 if (!isAssignableWith(lhsType, rhsType)) { 176 throw new ClassCastException("Cannot cast a(n) " + rhsType + " to a(n) " + lhsType); 177 } 178 } 179 180 @Entrypoint 181 static void aastore(Object[] arrayRef, int index, Object value) throws ArrayStoreException, ArrayIndexOutOfBoundsException { 182 checkstore(arrayRef, value); 183 int nelts = ObjectModel.getArrayLength(arrayRef); 184 if (index >= 0 && index < nelts) { 185 Services.setArrayUninterruptible(arrayRef, index, value); 186 } else { 187 throw new ArrayIndexOutOfBoundsException(index); 188 } 189 } 190 191 @Entrypoint 192 @Uninterruptible 193 static void aastoreUninterruptible(Object[] arrayRef, int index, Object value) { 194 if (VM.VerifyAssertions) { 195 int nelts = ObjectModel.getArrayLength(arrayRef); 196 VM._assert(index >= 0 && index < nelts); 197 } 198 Services.setArrayUninterruptible(arrayRef, index, value); 199 } 200 201 @Entrypoint 202 @Inline 203 static void checkstore(Object array, Object arrayElement) throws ArrayStoreException { 204 if (arrayElement == null) { 205 return; // null may be assigned to any type 206 } 207 208 RVMType lhsType = Magic.getObjectType(array); 209 RVMType elmType = lhsType.asArray().getElementType(); 210 211 if (elmType == RVMType.JavaLangObjectType) { 212 return; // array of Object can receive anything 213 } 214 215 RVMType rhsType = Magic.getObjectType(arrayElement); 216 217 if (elmType == rhsType) { 218 return; // exact type match 219 } 220 221 if (isAssignableWith(elmType, rhsType)) { 222 return; 223 } 224 225 throw new ArrayStoreException(); 226 } 227 228 /** 229 * May a variable of type "lhs" be assigned a value of type "rhs"? 230 * @param lhs type of variable 231 * @param rhs type of value 232 * @return true --> assignment is legal 233 * false --> assignment is illegal 234 * <strong>Assumption</strong>: caller has already tested "trivial" case 235 * (exact type match) 236 * so we need not repeat it here 237 */ 238 @Pure 239 @Inline(value = Inline.When.AllArgumentsAreConstant) 240 public static boolean isAssignableWith(RVMType lhs, RVMType rhs) { 241 if (!lhs.isResolved()) { 242 lhs.resolve(); 243 } 244 if (!rhs.isResolved()) { 245 rhs.resolve(); 246 } 247 return DynamicTypeCheck.instanceOfResolved(lhs, rhs); 248 } 249 250 //---------------------------------------------------------------// 251 // Object Allocation. // 252 //---------------------------------------------------------------// 253 254 /** 255 * Allocate something like "new Foo()". 256 * @param id id of type reference of class to create 257 * @param site the site id of the calling allocation site 258 * @return object with header installed and all fields set to zero/null 259 * (ready for initializer to be run on it) 260 * See also: bytecode 0xbb ("new") 261 */ 262 @Entrypoint 263 static Object unresolvedNewScalar(int id, int site) throws NoClassDefFoundError, OutOfMemoryError { 264 TypeReference tRef = TypeReference.getTypeRef(id); 265 RVMType t = tRef.peekType(); 266 if (t == null) { 267 t = tRef.resolve(); 268 } 269 RVMClass cls = t.asClass(); 270 if (!cls.isInitialized()) { 271 initializeClassForDynamicLink(cls); 272 } 273 274 int allocator = MemoryManager.pickAllocator(cls); 275 int align = ObjectModel.getAlignment(cls); 276 int offset = ObjectModel.getOffsetForAlignment(cls, false); 277 return resolvedNewScalar(cls.getInstanceSize(), 278 cls.getTypeInformationBlock(), 279 cls.hasFinalizer(), 280 allocator, 281 align, 282 offset, 283 site); 284 } 285 286 /** 287 * Allocate something like "new Foo()". 288 * @param cls RVMClass of array to create 289 * @return object with header installed and all fields set to zero/null 290 * (ready for initializer to be run on it) 291 * See also: bytecode 0xbb ("new") 292 */ 293 public static Object resolvedNewScalar(RVMClass cls) { 294 295 int allocator = MemoryManager.pickAllocator(cls); 296 int site = MemoryManager.getAllocationSite(false); 297 int align = ObjectModel.getAlignment(cls); 298 int offset = ObjectModel.getOffsetForAlignment(cls, false); 299 return resolvedNewScalar(cls.getInstanceSize(), 300 cls.getTypeInformationBlock(), 301 cls.hasFinalizer(), 302 allocator, 303 align, 304 offset, 305 site); 306 } 307 308 /** 309 * Allocate something like "new Foo()". 310 * @param size size of object (including header), in bytes 311 * @param tib type information block for object 312 * @param hasFinalizer does this type have a finalizer? 313 * @param allocator int that encodes which allocator should be used 314 * @param align the alignment requested; must be a power of 2. 315 * @param offset the offset at which the alignment is desired. 316 * @param site the site id of the calling allocation site 317 * @return object with header installed and all fields set to zero/null 318 * (ready for initializer to be run on it) 319 * See also: bytecode 0xbb ("new") 320 */ 321 @Entrypoint 322 public static Object resolvedNewScalar(int size, TIB tib, boolean hasFinalizer, int allocator, int align, 323 int offset, int site) throws OutOfMemoryError { 324 325 // GC stress testing 326 if (VM.ForceFrequentGC) checkAllocationCountDownToGC(); 327 328 // Allocate the object and initialize its header 329 Object newObj = MemoryManager.allocateScalar(size, tib, allocator, align, offset, site); 330 331 // Deal with finalization 332 if (hasFinalizer) MemoryManager.addFinalizer(newObj); 333 334 return newObj; 335 } 336 337 /** 338 * Allocate something like "new Foo[]". 339 * @param numElements number of array elements 340 * @param id id of type reference of array to create. 341 * @param site the site id of the calling allocation site 342 * @return array with header installed and all fields set to zero/null 343 * See also: bytecode 0xbc ("anewarray") 344 */ 345 @Entrypoint 346 public static Object unresolvedNewArray(int numElements, int id, int site) 347 throws NoClassDefFoundError, OutOfMemoryError, NegativeArraySizeException { 348 TypeReference tRef = TypeReference.getTypeRef(id); 349 RVMType t = tRef.peekType(); 350 if (t == null) { 351 t = tRef.resolve(); 352 } 353 RVMArray array = t.asArray(); 354 if (!array.isInitialized()) { 355 array.resolve(); 356 array.instantiate(); 357 } 358 359 return resolvedNewArray(numElements, array, site); 360 } 361 362 /** 363 * Allocate something like "new Foo[]". 364 * @param numElements number of array elements 365 * @param array RVMArray of array to create 366 * @return array with header installed and all fields set to zero/null 367 * See also: bytecode 0xbc ("anewarray") 368 */ 369 public static Object resolvedNewArray(int numElements, RVMArray array) 370 throws OutOfMemoryError, NegativeArraySizeException { 371 return resolvedNewArray(numElements, array, MemoryManager.getAllocationSite(false)); 372 } 373 374 public static Object resolvedNewArray(int numElements, RVMArray array, int site) 375 throws OutOfMemoryError, NegativeArraySizeException { 376 377 return resolvedNewArray(numElements, 378 array.getLogElementSize(), 379 ObjectModel.computeArrayHeaderSize(array), 380 array.getTypeInformationBlock(), 381 MemoryManager.pickAllocator(array), 382 ObjectModel.getAlignment(array), 383 ObjectModel.getOffsetForAlignment(array, false), 384 site); 385 } 386 387 /** 388 * Allocate something like "new int[cnt]" or "new Foo[cnt]". 389 * @param numElements number of array elements 390 * @param logElementSize size in bytes of an array element, log base 2. 391 * @param headerSize size in bytes of array header 392 * @param tib type information block for array object 393 * @param allocator int that encodes which allocator should be used 394 * @param align the alignment requested; must be a power of 2. 395 * @param offset the offset at which the alignment is desired. 396 * @param site the site id of the calling allocation site 397 * @return array object with header installed and all elements set 398 * to zero/null 399 * See also: bytecode 0xbc ("newarray") and 0xbd ("anewarray") 400 */ 401 @Entrypoint 402 public static Object resolvedNewArray(int numElements, int logElementSize, int headerSize, TIB tib, 403 int allocator, int align, int offset, int site) 404 throws OutOfMemoryError, NegativeArraySizeException { 405 406 if (numElements < 0) raiseNegativeArraySizeException(); 407 408 // GC stress testing 409 if (VM.ForceFrequentGC) checkAllocationCountDownToGC(); 410 411 // Allocate the array and initialize its header 412 return MemoryManager.allocateArray(numElements, logElementSize, headerSize, tib, allocator, align, offset, site); 413 } 414 415 /** 416 * Clone a Scalar or Array Object. 417 * called from java/lang/Object.clone(). 418 * <p> 419 * For simplicity, we just code this more or less in Java using 420 * internal reflective operations and some magic. 421 * This is inefficient for large scalar objects, but until that 422 * is proven to be a performance problem, we won't worry about it. 423 * By keeping this in Java instead of dropping into Memory.copy, 424 * we avoid having to add special case code to deal with write barriers, 425 * and other such things. 426 * <p> 427 * This method calls specific cloning routines based on type to help 428 * guide the inliner (which won't inline a single large method). 429 * 430 * @param obj the object to clone 431 * @return the cloned object 432 * @throws CloneNotSupportedException when the object does not support cloning 433 */ 434 public static Object clone(Object obj) throws OutOfMemoryError, CloneNotSupportedException { 435 RVMType type = Magic.getObjectType(obj); 436 if (type.isArrayType()) { 437 return cloneArray(obj, type); 438 } else { 439 return cloneClass(obj, type); 440 } 441 } 442 443 /** 444 * Clone an array 445 * 446 * @param obj the array to clone 447 * @param type the type information for the array 448 * @return the cloned object 449 */ 450 private static Object cloneArray(Object obj, RVMType type) throws OutOfMemoryError { 451 RVMArray ary = type.asArray(); 452 int nelts = ObjectModel.getArrayLength(obj); 453 Object newObj = resolvedNewArray(nelts, ary); 454 System.arraycopy(obj, 0, newObj, 0, nelts); 455 return newObj; 456 } 457 458 /** 459 * Clone an object implementing a class - check that the class is cloneable 460 * (we make this a small method with just a test so that the inliner will 461 * inline it and hopefully eliminate the instanceof test). 462 * 463 * @param obj the object to clone 464 * @param type the type information for the class 465 * @return the cloned object 466 * @throws CloneNotSupportedException when the object does not support cloning 467 */ 468 private static Object cloneClass(Object obj, RVMType type) throws OutOfMemoryError, CloneNotSupportedException { 469 if (!(obj instanceof Cloneable)) { 470 throw new CloneNotSupportedException(); 471 } else { 472 return cloneClass2(obj, type); 473 } 474 } 475 476 /** 477 * Clone an object implementing a class - the actual clone 478 * 479 * @param obj the object to clone 480 * @param type the type information for the class 481 * @return the cloned object 482 */ 483 private static Object cloneClass2(Object obj, RVMType type) throws OutOfMemoryError { 484 RVMClass cls = type.asClass(); 485 Object newObj = resolvedNewScalar(cls); 486 for (RVMField f : cls.getInstanceFields()) { 487 if (f.isReferenceType()) { 488 // Writing a reference 489 // Do via slower "VM-internal reflection" to enable 490 // collectors to do the right thing wrt reference counting 491 // and write barriers. 492 f.setObjectValueUnchecked(newObj, f.getObjectValueUnchecked(obj)); 493 } else { 494 // Primitive type 495 // Check if we need to go via the slower barried path 496 TypeReference fieldType = f.getType(); 497 if (Barriers.NEEDS_BOOLEAN_PUTFIELD_BARRIER && fieldType.isBooleanType()) { 498 f.setBooleanValueUnchecked(newObj, f.getBooleanValueUnchecked(obj)); 499 continue; 500 } else if (Barriers.NEEDS_BYTE_PUTFIELD_BARRIER && fieldType.isByteType()) { 501 f.setByteValueUnchecked(newObj, f.getByteValueUnchecked(obj)); 502 continue; 503 } else if (Barriers.NEEDS_CHAR_PUTFIELD_BARRIER && fieldType.isCharType()) { 504 f.setCharValueUnchecked(newObj, f.getCharValueUnchecked(obj)); 505 continue; 506 } else if (Barriers.NEEDS_DOUBLE_PUTFIELD_BARRIER && fieldType.isDoubleType()) { 507 f.setDoubleValueUnchecked(newObj, f.getDoubleValueUnchecked(obj)); 508 continue; 509 } else if (Barriers.NEEDS_FLOAT_PUTFIELD_BARRIER && fieldType.isFloatType()) { 510 f.setFloatValueUnchecked(newObj, f.getFloatValueUnchecked(obj)); 511 continue; 512 } else if (Barriers.NEEDS_INT_PUTFIELD_BARRIER && fieldType.isIntType()) { 513 f.setIntValueUnchecked(newObj, f.getIntValueUnchecked(obj)); 514 continue; 515 } else if (Barriers.NEEDS_LONG_PUTFIELD_BARRIER && fieldType.isLongType()) { 516 f.setLongValueUnchecked(newObj, f.getLongValueUnchecked(obj)); 517 continue; 518 } else if (Barriers.NEEDS_SHORT_PUTFIELD_BARRIER && fieldType.isShortType()) { 519 f.setShortValueUnchecked(newObj, f.getShortValueUnchecked(obj)); 520 continue; 521 } else if (Barriers.NEEDS_WORD_PUTFIELD_BARRIER && fieldType.isWordType()) { 522 f.setWordValueUnchecked(newObj, f.getWordValueUnchecked(obj)); 523 continue; 524 } else if (Barriers.NEEDS_ADDRESS_PUTFIELD_BARRIER && fieldType.isAddressType()) { 525 f.setAddressValueUnchecked(newObj, f.getAddressValueUnchecked(obj)); 526 continue; 527 } else if (Barriers.NEEDS_EXTENT_PUTFIELD_BARRIER && fieldType.isExtentType()) { 528 f.setExtentValueUnchecked(newObj, f.getExtentValueUnchecked(obj)); 529 continue; 530 } else if (Barriers.NEEDS_OFFSET_PUTFIELD_BARRIER && fieldType.isOffsetType()) { 531 f.setOffsetValueUnchecked(newObj, f.getOffsetValueUnchecked(obj)); 532 continue; 533 } else { 534 // Can perform raw copy of field 535 int size = f.getSize(); 536 Offset offset = f.getOffset(); 537 if (VM.BuildFor32Addr) { 538 // As per pre primitive write barrier code we test the most likely 539 // case first 540 if (size == BYTES_IN_INT) { 541 int bits = Magic.getIntAtOffset(obj, offset); 542 Magic.setIntAtOffset(newObj, offset, bits); 543 continue; 544 } else if (size == BYTES_IN_LONG) { 545 long bits = Magic.getLongAtOffset(obj, offset); 546 Magic.setLongAtOffset(newObj, offset, bits); 547 continue; 548 } 549 } else { 550 // BuildFor64Addr 551 // As per pre primitive write barrier code we test the most likely 552 // case first 553 if (size == BYTES_IN_LONG) { 554 long bits = Magic.getLongAtOffset(obj, offset); 555 Magic.setLongAtOffset(newObj, offset, bits); 556 continue; 557 } else if (size == BYTES_IN_INT) { 558 int bits = Magic.getIntAtOffset(obj, offset); 559 Magic.setIntAtOffset(newObj, offset, bits); 560 continue; 561 } 562 } 563 if (size == BYTES_IN_CHAR) { 564 char bits = Magic.getCharAtOffset(obj, offset); 565 Magic.setCharAtOffset(newObj, offset, bits); 566 } else { 567 if (VM.VerifyAssertions) VM._assert(size == BYTES_IN_BYTE); 568 byte bits = Magic.getByteAtOffset(obj, offset); 569 Magic.setByteAtOffset(newObj, offset, bits); 570 } 571 } 572 } 573 } 574 return newObj; 575 } 576 577 /** 578 * Helper function to actually throw the required exception. 579 * Keep out of line to mitigate code space when quickNewArray is inlined. 580 */ 581 @NoInline 582 private static void raiseNegativeArraySizeException() throws NegativeArraySizeException { 583 throw new NegativeArraySizeException(); 584 } 585 586 /** 587 * Get an object's "hashcode" value. 588 * 589 * Side effect: hash value is generated and stored into object's 590 * status word. 591 * @param object the object to hash 592 * @return object's hashcode. 593 * @see java.lang.Object#hashCode 594 */ 595 public static int getObjectHashCode(Object object) { 596 return ObjectModel.getObjectHashCode(object); 597 } 598 599 //---------------------------------------------------------------// 600 // Dynamic linking. // 601 //---------------------------------------------------------------// 602 603 /** 604 * Prepare a class for use prior to first allocation, 605 * field access, or method invocation. 606 * Made public so that it is accessible from java.lang.reflect.*. 607 * 608 * @param cls the class to prepare for dynamic link 609 * @see MemberReference#needsDynamicLink 610 */ 611 public static void initializeClassForDynamicLink(RVMClass cls) { 612 if (VM.TraceClassLoading) { 613 VM.sysWrite("RuntimeEntrypoints.initializeClassForDynamicLink: (begin) " + cls + "\n"); 614 } 615 616 cls.resolve(); 617 cls.instantiate(); 618 cls.initialize(); // throws ExceptionInInitializerError 619 620 if (VM.TraceClassLoading) { 621 VM.sysWrite("RuntimeEntrypoints.initializeClassForDynamicLink: (end) " + cls + "\n"); 622 } 623 } 624 625 //---------------------------------------------------------------// 626 // Implementation Errors. // 627 //---------------------------------------------------------------// 628 629 /** 630 * Report unexpected method call: interface method 631 * (virtual machine dispatching error, shouldn't happen). 632 */ 633 static void unexpectedInterfaceMethodCall() { 634 VM.sysFail("interface method dispatching error"); 635 } 636 637 /** 638 * Report unexpected method call: abstract method (verification error). 639 */ 640 @Entrypoint 641 static void unexpectedAbstractMethodCall() { 642 VM.sysWrite("RuntimeEntrypoints.unexpectedAbstractMethodCall\n"); 643 throw new AbstractMethodError(); 644 } 645 646 //---------------------------------------------------------------// 647 // Exception Handling. // 648 //---------------------------------------------------------------// 649 650 /** 651 * Deliver a software exception to current java thread. 652 * @param exceptionObject exception object to deliver 653 * (null --> deliver NullPointerException). 654 * does not return 655 * (stack is unwound and execution resumes in a catch block) 656 * 657 * This method is public so that it can be invoked by java.lang.VMClass. 658 */ 659 @NoInline 660 @Entrypoint 661 @Unpreemptible("Deliver exception possibly from unpreemptible code") 662 public static void athrow(Throwable exceptionObject) { 663 if (traceAthrow) { 664 VM.sysWriteln("in athrow."); 665 RVMThread.dumpStack(); 666 } 667 RVMThread myThread = RVMThread.getCurrentThread(); 668 AbstractRegisters exceptionRegisters = myThread.getExceptionRegisters(); 669 VM.disableGC(); // VM.enableGC() is called when the exception is delivered. 670 Magic.saveThreadState(exceptionRegisters); 671 exceptionRegisters.setInUse(true); 672 deliverException(exceptionObject, exceptionRegisters); 673 } 674 675 /** 676 * Deliver a hardware exception to current java thread. 677 * <p> 678 * Does not return. 679 * (stack is unwound, starting at trap site, and 680 * execution resumes in a catch block somewhere up the stack) 681 * /or/ execution resumes at instruction following trap 682 * (for TRAP_STACK_OVERFLOW) 683 * 684 * <p> Note: Control reaches here by the actions of an 685 * external "C" signal handler 686 * which saves the register state of the trap site into the 687 * "exceptionRegisters" field of the current 688 * Thread object. 689 * The signal handler also inserts a <hardware trap> frame 690 * onto the stack immediately above this frame, for use by 691 * HardwareTrapGCMapIterator during garbage collection. 692 * 693 * @param trapCode code indicating kind of exception that was trapped 694 * (see TRAP_xxx, above) 695 * @param trapInfo array subscript (for array bounds trap, only), marker 696 * (for stack overflow traps on PPC) or 697 */ 698 @Entrypoint 699 @UnpreemptibleNoWarn 700 static void deliverHardwareException(int trapCode, Word trapInfo) { 701 if (VM.verboseSignalHandling) VM.sysWriteln("delivering hardware exception"); 702 RVMThread myThread = RVMThread.getCurrentThread(); 703 if (VM.verboseSignalHandling) VM.sysWriteln("we have a thread = ",Magic.objectAsAddress(myThread)); 704 if (VM.verboseSignalHandling) VM.sysWriteln("it's in state = ",myThread.getExecStatus()); 705 AbstractRegisters exceptionRegisters = myThread.getExceptionRegisters(); 706 if (VM.verboseSignalHandling) VM.sysWriteln("we have exception registers = ",Magic.objectAsAddress(exceptionRegisters)); 707 708 if ((trapCode == TRAP_STACK_OVERFLOW || trapCode == TRAP_JNI_STACK) && 709 myThread.getStack().length < (StackFrameLayout.getMaxStackSize() >> LOG_BYTES_IN_ADDRESS) && 710 !myThread.hasNativeStackFrame()) { 711 // expand stack by the size appropriate for normal or native frame 712 // and resume execution at successor to trap instruction 713 // (C trap handler has set register.ip to the instruction following the trap). 714 if (trapCode == TRAP_JNI_STACK) { 715 RVMThread.resizeCurrentStack(myThread.getStackLength() + StackFrameLayout.getJNIStackGrowthSize(), exceptionRegisters); 716 } else { 717 RVMThread.resizeCurrentStack(myThread.getStackLength() + StackFrameLayout.getStackGrowthSize(), exceptionRegisters); 718 } 719 if (VM.VerifyAssertions) VM._assert(exceptionRegisters.getInUse()); 720 exceptionRegisters.setInUse(false); 721 Magic.restoreHardwareExceptionState(exceptionRegisters); 722 723 if (VM.VerifyAssertions) VM._assert(NOT_REACHED); 724 } 725 726 // GC stress testing 727 if (canForceGC()) { 728 //VM.sysWrite("FORCING GC: in deliverHardwareException\n"); 729 System.gc(); 730 } 731 732 // Sanity checking. 733 // Hardware traps in uninterruptible code should be considered hard failures. 734 if (!VM.sysFailInProgress()) { 735 Address fp = exceptionRegisters.getInnermostFramePointer(); 736 int compiledMethodId = Magic.getCompiledMethodID(fp); 737 if (compiledMethodId != StackFrameLayout.getInvisibleMethodID()) { 738 CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId); 739 Address ip = exceptionRegisters.getInnermostInstructionAddress(); 740 Offset instructionOffset = compiledMethod.getInstructionOffset(ip); 741 if (compiledMethod.isWithinUninterruptibleCode(instructionOffset)) { 742 switch (trapCode) { 743 case TRAP_NULL_POINTER: 744 VM.sysWriteln("\nFatal error: NullPointerException within uninterruptible region."); 745 break; 746 case TRAP_ARRAY_BOUNDS: 747 VM.sysWriteln("\nFatal error: ArrayIndexOutOfBoundsException within uninterruptible region (index was ", trapInfo.toInt(), ")."); 748 break; 749 case TRAP_DIVIDE_BY_ZERO: 750 VM.sysWriteln("\nFatal error: DivideByZero within uninterruptible region."); 751 break; 752 case TRAP_STACK_OVERFLOW: 753 case TRAP_JNI_STACK: 754 VM.sysWriteln("\nFatal error: StackOverflowError within uninterruptible region."); 755 break; 756 case TRAP_CHECKCAST: 757 VM.sysWriteln("\nFatal error: ClassCastException within uninterruptible region."); 758 break; 759 case TRAP_MUST_IMPLEMENT: 760 VM.sysWriteln("\nFatal error: IncompatibleClassChangeError within uninterruptible region."); 761 break; 762 case TRAP_STORE_CHECK: 763 VM.sysWriteln("\nFatal error: ArrayStoreException within uninterruptible region."); 764 break; 765 case TRAP_UNREACHABLE_BYTECODE: 766 VM.sysWriteln("\nFatal error: Reached a bytecode that was determined to be unreachable within uninterruptible region."); 767 break; 768 default: 769 VM.sysWriteln("\nFatal error: Unknown hardware trap within uninterruptible region."); 770 break; 771 } 772 VM.sysWriteln("trapCode = ", trapCode); 773 VM.sysWriteln("trapInfo = ", trapInfo.toAddress()); 774 VM.sysFail("Exiting virtual machine due to uninterruptibility violation."); 775 } 776 } 777 } 778 779 Throwable exceptionObject; 780 switch (trapCode) { 781 case TRAP_NULL_POINTER: 782 exceptionObject = new java.lang.NullPointerException(); 783 break; 784 case TRAP_ARRAY_BOUNDS: 785 exceptionObject = new java.lang.ArrayIndexOutOfBoundsException(trapInfo.toInt()); 786 break; 787 case TRAP_DIVIDE_BY_ZERO: 788 exceptionObject = new java.lang.ArithmeticException(); 789 break; 790 case TRAP_STACK_OVERFLOW: 791 case TRAP_JNI_STACK: 792 exceptionObject = new java.lang.StackOverflowError(); 793 break; 794 case TRAP_CHECKCAST: 795 exceptionObject = new java.lang.ClassCastException(); 796 break; 797 case TRAP_MUST_IMPLEMENT: 798 exceptionObject = new java.lang.IncompatibleClassChangeError(); 799 break; 800 case TRAP_STORE_CHECK: 801 exceptionObject = new java.lang.ArrayStoreException(); 802 break; 803 case TRAP_UNREACHABLE_BYTECODE: 804 exceptionObject = new java.lang.InternalError(UNREACHABLE_BC_MESSAGE); 805 break; 806 default: 807 exceptionObject = new java.lang.UnknownError(); 808 RVMThread.traceback("UNKNOWN ERROR"); 809 break; 810 } 811 812 VM.disableGC(); // VM.enableGC() is called when the exception is delivered. 813 deliverException(exceptionObject, exceptionRegisters); 814 } 815 816 /** 817 * Unlock an object and then deliver a software exception 818 * to current java thread. 819 * <p> 820 * Does not return (stack is unwound and execution resumes in a catch block). 821 * 822 * @param objToUnlock object to unlock 823 * @param objToThrow exception object to deliver 824 * ({@code null} --> deliver NullPointerException). 825 */ 826 @NoInline 827 @Entrypoint 828 static void unlockAndThrow(Object objToUnlock, Throwable objToThrow) { 829 ObjectModel.genericUnlock(objToUnlock); 830 athrow(objToThrow); 831 } 832 833 /** 834 * Create and throw a java.lang.ArrayIndexOutOfBoundsException. 835 * Only used in some configurations where it is easier to make a call 836 * then recover the array index from a trap instruction. 837 * 838 * @param index the failing index 839 */ 840 @NoInline 841 @Entrypoint 842 static void raiseArrayIndexOutOfBoundsException(int index) { 843 throw new java.lang.ArrayIndexOutOfBoundsException(index); 844 } 845 846 /** 847 * Create and throw a java.lang.ArrayIndexOutOfBoundsException. 848 * Used (rarely) by the opt compiler when it has determined that 849 * an array access will unconditionally raise an array bounds check 850 * error, but it has lost track of exactly what the index is going to be. 851 */ 852 @NoInline 853 static void raiseArrayIndexOutOfBoundsException() { 854 throw new java.lang.ArrayIndexOutOfBoundsException(); 855 } 856 857 /** 858 * Create and throw a java.lang.NullPointerException. 859 * Used in a few circumstances to reduce code space costs 860 * of inlining (see java.lang.System.arraycopy()). Could also 861 * be used to raise a null pointer exception without going through 862 * the hardware trap handler; currently this is only done when the 863 * opt compiler has determined that an instruction will unconditionally 864 * raise a null pointer exception. 865 */ 866 @NoInline 867 @Entrypoint 868 public static void raiseNullPointerException() { 869 throw new java.lang.NullPointerException(); 870 } 871 872 /** 873 * Create and throw a java.lang.ArrayStoreException. 874 * Used in a few circumstances to reduce code space costs 875 * of inlining (see java.lang.System.arraycopy()). 876 */ 877 @NoInline 878 public static void raiseArrayStoreException() { 879 throw new java.lang.ArrayStoreException(); 880 } 881 882 /** 883 * Create and throw a java.lang.ArithmeticException. 884 * Used to raise an arithmetic exception without going through 885 * the hardware trap handler; currently this is only done when the 886 * opt compiler has determined that an instruction will unconditionally 887 * raise an arithmetic exception. 888 */ 889 @NoInline 890 @Entrypoint 891 static void raiseArithmeticException() { 892 throw new java.lang.ArithmeticException(); 893 } 894 895 /** 896 * Create and throw a java.lang.AbstractMethodError. 897 * Used to handle error cases in invokeinterface dispatching. 898 */ 899 @NoInline 900 @Entrypoint 901 static void raiseAbstractMethodError() { 902 throw new java.lang.AbstractMethodError(); 903 } 904 905 /** 906 * Create and throw a java.lang.IllegalAccessError. 907 * Used to handle error cases in invokeinterface dispatching. 908 */ 909 @NoInline 910 @Entrypoint 911 static void raiseIllegalAccessError() { 912 throw new java.lang.IllegalAccessError(); 913 } 914 915 //----------------// 916 // implementation // 917 //----------------// 918 919 public static void init() { 920 // tell the bootloader (sysSignal*.c) to pass control to 921 // "RuntimeEntrypoints.deliverHardwareException()" 922 // whenever the host operating system detects a hardware trap. 923 // 924 BootRecord.the_boot_record.hardwareTrapMethodId = CompiledMethods.createHardwareTrapCompiledMethod().getId(); 925 BootRecord.the_boot_record.deliverHardwareExceptionOffset = 926 Entrypoints.deliverHardwareExceptionMethod.getOffset(); 927 928 // tell the bootloader (sysSignal.c) to set "RVMThread.debugRequested" flag 929 // whenever the host operating system detects a debug request signal 930 // 931 BootRecord.the_boot_record.debugRequestedOffset = Entrypoints.debugRequestedField.getOffset(); 932 } 933 934 /** 935 * Build a multi-dimensional array. 936 * @param methodId TODO document me 937 * @param numElements number of elements to allocate for each dimension 938 * @param arrayType type of array that will result 939 * @return array object 940 */ 941 public static Object buildMultiDimensionalArray(int methodId, int[] numElements, RVMArray arrayType) { 942 RVMMethod method = MemberReference.getMethodRef(methodId).peekResolvedMethod(); 943 if (VM.VerifyAssertions) VM._assert(method != null); 944 return buildMDAHelper(method, numElements, 0, arrayType); 945 } 946 947 /** 948 * Build a two-dimensional array. 949 * @param methodId TODO document me 950 * @param dim0 the arraylength for arrays in dimension 0 951 * @param dim1 the arraylength for arrays in dimension 1 952 * @param arrayType type of array that will result 953 * @return array object 954 */ 955 public static Object buildTwoDimensionalArray(int methodId, int dim0, int dim1, RVMArray arrayType) { 956 RVMMethod method = MemberReference.getMethodRef(methodId).peekResolvedMethod(); 957 if (VM.VerifyAssertions) VM._assert(method != null); 958 959 if (!arrayType.isInstantiated()) { 960 arrayType.resolve(); 961 arrayType.instantiate(); 962 } 963 964 Object[] newArray = (Object[])resolvedNewArray(dim0, arrayType); 965 966 RVMArray innerArrayType = arrayType.getElementType().asArray(); 967 if (!innerArrayType.isInstantiated()) { 968 innerArrayType.resolve(); 969 innerArrayType.instantiate(); 970 } 971 972 for (int i = 0; i < dim0; i++) { 973 newArray[i] = resolvedNewArray(dim1, innerArrayType); 974 } 975 976 return newArray; 977 } 978 979 /** 980 * @param method Apparently unused (!) 981 * @param numElements Number of elements to allocate for each dimension 982 * @param dimIndex Current dimension to build 983 * @param arrayType type of array that will result 984 * @return a multi-dimensional array 985 */ 986 public static Object buildMDAHelper(RVMMethod method, int[] numElements, int dimIndex, RVMArray arrayType) { 987 988 if (!arrayType.isInstantiated()) { 989 arrayType.resolve(); 990 arrayType.instantiate(); 991 } 992 993 int nelts = numElements[dimIndex]; 994 Object newObject = resolvedNewArray(nelts, arrayType); 995 996 if (++dimIndex == numElements.length) { 997 return newObject; // all dimensions have been built 998 } 999 1000 Object[] newArray = (Object[]) newObject; 1001 RVMArray newArrayType = arrayType.getElementType().asArray(); 1002 1003 for (int i = 0; i < nelts; ++i) { 1004 newArray[i] = buildMDAHelper(method, numElements, dimIndex, newArrayType); 1005 } 1006 1007 return newArray; 1008 } 1009 1010 /** 1011 * Deliver an exception to current java thread. 1012 * <STRONG> Precondition: </STRONG> VM.disableGC has already been called. 1013 * <ol> 1014 * <li> exceptionRegisters may not match any reasonable stack 1015 * frame at this point. 1016 * <li> we're going to be playing with raw addresses (fp, ip). 1017 * </ol> 1018 * <p> 1019 * Does not return: 1020 * <ul> 1021 * <li> stack is unwound and execution resumes in a catch block 1022 * <li> <em> or </em> current thread is terminated if no catch block is found 1023 * </ul> 1024 1025 * @param exceptionObject exception object to deliver 1026 * @param exceptionRegisters register state corresponding to exception site 1027 */ 1028 @Unpreemptible("Deliver exception trying to avoid preemption") 1029 private static void deliverException(Throwable exceptionObject, AbstractRegisters exceptionRegisters) { 1030 if (VM.TraceExceptionDelivery) { 1031 VM.sysWriteln("RuntimeEntrypoints.deliverException() entered; just got an exception object."); 1032 } 1033 //VM.sysWriteln("throwing exception!"); 1034 //RVMThread.dumpStack(); 1035 1036 // walk stack and look for a catch block 1037 // 1038 if (VM.TraceExceptionDelivery) { 1039 VM.sysWrite("Hunting for a catch block..."); 1040 } 1041 RVMType exceptionType = Magic.getObjectType(exceptionObject); 1042 Address fp = exceptionRegisters.getInnermostFramePointer(); 1043 Address hijackedCalleeFp = RVMThread.getCurrentThread().getHijackedReturnCalleeFp(); 1044 boolean leapfroggedReturnBarrier = false; 1045 if (VM.VerifyAssertions) VM._assert(hijackedCalleeFp.isZero() || hijackedCalleeFp.GE(fp)); 1046 while (Magic.getCallerFramePointer(fp).NE(StackFrameLayout.getStackFrameSentinelFP())) { 1047 if (!hijackedCalleeFp.isZero() && hijackedCalleeFp.LE(fp)) { 1048 leapfroggedReturnBarrier = true; 1049 } 1050 int compiledMethodId = Magic.getCompiledMethodID(fp); 1051 if (compiledMethodId != StackFrameLayout.getInvisibleMethodID()) { 1052 CompiledMethod compiledMethod = CompiledMethods.getCompiledMethod(compiledMethodId); 1053 ExceptionDeliverer exceptionDeliverer = compiledMethod.getExceptionDeliverer(); 1054 Address ip = exceptionRegisters.getInnermostInstructionAddress(); 1055 Offset ipOffset = compiledMethod.getInstructionOffset(ip); 1056 int catchBlockOffset = compiledMethod.findCatchBlockForInstruction(ipOffset, exceptionType); 1057 1058 if (catchBlockOffset >= 0) { 1059 // found an appropriate catch block 1060 if (VM.TraceExceptionDelivery) { 1061 VM.sysWriteln("found one; delivering."); 1062 } 1063 if (leapfroggedReturnBarrier) { 1064 RVMThread t = RVMThread.getCurrentThread(); 1065 if (RVMThread.DEBUG_STACK_TRAMPOLINE) VM.sysWriteln("leapfrogged..."); 1066 t.deInstallStackTrampoline(); 1067 } 1068 Address catchBlockStart = compiledMethod.getInstructionAddress(Offset.fromIntSignExtend(catchBlockOffset)); 1069 exceptionDeliverer.deliverException(compiledMethod, catchBlockStart, exceptionObject, exceptionRegisters); 1070 if (VM.VerifyAssertions) VM._assert(NOT_REACHED); 1071 } 1072 1073 exceptionDeliverer.unwindStackFrame(compiledMethod, exceptionRegisters); 1074 } else { 1075 unwindInvisibleStackFrame(exceptionRegisters); 1076 } 1077 fp = exceptionRegisters.getInnermostFramePointer(); 1078 } 1079 1080 if (VM.TraceExceptionDelivery) { 1081 VM.sysWriteln("Nope."); 1082 VM.sysWriteln("RuntimeEntrypoints.deliverException() found no catch block."); 1083 } 1084 /* No appropriate catch block found. */ 1085 if (RVMThread.DEBUG_STACK_TRAMPOLINE && leapfroggedReturnBarrier) VM.sysWriteln("Leapfrogged, and unhandled!"); 1086 handleUncaughtException(exceptionObject); 1087 } 1088 1089 @UnpreemptibleNoWarn("Uncaught exception handling that may cause preemption") 1090 private static void handleUncaughtException(Throwable exceptionObject) { 1091 RVMThread.getCurrentThread().handleUncaughtException(exceptionObject); 1092 } 1093 1094 /** 1095 * Skip over all frames below currfp with saved code pointers outside of heap 1096 * (C frames), stopping at the native frame immediately preceding the glue 1097 * frame which contains the method ID of the native method (this is necessary 1098 * to allow retrieving the return address of the glue frame). 1099 * 1100 * @param currfp The current frame is expected to be one of the JNI functions 1101 * called from C, below which is one or more native stack frames 1102 * @return the frame pointer for the appropriate frame 1103 */ 1104 @Uninterruptible 1105 public static Address unwindNativeStackFrame(Address currfp) { 1106 if (VM.BuildForIA32) { 1107 return currfp; 1108 } 1109 // Remembered address of previous FP 1110 Address callee_fp; 1111 // Address of native frame 1112 Address fp = Magic.getCallerFramePointer(currfp); 1113 // Instruction pointer for current native frame 1114 Address ip; 1115 1116 // Loop until either we fall off the stack or we find an instruction address 1117 // in one of our heaps 1118 do { 1119 callee_fp = fp; 1120 ip = Magic.getReturnAddressUnchecked(fp); 1121 fp = Magic.getCallerFramePointer(fp); 1122 } while (!MemoryManager.addressInVM(ip) && fp.NE(StackFrameLayout.getStackFrameSentinelFP())); 1123 1124 if (VM.BuildForPowerPC) { 1125 // We want to return fp, not callee_fp because we want the stack walkers 1126 // to see the "mini-frame" which has the RVM information, not the "main frame" 1127 // pointed to by callee_fp which is where the saved ip was actually stored. 1128 return fp; 1129 } else { 1130 return callee_fp; 1131 } 1132 } 1133 1134 /** 1135 * The current frame is expected to be one of the JNI functions 1136 * called from C, 1137 * below which is one or more native stack frames. 1138 * Skip over all frames below which do not contain any object 1139 * references. 1140 * 1141 * @param currfp the frame pointer of the current frame 1142 * @return the frame pointer for the appropriate frame 1143 */ 1144 @Uninterruptible 1145 public static Address unwindNativeStackFrameForGC(Address currfp) { 1146 return unwindNativeStackFrame(currfp); 1147 } 1148 1149 /** 1150 * Unwind stack frame for an <invisible method>. 1151 * See also: ExceptionDeliverer.unwindStackFrame() 1152 * <p> 1153 * !!TODO: Could be a reflective method invoker frame. 1154 * Does it clobber any non-volatiles? 1155 * If so, how do we restore them? 1156 * (I don't think our current implementations of reflective method 1157 * invokers save/restore any nonvolatiles, so we're probably ok. 1158 * --dave 6/29/01 1159 * 1160 * @param registers exception registers 1161 */ 1162 @Uninterruptible 1163 private static void unwindInvisibleStackFrame(AbstractRegisters registers) { 1164 registers.unwindStackFrame(); 1165 } 1166 1167 /** 1168 * Number of allocations left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0. 1169 */ 1170 static int allocationCountDownToGC = VM.StressGCAllocationInterval; 1171 1172 /** 1173 * Number of c-to-java jni calls left before a GC is forced. Only used if VM.StressGCAllocationInterval is not 0. 1174 */ 1175 static int jniCountDownToGC = VM.StressGCAllocationInterval; 1176 1177 /** 1178 * Check to see if we are stress testing garbage collector and if another JNI call should 1179 * trigger a gc then do so. 1180 */ 1181 @Inline 1182 public static void checkJNICountDownToGC() { 1183 // Temporarily disabled as it will causes nightly to take too long to run 1184 // There should be a mechanism to optionally enable this countdown in Configuration 1185 if (false && canForceGC()) { 1186 if (jniCountDownToGC-- <= 0) { 1187 jniCountDownToGC = VM.StressGCAllocationInterval; 1188 System.gc(); 1189 } 1190 } 1191 } 1192 1193 /** 1194 * Check to see if we are stress testing garbage collector and if another allocation should 1195 * trigger a GC then do so. 1196 */ 1197 @Inline 1198 private static void checkAllocationCountDownToGC() { 1199 if (canForceGC()) { 1200 if (allocationCountDownToGC-- <= 0) { 1201 allocationCountDownToGC = VM.StressGCAllocationInterval; 1202 System.gc(); 1203 } 1204 } 1205 } 1206 1207 /** 1208 * @return {@code true} if we are stress testing garbage collector and the 1209 * system is in state where we can force a garbage collection. 1210 */ 1211 @Inline 1212 @Uninterruptible 1213 private static boolean canForceGC() { 1214 return VM.ForceFrequentGC && RVMThread.safeToForceGCs() && MemoryManager.collectionEnabled(); 1215 } 1216}