Skip to content

Commit 29dee8d

Browse files
committed
Final cleanup for handle encoding
1 parent a5f5a0c commit 29dee8d

File tree

2 files changed

+54
-22
lines changed

2 files changed

+54
-22
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ObjectHandlesImpl.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@
3838
* This class implements {@link ObjectHandle word}-sized integer handles that refer to Java objects.
3939
* {@link #create(Object) Creating}, {@link #get(ObjectHandle) dereferencing} and
4040
* {@link #destroy(ObjectHandle) destroying} handles is thread-safe and the handles themselves are
41-
* valid across threads. This class also supports weak handles, with which the referenced object may
42-
* be garbage-collected, after which {@link #get(ObjectHandle)} returns {@code null}. Still, weak
43-
* handles must also be {@link #destroyWeak(ObjectHandle) explicitly destroyed} to reclaim their
44-
* handle value.
41+
* valid across threads.
4542
* <p>
4643
* The implementation uses a variable number of object arrays, in which each array element
4744
* represents a handle. The array element's index determines the handle's integer value, and the
@@ -53,8 +50,6 @@
5350
*/
5451
public final class ObjectHandlesImpl implements ObjectHandles {
5552

56-
/** Private subclass to distinguish from regular handles to {@link WeakReference} objects. */
57-
5853
private static final int MAX_FIRST_BUCKET_CAPACITY = 1024;
5954
static { // must be a power of 2 for the arithmetic below to work
6055
assert Integer.lowestOneBit(MAX_FIRST_BUCKET_CAPACITY) == MAX_FIRST_BUCKET_CAPACITY;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIObjectHandles.java

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
* </ul>
6666
*/
6767
public final class JNIObjectHandles {
68-
@Fold // replaces with true/false at compile time and removes unnecessary runtime checks
68+
@Fold
6969
static boolean haveAssertions() {
7070
return RuntimeAssertionsSupport.singleton().desiredAssertionStatus(JNIObjectHandles.class);
7171
}
@@ -219,6 +219,7 @@ public static void deleteLocalRef(JNIObjectHandle localRef) {
219219
getOrCreateLocals().delete(decodeLocal(localRef));
220220
}
221221
}
222+
222223
// in frames are local handles stored
223224
public static int pushLocalFrame(int capacity) {
224225
return getOrCreateLocals().pushFrame(capacity);
@@ -227,6 +228,7 @@ public static int pushLocalFrame(int capacity) {
227228
public static void popLocalFrame() {
228229
getExistingLocals().popFrame();
229230
}
231+
230232
// pops frames down to a specific starting point identified by the int frame
231233
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
232234
public static void popLocalFramesIncluding(int frame) {
@@ -295,6 +297,7 @@ static long computeCurrentGlobalHandleCount() {
295297
final class JNIGlobalHandles {
296298
static final SignedWord MIN_VALUE = Word.signed(Long.MIN_VALUE); // -2^63
297299
static final SignedWord MAX_VALUE = JNIObjectHandles.nullHandle().subtract(1); // -1
300+
298301
static {
299302
assert JNIObjectHandles.nullHandle().equal(Word.zero());
300303
}
@@ -311,6 +314,23 @@ final class JNIGlobalHandles {
311314
// Define the mid-point to split the range in half
312315
private static final SignedWord HANDLE_RANGE_SPLIT_POINT = Word.signed(1L << 30);
313316

317+
// Strong global handles will occupy the lower half of the global handles range
318+
public static final SignedWord STRONG_GLOBAL_RANGE_MIN = JNIObjectHandles.nullHandle().add(1);
319+
;
320+
public static final SignedWord STRONG_GLOBAL_RANGE_MAX = HANDLE_RANGE_SPLIT_POINT.subtract(1);
321+
322+
// Weak global handles will occupy the upper half of the global handles range
323+
public static final SignedWord WEAK_GLOBAL_RANGE_MIN = HANDLE_RANGE_SPLIT_POINT;
324+
public static final SignedWord WEAK_GLOBAL_RANGE_MAX = HANDLE_BITS_MASK;
325+
326+
private static final ObjectHandlesImpl strongGlobalHandles
327+
= new ObjectHandlesImpl(STRONG_GLOBAL_RANGE_MIN, STRONG_GLOBAL_RANGE_MAX, JNIObjectHandles.nullHandle());
328+
private static final ObjectHandlesImpl weakGlobalHandles
329+
= new ObjectHandlesImpl(WEAK_GLOBAL_RANGE_MIN, WEAK_GLOBAL_RANGE_MAX, JNIObjectHandles.nullHandle());
330+
331+
// Define the mid-point to split the range in half
332+
private static final SignedWord HANDLE_RANGE_SPLIT_POINT = Word.signed(1L << 30);
333+
314334
// Strong global handles will occupy the lower half of the global handles range
315335
public static final SignedWord STRONG_GLOBAL_RANGE_MIN = JNIObjectHandles.nullHandle().add(1);;
316336
public static final SignedWord STRONG_GLOBAL_RANGE_MAX = HANDLE_RANGE_SPLIT_POINT.subtract(1);
@@ -334,6 +354,19 @@ private static Word isolateHash() {
334354
return Word.unsigned(isolateHash);
335355
}
336356

357+
/**
358+
* Encodes a raw {@code ObjectHandle} into a strong {@code JNIObjectHandle}.
359+
* * A strong handle guarantees the referenced object remains alive as long as
360+
* the handle itself exists.
361+
* * The handle is encoded by:
362+
* 1. Asserting the handle fits within the available bit range.
363+
* 2. Inserting validation bits (derived from the isolate hash) for security.
364+
* 3. Setting the Most Significant Bit (MSB, bit 63) to mark it as an encoded handle.
365+
* 4. The WEAK_HANDLE_FLAG bit (bit 62) remains 0.
366+
*
367+
* @param handle The raw, unencoded handle to the Java object.
368+
* @return The resulting strong JNI object handle with embedded metadata.
369+
*/
337370
private static JNIObjectHandle encodeStrong(ObjectHandle handle) {
338371
SignedWord h = (Word) handle;
339372
if (JNIObjectHandles.haveAssertions()) {
@@ -347,34 +380,38 @@ private static JNIObjectHandle encodeStrong(ObjectHandle handle) {
347380
return (JNIObjectHandle) h;
348381
}
349382

383+
/**
384+
* Encodes a raw {@code ObjectHandle} into a weak {@code JNIObjectHandle}.
385+
* * A weak handle allows the referenced object to be garbage collected even
386+
* if the handle exists. The handle will be cleared when the object dies.
387+
* * This method calls {@link #encodeStrong(ObjectHandle)} to perform all
388+
* common encoding steps, and then explicitly sets the {@code WEAK_HANDLE_FLAG}
389+
* bit (bit 62) to mark the handle as weak.
390+
*
391+
* @param handle The raw, unencoded handle to the Java object.
392+
* @return The resulting weak JNI object handle with embedded metadata.
393+
*/
350394
private static JNIObjectHandle encodeWeak(ObjectHandle handle) {
351-
SignedWord h = (Word) handle;
352-
if (JNIObjectHandles.haveAssertions()) {
353-
assert h.and(HANDLE_BITS_MASK).equal(h) : "unencoded handle must fit in range";
354-
Word v = isolateHash().shiftLeft(VALIDATION_BITS_SHIFT);
355-
assert v.and(VALIDATION_BITS_MASK).equal(v) : "validation value must fit in its range";
356-
h = h.or(v);
357-
}
358-
h = h.or(MSB);
359-
h = h.or(WEAK_HANDLE_FLAG); // Set bit 62 to mark it as weak
395+
SignedWord h = (Word) encodeStrong(handle);
396+
h = h.or(WEAK_HANDLE_FLAG);
360397
assert isInRange((JNIObjectHandle) h);
361398
return (JNIObjectHandle) h;
362399
}
363400

364401
private static ObjectHandle decode(JNIObjectHandle handle) {
365402
assert isInRange(handle);
366403
assert ((Word) handle).and(VALIDATION_BITS_MASK).unsignedShiftRight(VALIDATION_BITS_SHIFT)
367-
.equal(isolateHash()) : "mismatching validation value -- passed a handle from a different isolate?";
404+
.equal(isolateHash()) : "mismatching validation value -- passed a handle from a different isolate?";
368405
return (ObjectHandle) HANDLE_BITS_MASK.and((Word) handle);
369406
}
370407

371408
static <T> T getObject(JNIObjectHandle handle) {
372409
SignedWord handleValue = (Word) handle;
373-
if (handleValue.greaterOrEqual(STRONG_GLOBAL_RANGE_MIN) && handleValue.lessOrEqual(STRONG_GLOBAL_RANGE_MAX)) {
410+
if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 0) {
374411
return strongGlobalHandles.get(decode(handle));
375412
}
376413

377-
if (handleValue.greaterOrEqual(WEAK_GLOBAL_RANGE_MIN) && handleValue.lessOrEqual(WEAK_GLOBAL_RANGE_MAX)) {
414+
if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 1) {
378415
return weakGlobalHandles.get(decode((handle)));
379416
}
380417

@@ -383,11 +420,11 @@ static <T> T getObject(JNIObjectHandle handle) {
383420

384421
static JNIObjectRefType getHandleType(JNIObjectHandle handle) {
385422
SignedWord handleValue = (Word) handle;
386-
if (handleValue.greaterOrEqual(STRONG_GLOBAL_RANGE_MIN) && handleValue.lessOrEqual(STRONG_GLOBAL_RANGE_MAX)) {
423+
if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 0) {
387424
return JNIObjectRefType.Global;
388425
}
389426

390-
if (handleValue.greaterOrEqual(WEAK_GLOBAL_RANGE_MIN) && handleValue.lessOrEqual(WEAK_GLOBAL_RANGE_MAX)) {
427+
if ((handleValue.toLong() & WEAK_HANDLE_FLAG.toLong()) == 1) {
391428
return JNIObjectRefType.WeakGlobal;
392429
}
393430
return JNIObjectRefType.Invalid;
@@ -419,7 +456,7 @@ public static long computeCurrentCount() {
419456
* move and are not garbage-collected, so the handle just contains an object's offset in the image
420457
* heap. This approach has the major benefit that handles are valid across isolates that are created
421458
* from the same image, which helps with native code that is unaware of multiple isolates.
422-
*
459+
* <p>
423460
* Although this type of handle doesn't need explicit management, we still distinguish between
424461
* local, global and weak-global references by means of a bit pattern in order to comply with the
425462
* JNI specification (in particular, for function {@code GetObjectRefType}).

0 commit comments

Comments
 (0)