Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,74 @@
*/
package com.oracle.svm.core.jdk;

import java.nio.Buffer;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.FieldValueTransformer;

import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.config.ObjectLayout;
import com.oracle.svm.core.util.VMError;

import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.vm.ci.meta.JavaKind;

@TargetClass(className = "java.nio.Buffer")
public final class Target_java_nio_Buffer {
@Alias public long address;
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = BufferAddressTransformer.class)//
public long address;
}

/**
* For array-based {@link Buffer} instances, the {@code address} field contains the memory offset of
* the first array element accessed by the buffer, so that Unsafe memory accesses relative to the
* {@code base} are possible. For buffers that are in the image heap, that address must be properly
* recomputed from the hosted array offset to the runtime array offset. Note that the address can
* point into the middle of an array, not just to the first array element. When buffers are wrapped,
* the array kind of the innermost buffer defines the kind of array base offset and array index
* scale to use for the recomputation.
*/
class BufferAddressTransformer implements FieldValueTransformer {
@Override
public Object transform(Object receiver, Object originalValue) {
Buffer buffer = (Buffer) receiver;
long hostedAddress = (Long) originalValue;

if (buffer.isDirect()) {
/*
* Most direct buffers are disallowed in the image heap, but 1) this is not the place to
* raise an error about it, and 2) some 0-length direct buffers are allowed. But no
* address of the image generator can ever be valid at image run time, so we return an
* illegal marker value.
*/
return Long.valueOf(0xDEADBEEF00052885L);
}

Object bufferBase = SharedSecrets.getJavaNioAccess().getBufferBase(buffer);
if (bufferBase == null) {
/*
* For example, StringCharBuffer does not have a backing array because all get()
* operations are forwarded to the wrapped CharSequence.
*/
VMError.guarantee(hostedAddress == 0, "When the buffer does not have a backing array, the address must be unused too: buffer %s of %s, address %s",
buffer, buffer.getClass(), hostedAddress);
return hostedAddress;
}
VMError.guarantee(bufferBase.getClass().isArray(), "Buffer is not backed by an array: buffer %s of %s, address %s", buffer, buffer.getClass(), hostedAddress);

int hostedBaseOffset = Unsafe.getUnsafe().arrayBaseOffset(bufferBase.getClass());
int hostedIndexScale = Unsafe.getUnsafe().arrayIndexScale(bufferBase.getClass());

ObjectLayout layout = ImageSingletons.lookup(ObjectLayout.class);
JavaKind kind = JavaKind.fromJavaClass(bufferBase.getClass().getComponentType());
int runtimeBaseOffset = layout.getArrayBaseOffset(kind);
int runtimeIndexScale = layout.getArrayIndexScale(kind);

VMError.guarantee(hostedIndexScale == runtimeIndexScale, "Currently the hosted and runtime array index scale is always the same, so we do not need to transform");
VMError.guarantee(hostedAddress >= hostedBaseOffset, "invalid address: %s, %s", hostedAddress, hostedBaseOffset);
return Long.valueOf(hostedAddress - hostedBaseOffset + runtimeBaseOffset);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ public final class Target_java_nio_DirectByteBuffer {
* singleton empty buffer referenced from a static field, and a lot of Netty classes reference
* this buffer statically.
*
* Such buffers do actually have an address to memory that is allocated during image generation
* and therefore no longer available at run time. But since the capacity is 0, no memory can
* ever be accessed. We therefore allow this "dangling" address. However, we must never call
* free() for that address, so we remove the Cleaner registered for the buffer by resetting the
* field {@link #cleaner}.
* Such buffers do actually not have a valid address, see {@link BufferAddressTransformer}. But
* since the capacity is 0, no memory can ever be accessed. We therefore allow this "dangling"
* address. However, we must never call free() for that address, so we remove the Cleaner
* registered for the buffer by resetting the field {@link #cleaner}.
*/
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
Target_jdk_internal_ref_Cleaner cleaner;
Expand Down