Skip to content
Open
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
8 changes: 8 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# General build settings
build --cxxopt='-std=c++20'

# Set Java compiler options
build --java_language_version=17
build --java_runtime_version=17
build --tool_java_language_version=17
build --tool_java_runtime_version=17
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
bazel*
.vscode
MODULE.bazel.lock
1 change: 1 addition & 0 deletions implementation/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1331,3 +1331,4 @@ cc_library(
name = "void",
hdrs = ["void.h"],
)

60 changes: 56 additions & 4 deletions implementation/array_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,37 @@ class ArrayRef : public ScopedArrayImpl<JniT> {

explicit ArrayRef(int size) : ArrayRef(static_cast<std::size_t>(size)) {}

ArrayView<SpanType, JniT::kRank> Pin(bool copy_on_completion = true) {
return {Base::object_ref_, copy_on_completion, Length()};
// Pin the array to get direct access to its elements
//
// copy_on_completion: Whether to copy changes back to the Java array when the
// ArrayView is destroyed (default: true)
// access_mode: The access mode to use (REGULAR for normal JNI access,
// CRITICAL for GetPrimitiveArrayCritical)
//
// Returns: An ArrayView that provides access to the array elements
//
// When access_mode is REGULAR, this uses the standard JNI Get*ArrayElements functions
// When access_mode is CRITICAL, this uses the JNI GetPrimitiveArrayCritical function
// which may provide a direct pointer to the array in the Java heap without copying
ArrayView<SpanType, JniT::kRank> Pin(bool copy_on_completion = true,
ArrayAccessMode access_mode = ArrayAccessMode::REGULAR) {
return {Base::object_ref_, copy_on_completion, Length(), access_mode};
}

// Convenience method to access array using the critical API
//
// This uses GetPrimitiveArrayCritical which may provide direct access to the
// array memory without copying. During critical access:
// - No other JNI calls should be made
// - The critical section should be as short as possible
// - The JVM's garbage collector may be blocked
//
// copy_on_completion: Whether to copy changes back to the Java array when the
// ArrayView is destroyed (default: true)
//
// Returns: An ArrayView that provides access to the array elements
ArrayView<SpanType, JniT::kRank> PinCritical(bool copy_on_completion = true) {
return Pin(copy_on_completion, ArrayAccessMode::CRITICAL);
}

std::size_t Length() {
Expand Down Expand Up @@ -96,8 +125,31 @@ class ArrayRefBase : public ScopedArrayImpl<JniT> {
static_cast<jobject>(obj))) {}

// Object arrays cannot be efficiently pinned like primitive types can.
ArrayView<SpanType, JniT::kRank> Pin() {
return {Base::object_ref_, false, Length()};
// The access_mode parameter is ignored for object arrays as they cannot use
// the GetPrimitiveArrayCritical API.
//
// copy_on_completion: Whether to copy changes back to the Java array when the
// ArrayView is destroyed (default: false)
// access_mode: Ignored for object arrays
//
// Returns: An ArrayView that provides access to the array elements
ArrayView<SpanType, JniT::kRank> Pin(bool copy_on_completion = false,
ArrayAccessMode access_mode = ArrayAccessMode::REGULAR) {
return {Base::object_ref_, copy_on_completion, Length()};
}

// For API consistency only - objects cannot use critical access
//
// Note: This method exists for API consistency, but object arrays
// cannot use GetPrimitiveArrayCritical. This call is equivalent to
// calling Pin() with REGULAR access mode.
//
// copy_on_completion: Whether to copy changes back to the Java array when the
// ArrayView is destroyed (default: false)
//
// Returns: An ArrayView that provides access to the array elements
ArrayView<SpanType, JniT::kRank> PinCritical(bool copy_on_completion = false) {
return Pin(copy_on_completion, ArrayAccessMode::REGULAR);
}

std::size_t Length() {
Expand Down
17 changes: 13 additions & 4 deletions implementation/array_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,24 @@ class ArrayView {
ArrayView(ArrayView&&) = delete;
ArrayView(const ArrayView&) = delete;

ArrayView(jarray array, bool copy_on_completion, std::size_t size)
// Constructor for ArrayView
// array: The Java array to access
// copy_on_completion: Whether to copy changes back to the Java array on destruction
// size: The size of the array
// access_mode: The access mode to use (REGULAR for normal JNI access,
// CRITICAL for GetPrimitiveArrayCritical)
ArrayView(jarray array, bool copy_on_completion, std::size_t size,
ArrayAccessMode access_mode = ArrayAccessMode::REGULAR)
: array_(array),
get_array_elements_result_(
JniArrayHelper<SpanType, kRank>::GetArrayElements(array)),
JniArrayHelper<SpanType, kRank>::GetArrayElements(array, access_mode)),
copy_on_completion_(copy_on_completion),
size_(size) {}
size_(size),
access_mode_(access_mode) {}

~ArrayView() {
JniArrayHelper<SpanType, kRank>::ReleaseArrayElements(
array_, get_array_elements_result_.ptr_, copy_on_completion_);
array_, get_array_elements_result_.ptr_, copy_on_completion_, access_mode_);
}

// Arrays of rank > 1 are object arrays which are not contiguous.
Expand All @@ -119,6 +127,7 @@ class ArrayView {
const GetArrayElementsResult<SpanType> get_array_elements_result_;
const bool copy_on_completion_;
const std::size_t size_;
const ArrayAccessMode access_mode_;
};

// Metafunction that returns the type after a single dereference.
Expand Down
81 changes: 81 additions & 0 deletions implementation/array_view_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,87 @@ TEST_F(JniTest, LocalArrayView_AllowsCTAD) {
// ArrayView ctad_array_view_2 {std::move(ctad_array_view)};
}

TEST_F(JniTest, ArrayView_GetsAndReleaseCriticalArrayBuffer) {
EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jbooleanArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jboolean*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jbooleanArray>()),
Eq(static_cast<void*>(Fake<jboolean*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jbyteArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jbyte*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jbyteArray>()),
Eq(static_cast<void*>(Fake<jbyte*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jcharArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jchar*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jcharArray>()),
Eq(static_cast<void*>(Fake<jchar*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jshortArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jshort*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jshortArray>()),
Eq(static_cast<void*>(Fake<jshort*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jintArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jint*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jintArray>()),
Eq(static_cast<void*>(Fake<jint*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jlongArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jlong*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jlongArray>()),
Eq(static_cast<void*>(Fake<jlong*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jfloatArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jfloat*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jfloatArray>()),
Eq(static_cast<void*>(Fake<jfloat*>())), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jdoubleArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jdouble*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jdoubleArray>()),
Eq(static_cast<void*>(Fake<jdouble*>())), 0));

LocalArray<jboolean> boolean_array{AdoptLocal{}, Fake<jbooleanArray>()};
LocalArray<jbyte> byte_array{AdoptLocal{}, Fake<jbyteArray>()};
LocalArray<jchar> char_array{AdoptLocal{}, Fake<jcharArray>()};
LocalArray<jshort> short_array{AdoptLocal{}, Fake<jshortArray>()};
LocalArray<jint> int_array{AdoptLocal{}, Fake<jintArray>()};
LocalArray<jlong> long_array{AdoptLocal{}, Fake<jlongArray>()};
LocalArray<jfloat> float_array{AdoptLocal{}, Fake<jfloatArray>()};
LocalArray<jdouble> double_array{AdoptLocal{}, Fake<jdoubleArray>()};

// Test using PinCritical convenience method
ArrayView<jboolean, 1> boolean_array_pin = {boolean_array.PinCritical()};
ArrayView<jbyte, 1> byte_array_pin = {byte_array.PinCritical()};
ArrayView<jint, 1> int_array_pin = {int_array.PinCritical()};
ArrayView<jchar, 1> char_array_pin = {char_array.PinCritical()};
ArrayView<jshort, 1> short_array_pin = {short_array.PinCritical()};
ArrayView<jlong, 1> long_array_pin = {long_array.PinCritical()};
ArrayView<jfloat, 1> float_array_pin = {float_array.PinCritical()};
ArrayView<jdouble, 1> double_array_pin = {double_array.PinCritical()};
}

TEST_F(JniTest, ArrayView_GetsAndReleaseWithExplicitAccessMode) {
EXPECT_CALL(*env_, GetIntArrayElements(Eq(Fake<jintArray>()), _))
.WillOnce(::testing::Return(Fake<jint*>()));
EXPECT_CALL(*env_, ReleaseIntArrayElements(Eq(Fake<jintArray>()),
Eq(Fake<jint*>()), 0));

EXPECT_CALL(*env_, GetPrimitiveArrayCritical(Eq(Fake<jintArray>()), _))
.WillOnce(::testing::Return(static_cast<void*>(Fake<jint*>())));
EXPECT_CALL(*env_, ReleasePrimitiveArrayCritical(Eq(Fake<jintArray>()),
Eq(static_cast<void*>(Fake<jint*>())), 0));

LocalArray<jint> int_array{AdoptLocal{}, Fake<jintArray>()};

// Test using Pin with explicit access mode
ArrayView<jint, 1> int_array_pin_regular = {
int_array.Pin(true, jni::ArrayAccessMode::REGULAR)};
ArrayView<jint, 1> int_array_pin_critical = {
int_array.Pin(true, jni::ArrayAccessMode::CRITICAL)};
}

TEST_F(JniTest, ArrayView_ConstructsFromAnObject) {
static constexpr Class kClass{"kClass"};
LocalArray<jobject, 1, kClass> local_obj_array{1, LocalObject<kClass>{}};
Expand Down
6 changes: 6 additions & 0 deletions implementation/jni_helper/get_array_element_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ struct GetArrayElementsResult {
jboolean is_copy;
};

// Enum to specify array access mode (regular vs. critical)
enum class ArrayAccessMode {
REGULAR, // Uses Get*ArrayElements API
CRITICAL // Uses GetPrimitiveArrayCritical API
};

} // namespace jni

#endif // JNI_BIND_IMPLEMENTATION_JNI_HELPER_GET_ARRAY_ELEMENT_RESULT_H_
Loading
Loading