Jonathan Garcia
Jonathan Garcia

Reputation: 33

How to Replace Unsafe with VarHandle or Foreign API

I've had a hard time removing usages of Unsafe and replacing with either VarHandle or MemorySegment/MemoryLayout. The goal is to remove usages of Unsafe and replace with nondeprecated API's.

Two examples are:

private static long methodA(Buffer x) {    
   return UNSAFE.getLong(x, addressFieldOffset); 
}

and

private static Object methodB(ByteBuffer x) {    
   return UNSAFE.getObject(x, hbFieldOffset); 
}

Firstly, creating the right VarHandle or MemorySegment has been a challenge. It seems when I end up using the documentation's suggested "Use VarHandle.get() or MemorySegment.get(ValueLayout.ofLong, long)" I end up with a WrongMethodTypeException or a different result than before that breaks current tests, etc. Can I get some help on the appropriate use?

I've tried:

MemorySegment segment = MemorySegment.ofBuffer(x);
long result = segment.get(ValueLayout.ofLong, int offset) // I've tried several different layouts and different offsets,

either getting a WrongMethodTypeException or an incompatible offset error

Also tried:

MemorySegment segment = MemorySegment.ofArray(new long[10]); // also tried with byte[10] and int[10]

With varHandle I've tried:

VarHandle handle = MethodHandles.arrayElementVarHandle(long[].class); \\ also tried byte[].class, int[].class, and long[].class

Buffer bufferField = Buffer.allocate(16)

VarHandle handle = MethodHandles.lookup().findStaticVarHandle(ClassIAmIn.class, "bufferField", Buffer.class);

Usually get a Cannot convert MethodHandles(VarHandle, Buffer, long) to (VarHandle)Buffer or something along those lines.

Upvotes: 3

Views: 140

Answers (2)

Louis Wasserman
Louis Wasserman

Reputation: 198391

You should not be trying to access the address or array of ByteBuffers. That's still unsafe, and there's a reason they're removing Unsafe.

You should be using byteBufferViewVarHandle to replace the places where you use unsafe accessors of the byte buffer.

This allows you to do the important things you might have done with the unsafe access to the address and buffer fields. If that doesn't satisfy your needs, then what you should be doing is asking how to replace that with VarHandle instead of using these functions.

You should definitely not have to open any modules.

Upvotes: 1

Rob Spoor
Rob Spoor

Reputation: 9175

I'm assuming that addressFieldOffset is the offset of field address of class java.nio.Buffer, and hbFieldOffset is the offset of field hb of class java.nio.ByteBuffer.

You're trying to access private fields. That means you need a private method lookup first:

MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Buffer.class, MethodHandles.lookup());

You need to open package java.nio of module java.base to your module (ALL-UNNAMED if your module is unnamed), or you'll get an exception:

Exception java.lang.IllegalAccessException: module java.base does not open java.nio to unnamed module @7e0b0338
      at MethodHandles.privateLookupIn (MethodHandles.java:279)

Now you can lookup the var handles:

VarHandle addressHandle = lookup.findVarHandle(Buffer.class, "address", long.class);
long address = (long) addressHandle.get(buffer);
System.out.printf("%d%n", address);

VarHandle hbHandle = lookup.findVarHandle(ByteBuffer.class, "hb", byte[].class);
byte[] hb = (byte[]) hbHandle.get(buffer);
System.out.printf("%s%n", Arrays.toString(hb));

Upvotes: 1

Related Questions