diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java index 6c5d6417dd45..8269d6e98ef4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java @@ -38,10 +38,7 @@ * This class implements {@link ObjectHandle word}-sized integer handles that refer to Java objects. * {@link #create(Object) Creating}, {@link #get(ObjectHandle) dereferencing} and * {@link #destroy(ObjectHandle) destroying} handles is thread-safe and the handles themselves are - * valid across threads. This class also supports weak handles, with which the referenced object may - * be garbage-collected, after which {@link #get(ObjectHandle)} returns {@code null}. Still, weak - * handles must also be {@link #destroyWeak(ObjectHandle) explicitly destroyed} to reclaim their - * handle value. + * valid across threads. *

* The implementation uses a variable number of object arrays, in which each array element * represents a handle. The array element's index determines the handle's integer value, and the @@ -53,13 +50,6 @@ */ public final class ObjectHandlesImpl implements ObjectHandles { - /** Private subclass to distinguish from regular handles to {@link WeakReference} objects. */ - private static final class HandleWeakReference extends WeakReference { - HandleWeakReference(T referent) { - super(referent); - } - } - private static final int MAX_FIRST_BUCKET_CAPACITY = 1024; static { // must be a power of 2 for the arithmetic below to work assert Integer.lowestOneBit(MAX_FIRST_BUCKET_CAPACITY) == MAX_FIRST_BUCKET_CAPACITY; @@ -210,17 +200,10 @@ public ObjectHandle create(Object obj) { } } - public ObjectHandle createWeak(Object obj) { - return create(new HandleWeakReference<>(obj)); - } - @SuppressWarnings("unchecked") @Override public T get(ObjectHandle handle) { Object obj = doGet(handle); - if (obj instanceof HandleWeakReference) { - obj = ((HandleWeakReference) obj).get(); - } return (T) obj; } @@ -240,10 +223,6 @@ private Object doGet(ObjectHandle handle) { return Unsafe.getUnsafe().getReferenceVolatile(bucket, getObjectArrayByteOffset(indexInBucket)); } - public boolean isWeak(ObjectHandle handle) { - return (doGet(handle) instanceof HandleWeakReference); - } - @Override public void destroy(ObjectHandle handle) { if (handle.equal(nullHandle)) { @@ -261,10 +240,6 @@ public void destroy(ObjectHandle handle) { Unsafe.getUnsafe().putReferenceRelease(bucket, getObjectArrayByteOffset(indexInBucket), null); } - public void destroyWeak(ObjectHandle handle) { - destroy(handle); - } - public long computeCurrentCount() { long count = 0; int bucketIndex = 0; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java index 91b39f6ec6eb..13e8a954716d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java @@ -220,6 +220,7 @@ public static void deleteLocalRef(JNIObjectHandle localRef) { } } + // in frames are local handles stored public static int pushLocalFrame(int capacity) { return getOrCreateLocals().pushFrame(capacity); } @@ -228,6 +229,7 @@ public static void popLocalFrame() { getExistingLocals().popFrame(); } + // pops frames down to a specific starting point identified by the int frame @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static void popLocalFramesIncluding(int frame) { getExistingLocals().popFramesIncluding(frame); @@ -293,19 +295,37 @@ static long computeCurrentGlobalHandleCount() { * for example by native code that is unaware of isolates. */ final class JNIGlobalHandles { - static final SignedWord MIN_VALUE = Word.signed(Long.MIN_VALUE); - static final SignedWord MAX_VALUE = JNIObjectHandles.nullHandle().subtract(1); + static final SignedWord MIN_VALUE = Word.signed(Long.MIN_VALUE); // -2^63 + static final SignedWord MAX_VALUE = JNIObjectHandles.nullHandle().subtract(1); // -1 + static { assert JNIObjectHandles.nullHandle().equal(Word.zero()); } + // Handle=(MSB)+(Validation Tag)+(Handle Index) private static final int HANDLE_BITS_COUNT = 31; private static final SignedWord HANDLE_BITS_MASK = Word.signed((1L << HANDLE_BITS_COUNT) - 1); private static final int VALIDATION_BITS_SHIFT = HANDLE_BITS_COUNT; - private static final int VALIDATION_BITS_COUNT = 32; + private static final int VALIDATION_BITS_COUNT = 31; private static final SignedWord VALIDATION_BITS_MASK = Word.signed((1L << VALIDATION_BITS_COUNT) - 1).shiftLeft(VALIDATION_BITS_SHIFT); + private static final SignedWord WEAK_HANDLE_FLAG = Word.signed(1L << 62); private static final SignedWord MSB = Word.signed(1L << 63); - private static final ObjectHandlesImpl globalHandles = new ObjectHandlesImpl(JNIObjectHandles.nullHandle().add(1), HANDLE_BITS_MASK, JNIObjectHandles.nullHandle()); + + // Define the mid-point to split the range in half + private static final SignedWord HANDLE_RANGE_SPLIT_POINT = Word.signed(1L << 30); + + // Strong global handles will occupy the lower half of the global handles range + public static final SignedWord STRONG_GLOBAL_RANGE_MIN = JNIObjectHandles.nullHandle().add(1);; + public static final SignedWord STRONG_GLOBAL_RANGE_MAX = HANDLE_RANGE_SPLIT_POINT.subtract(1); + + // Weak global handles will occupy the upper half of the global handles range + public static final SignedWord WEAK_GLOBAL_RANGE_MIN = HANDLE_RANGE_SPLIT_POINT; + public static final SignedWord WEAK_GLOBAL_RANGE_MAX = HANDLE_BITS_MASK; + + private static final ObjectHandlesImpl strongGlobalHandles + = new ObjectHandlesImpl(STRONG_GLOBAL_RANGE_MIN, STRONG_GLOBAL_RANGE_MAX, JNIObjectHandles.nullHandle()); + private static final ObjectHandlesImpl weakGlobalHandles + = new ObjectHandlesImpl(WEAK_GLOBAL_RANGE_MIN, WEAK_GLOBAL_RANGE_MAX, JNIObjectHandles.nullHandle()); @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) static boolean isInRange(JNIObjectHandle handle) { @@ -317,7 +337,20 @@ private static Word isolateHash() { return Word.unsigned(isolateHash); } - private static JNIObjectHandle encode(ObjectHandle handle) { + /** + * Encodes a raw {@code ObjectHandle} into a strong {@code JNIObjectHandle}. + * * A strong handle guarantees the referenced object remains alive as long as + * the handle itself exists. + * * The handle is encoded by: + * 1. Asserting the handle fits within the available bit range. + * 2. Inserting validation bits (derived from the isolate hash) for security. + * 3. Setting the Most Significant Bit (MSB, bit 63) to mark it as an encoded handle. + * 4. The WEAK_HANDLE_FLAG bit (bit 62) remains 0. + * + * @param handle The raw, unencoded handle to the Java object. + * @return The resulting strong JNI object handle with embedded metadata. + */ + private static JNIObjectHandle encodeStrong(ObjectHandle handle) { SignedWord h = (Word) handle; if (JNIObjectHandles.haveAssertions()) { assert h.and(HANDLE_BITS_MASK).equal(h) : "unencoded handle must fit in range"; @@ -330,43 +363,74 @@ private static JNIObjectHandle encode(ObjectHandle handle) { return (JNIObjectHandle) h; } + /** + * Encodes a raw {@code ObjectHandle} into a weak {@code JNIObjectHandle}. + * * A weak handle allows the referenced object to be garbage collected even + * if the handle exists. The handle will be cleared when the object dies. + * * This method calls {@link #encodeStrong(ObjectHandle)} to perform all + * common encoding steps, and then explicitly sets the {@code WEAK_HANDLE_FLAG} + * bit (bit 62) to mark the handle as weak. + * + * @param handle The raw, unencoded handle to the Java object. + * @return The resulting weak JNI object handle with embedded metadata. + */ + private static JNIObjectHandle encodeWeak(ObjectHandle handle) { + SignedWord h = (Word) encodeStrong(handle); + h = h.or(WEAK_HANDLE_FLAG); + assert isInRange((JNIObjectHandle) h); + return (JNIObjectHandle) h; + } + private static ObjectHandle decode(JNIObjectHandle handle) { assert isInRange(handle); assert ((Word) handle).and(VALIDATION_BITS_MASK).unsignedShiftRight(VALIDATION_BITS_SHIFT) - .equal(isolateHash()) : "mismatching validation value -- passed a handle from a different isolate?"; + .equal(isolateHash()) : "mismatching validation value -- passed a handle from a different isolate?"; return (ObjectHandle) HANDLE_BITS_MASK.and((Word) handle); } static T getObject(JNIObjectHandle handle) { - return globalHandles.get(decode(handle)); + SignedWord handleValue = (Word) handle; + if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 0) { + return strongGlobalHandles.get(decode(handle)); + } + + if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 1) { + return weakGlobalHandles.get(decode((handle))); + } + + throw new IllegalArgumentException("Invalid handle"); } static JNIObjectRefType getHandleType(JNIObjectHandle handle) { - assert isInRange(handle); - if (globalHandles.isWeak(decode(handle))) { + SignedWord handleValue = (Word) handle; + if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 0) { + return JNIObjectRefType.Global; + } + + if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 1) { return JNIObjectRefType.WeakGlobal; } - return JNIObjectRefType.Global; + return JNIObjectRefType.Invalid; } static JNIObjectHandle create(Object obj) { - return encode(globalHandles.create(obj)); + return encodeStrong(strongGlobalHandles.create(obj)); } static void destroy(JNIObjectHandle handle) { - globalHandles.destroy(decode(handle)); + strongGlobalHandles.destroy(decode(handle)); } static JNIObjectHandle createWeak(Object obj) { - return encode(globalHandles.createWeak(obj)); + return encodeWeak(weakGlobalHandles.create(obj)); } static void destroyWeak(JNIObjectHandle weakRef) { - globalHandles.destroyWeak(decode(weakRef)); + weakGlobalHandles.destroy(decode(weakRef)); } public static long computeCurrentCount() { - return globalHandles.computeCurrentCount(); + return strongGlobalHandles.computeCurrentCount() + weakGlobalHandles.computeCurrentCount(); } } @@ -375,7 +439,7 @@ public static long computeCurrentCount() { * move and are not garbage-collected, so the handle just contains an object's offset in the image * heap. This approach has the major benefit that handles are valid across isolates that are created * from the same image, which helps with native code that is unaware of multiple isolates. - * + *

* Although this type of handle doesn't need explicit management, we still distinguish between * local, global and weak-global references by means of a bit pattern in order to comply with the * JNI specification (in particular, for function {@code GetObjectRefType}).