From 2e4ad35e36e1bddcd6db9677b5dff569aa96aadb Mon Sep 17 00:00:00 2001 From: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com> Date: Thu, 24 Aug 2023 00:02:56 -0700 Subject: [PATCH] [wpiutil] jni_util: Add JSpan and CriticalJSpan (#5554) These replace JArrayRef et al and support statically sized arrays similar to std::span. --- .../src/main/native/cpp/jni/AprilTagJNI.cpp | 21 +- .../main/native/cpp/jni/AddressableLEDJNI.cpp | 7 +- hal/src/main/native/cpp/jni/CANAPIJNI.cpp | 28 +- hal/src/main/native/cpp/jni/CANJNI.cpp | 6 +- hal/src/main/native/cpp/jni/DMAJNI.cpp | 10 +- .../main/native/cpp/jni/DriverStationJNI.cpp | 8 +- hal/src/main/native/cpp/jni/I2CJNI.cpp | 4 +- hal/src/main/native/cpp/jni/SPIJNI.cpp | 13 +- hal/src/main/native/cpp/jni/SerialPortJNI.cpp | 9 +- hal/src/main/native/cpp/jni/SimDeviceJNI.cpp | 2 +- .../jni/simulation/AddressableLEDDataJNI.cpp | 2 +- .../jni/simulation/DriverStationDataJNI.cpp | 4 +- .../src/generate/cpp/jni/types_jni.cpp.jinja | 10 +- ntcore/src/generate/types.json | 8 +- .../main/native/cpp/jni/NetworkTablesJNI.cpp | 3 +- .../src/main/native/cpp/jni/WPIMathJNI.cpp | 114 +++--- .../src/main/native/cpp/jni/DataLogJNI.cpp | 18 +- .../src/main/native/cpp/jni/WPIUtilJNI.cpp | 8 +- .../src/main/native/include/wpi/jni_util.h | 375 ++++++++++-------- 19 files changed, 333 insertions(+), 317 deletions(-) diff --git a/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp b/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp index 31fd4f979f..c957b2da8d 100644 --- a/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp +++ b/apriltag/src/main/native/cpp/jni/AprilTagJNI.cpp @@ -504,15 +504,14 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseHomography nullPointerEx.Throw(env, "homography cannot be null"); return nullptr; } - JDoubleArrayRef harr{env, homography}; + JSpan harr{env, homography}; if (harr.size() != 9) { illegalArgEx.Throw(env, "homography array must be size 9"); return nullptr; } AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy}); - return MakeJObject(env, estimator.EstimateHomography( - std::span{harr.array()})); + return MakeJObject(env, estimator.EstimateHomography(harr)); } /* @@ -530,7 +529,7 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration nullPointerEx.Throw(env, "homography cannot be null"); return nullptr; } - JDoubleArrayRef harr{env, homography}; + JSpan harr{env, homography}; if (harr.size() != 9) { illegalArgEx.Throw(env, "homography array must be size 9"); return nullptr; @@ -541,7 +540,7 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration nullPointerEx.Throw(env, "corners cannot be null"); return nullptr; } - JDoubleArrayRef carr{env, corners}; + JSpan carr{env, corners}; if (carr.size() != 8) { illegalArgEx.Throw(env, "corners array must be size 8"); return nullptr; @@ -549,9 +548,7 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePoseOrthogonalIteration AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy}); return MakeJObject(env, - estimator.EstimateOrthogonalIteration( - std::span{harr.array()}, - std::span{carr.array()}, nIters)); + estimator.EstimateOrthogonalIteration(harr, carr, nIters)); } /* @@ -569,7 +566,7 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePose nullPointerEx.Throw(env, "homography cannot be null"); return nullptr; } - JDoubleArrayRef harr{env, homography}; + JSpan harr{env, homography}; if (harr.size() != 9) { illegalArgEx.Throw(env, "homography array must be size 9"); return nullptr; @@ -580,16 +577,14 @@ Java_edu_wpi_first_apriltag_jni_AprilTagJNI_estimatePose nullPointerEx.Throw(env, "corners cannot be null"); return nullptr; } - JDoubleArrayRef carr{env, corners}; + JSpan carr{env, corners}; if (carr.size() != 8) { illegalArgEx.Throw(env, "corners array must be size 8"); return nullptr; } AprilTagPoseEstimator estimator({units::meter_t{tagSize}, fx, fy, cx, cy}); - return MakeJObject( - env, estimator.Estimate(std::span{harr.array()}, - std::span{carr.array()})); + return MakeJObject(env, estimator.Estimate(harr, carr)); } } // extern "C" diff --git a/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp b/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp index 6fee9ecd12..66e3ed09c7 100644 --- a/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp +++ b/hal/src/main/native/cpp/jni/AddressableLEDJNI.cpp @@ -69,12 +69,11 @@ Java_edu_wpi_first_hal_AddressableLEDJNI_setData (JNIEnv* env, jclass, jint handle, jbyteArray arr) { int32_t status = 0; - JByteArrayRef jArrRef{env, arr}; - auto arrRef = jArrRef.array(); + JSpan jArrRef{env, arr}; HAL_WriteAddressableLEDData( static_cast(handle), - reinterpret_cast(arrRef.data()), - arrRef.size() / 4, &status); + reinterpret_cast(jArrRef.data()), + jArrRef.size() / 4, &status); CheckStatus(env, status); } diff --git a/hal/src/main/native/cpp/jni/CANAPIJNI.cpp b/hal/src/main/native/cpp/jni/CANAPIJNI.cpp index e1299a8f7a..929f7da1ef 100644 --- a/hal/src/main/native/cpp/jni/CANAPIJNI.cpp +++ b/hal/src/main/native/cpp/jni/CANAPIJNI.cpp @@ -71,11 +71,10 @@ Java_edu_wpi_first_hal_CANAPIJNI_writeCANPacket (JNIEnv* env, jclass, jint handle, jbyteArray data, jint apiId) { auto halHandle = static_cast(handle); - JByteArrayRef arr{env, data}; - auto arrRef = arr.array(); + JSpan arr{env, data}; int32_t status = 0; - HAL_WriteCANPacket(halHandle, reinterpret_cast(arrRef.data()), - arrRef.size(), apiId, &status); + HAL_WriteCANPacket(halHandle, reinterpret_cast(arr.data()), + arr.size(), apiId, &status); CheckStatus(env, status); } @@ -90,12 +89,11 @@ Java_edu_wpi_first_hal_CANAPIJNI_writeCANPacketRepeating jint timeoutMs) { auto halHandle = static_cast(handle); - JByteArrayRef arr{env, data}; - auto arrRef = arr.array(); + JSpan arr{env, data}; int32_t status = 0; HAL_WriteCANPacketRepeating(halHandle, - reinterpret_cast(arrRef.data()), - arrRef.size(), apiId, timeoutMs, &status); + reinterpret_cast(arr.data()), + arr.size(), apiId, timeoutMs, &status); CheckStatus(env, status); } @@ -124,11 +122,10 @@ Java_edu_wpi_first_hal_CANAPIJNI_writeCANPacketNoThrow (JNIEnv* env, jclass, jint handle, jbyteArray data, jint apiId) { auto halHandle = static_cast(handle); - JByteArrayRef arr{env, data}; - auto arrRef = arr.array(); + JSpan arr{env, data}; int32_t status = 0; - HAL_WriteCANPacket(halHandle, reinterpret_cast(arrRef.data()), - arrRef.size(), apiId, &status); + HAL_WriteCANPacket(halHandle, reinterpret_cast(arr.data()), + arr.size(), apiId, &status); return status; } @@ -143,12 +140,11 @@ Java_edu_wpi_first_hal_CANAPIJNI_writeCANPacketRepeatingNoThrow jint timeoutMs) { auto halHandle = static_cast(handle); - JByteArrayRef arr{env, data}; - auto arrRef = arr.array(); + JSpan arr{env, data}; int32_t status = 0; HAL_WriteCANPacketRepeating(halHandle, - reinterpret_cast(arrRef.data()), - arrRef.size(), apiId, timeoutMs, &status); + reinterpret_cast(arr.data()), + arr.size(), apiId, timeoutMs, &status); return status; } diff --git a/hal/src/main/native/cpp/jni/CANJNI.cpp b/hal/src/main/native/cpp/jni/CANJNI.cpp index 4ad2a74e04..9375a06e80 100644 --- a/hal/src/main/native/cpp/jni/CANJNI.cpp +++ b/hal/src/main/native/cpp/jni/CANJNI.cpp @@ -26,11 +26,11 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_hal_can_CANJNI_FRCNetCommCANSessionMuxSendMessage (JNIEnv* env, jclass, jint messageID, jbyteArray data, jint periodMs) { - JByteArrayRef dataArray{env, data}; + JSpan dataArray{env, data}; const uint8_t* dataBuffer = - reinterpret_cast(dataArray.array().data()); - uint8_t dataSize = dataArray.array().size(); + reinterpret_cast(dataArray.data()); + uint8_t dataSize = dataArray.size(); int32_t status = 0; HAL_CAN_SendMessage(messageID, dataBuffer, dataSize, periodMs, &status); diff --git a/hal/src/main/native/cpp/jni/DMAJNI.cpp b/hal/src/main/native/cpp/jni/DMAJNI.cpp index 3e95c7b6ac..79dc14b024 100644 --- a/hal/src/main/native/cpp/jni/DMAJNI.cpp +++ b/hal/src/main/native/cpp/jni/DMAJNI.cpp @@ -5,12 +5,15 @@ #include #include +#include + #include "HALUtil.h" #include "edu_wpi_first_hal_DMAJNI.h" #include "hal/DMA.h" #include "hal/handles/HandlesInternal.h" using namespace hal; +using namespace wpi::java; namespace hal { bool GetEncoderBaseHandle(HAL_EncoderHandle handle, @@ -312,20 +315,17 @@ Java_edu_wpi_first_hal_DMAJNI_readDMA env->SetIntArrayRegion(buf, 0, dmaSample.captureSize, reinterpret_cast(dmaSample.readBuffer)); - int32_t* nativeArr = - static_cast(env->GetPrimitiveArrayCritical(store, nullptr)); + CriticalJSpan nativeArr{env, store}; std::copy_n( dmaSample.channelOffsets, sizeof(dmaSample.channelOffsets) / sizeof(dmaSample.channelOffsets[0]), - nativeArr); + nativeArr.data()); nativeArr[22] = static_cast(dmaSample.captureSize); nativeArr[23] = static_cast(dmaSample.triggerChannels); nativeArr[24] = remaining; nativeArr[25] = readStatus; - env->ReleasePrimitiveArrayCritical(store, nativeArr, JNI_ABORT); - return dmaSample.timeStamp; } diff --git a/hal/src/main/native/cpp/jni/DriverStationJNI.cpp b/hal/src/main/native/cpp/jni/DriverStationJNI.cpp index 7cf53dbc96..5e3e97f5f8 100644 --- a/hal/src/main/native/cpp/jni/DriverStationJNI.cpp +++ b/hal/src/main/native/cpp/jni/DriverStationJNI.cpp @@ -251,10 +251,10 @@ Java_edu_wpi_first_hal_DriverStationJNI_getAllJoystickData HAL_GetAllJoystickData(axes, povs, buttons); - CriticalJFloatArrayRef jAxes(env, axesArray); - CriticalJByteArrayRef jRawAxes(env, rawAxesArray); - CriticalJShortArrayRef jPovs(env, povsArray); - CriticalJLongArrayRef jButtons(env, buttonsAndMetadataArray); + CriticalJSpan jAxes(env, axesArray); + CriticalJSpan jRawAxes(env, rawAxesArray); + CriticalJSpan jPovs(env, povsArray); + CriticalJSpan jButtons(env, buttonsAndMetadataArray); static_assert(sizeof(jAxes[0]) == sizeof(axes[0].axes[0])); static_assert(sizeof(jRawAxes[0]) == sizeof(axes[0].raw[0])); diff --git a/hal/src/main/native/cpp/jni/I2CJNI.cpp b/hal/src/main/native/cpp/jni/I2CJNI.cpp index 68b844223a..6e06bb897d 100644 --- a/hal/src/main/native/cpp/jni/I2CJNI.cpp +++ b/hal/src/main/native/cpp/jni/I2CJNI.cpp @@ -79,7 +79,7 @@ Java_edu_wpi_first_hal_I2CJNI_i2CTransactionB jint returnValue = HAL_TransactionI2C(static_cast(port), address, reinterpret_cast( - JByteArrayRef(env, dataToSend).array().data()), + JSpan(env, dataToSend).data()), sendSize, recvBuf.data(), receiveSize); env->SetByteArrayRegion(dataReceived, 0, receiveSize, reinterpret_cast(recvBuf.data())); @@ -120,7 +120,7 @@ Java_edu_wpi_first_hal_I2CJNI_i2CWriteB jint returnValue = HAL_WriteI2C(static_cast(port), address, reinterpret_cast( - JByteArrayRef(env, dataToSend).array().data()), + JSpan(env, dataToSend).data()), sendSize); return returnValue; } diff --git a/hal/src/main/native/cpp/jni/SPIJNI.cpp b/hal/src/main/native/cpp/jni/SPIJNI.cpp index 4f1e5568ef..f8c5f7e3c3 100644 --- a/hal/src/main/native/cpp/jni/SPIJNI.cpp +++ b/hal/src/main/native/cpp/jni/SPIJNI.cpp @@ -94,7 +94,7 @@ Java_edu_wpi_first_hal_SPIJNI_spiTransactionB jint retVal = HAL_TransactionSPI(static_cast(port), reinterpret_cast( - JByteArrayRef(env, dataToSend).array().data()), + JSpan(env, dataToSend).data()), recvBuf.data(), size); env->SetByteArrayRegion(dataReceived, 0, size, reinterpret_cast(recvBuf.data())); @@ -131,7 +131,7 @@ Java_edu_wpi_first_hal_SPIJNI_spiWriteB { jint retVal = HAL_WriteSPI(static_cast(port), reinterpret_cast( - JByteArrayRef(env, dataToSend).array().data()), + JSpan(env, dataToSend).data()), size); return retVal; } @@ -356,12 +356,11 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_hal_SPIJNI_spiSetAutoTransmitData (JNIEnv* env, jclass, jint port, jbyteArray dataToSend, jint zeroSize) { - JByteArrayRef jarr(env, dataToSend); + JSpan jarr(env, dataToSend); int32_t status = 0; - HAL_SetSPIAutoTransmitData( - static_cast(port), - reinterpret_cast(jarr.array().data()), - jarr.array().size(), zeroSize, &status); + HAL_SetSPIAutoTransmitData(static_cast(port), + reinterpret_cast(jarr.data()), + jarr.size(), zeroSize, &status); CheckStatus(env, status); } diff --git a/hal/src/main/native/cpp/jni/SerialPortJNI.cpp b/hal/src/main/native/cpp/jni/SerialPortJNI.cpp index e7f81aa3e0..281e8ef459 100644 --- a/hal/src/main/native/cpp/jni/SerialPortJNI.cpp +++ b/hal/src/main/native/cpp/jni/SerialPortJNI.cpp @@ -261,11 +261,10 @@ Java_edu_wpi_first_hal_SerialPortJNI_serialWrite (JNIEnv* env, jclass, jint handle, jbyteArray dataToSend, jint size) { int32_t status = 0; - jint retVal = - HAL_WriteSerial(static_cast(handle), - reinterpret_cast( - JByteArrayRef(env, dataToSend).array().data()), - size, &status); + jint retVal = HAL_WriteSerial( + static_cast(handle), + reinterpret_cast(JSpan(env, dataToSend).data()), + size, &status); CheckStatus(env, status); return retVal; } diff --git a/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp b/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp index 494b732c02..2cb6c9c762 100644 --- a/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp +++ b/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp @@ -150,7 +150,7 @@ Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueEnumDouble } return HAL_CreateSimValueEnumDouble( device, JStringRef{env, name}.c_str(), direction, len, carr.data(), - JDoubleArrayRef{env, optionValues}.array().data(), initialValue); + JSpan{env, optionValues}.data(), initialValue); } /* diff --git a/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp index 70b31ab2b3..38d9ce7ca4 100644 --- a/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp +++ b/hal/src/main/native/cpp/jni/simulation/AddressableLEDDataJNI.cpp @@ -269,7 +269,7 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_hal_simulation_AddressableLEDDataJNI_setData (JNIEnv* env, jclass, jint index, jbyteArray arr) { - JByteArrayRef jArrRef{env, arr}; + JSpan jArrRef{env, arr}; auto arrRef = jArrRef.array(); HALSIM_SetAddressableLEDData( index, reinterpret_cast(arrRef.data()), diff --git a/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp index c50b8e306e..2aaa7f41a0 100644 --- a/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp +++ b/hal/src/main/native/cpp/jni/simulation/DriverStationDataJNI.cpp @@ -436,7 +436,7 @@ Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setJoystickAxes { HAL_JoystickAxes axes; { - wpi::java::JFloatArrayRef jArrayRef(env, axesArray); + JSpan jArrayRef(env, axesArray); auto arrayRef = jArrayRef.array(); auto arraySize = arrayRef.size(); int maxCount = @@ -461,7 +461,7 @@ Java_edu_wpi_first_hal_simulation_DriverStationDataJNI_setJoystickPOVs { HAL_JoystickPOVs povs; { - wpi::java::JShortArrayRef jArrayRef(env, povsArray); + JSpan jArrayRef(env, povsArray); auto arrayRef = jArrayRef.array(); auto arraySize = arrayRef.size(); int maxCount = diff --git a/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja b/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja index c26adc2cc2..29cf570377 100644 --- a/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja +++ b/ntcore/src/generate/cpp/jni/types_jni.cpp.jinja @@ -77,7 +77,7 @@ void JNI_UnloadTypes(JNIEnv* env) { } // namespace nt static std::vector FromJavaBooleanArray(JNIEnv* env, jbooleanArray jarr) { - CriticalJBooleanArrayRef ref{env, jarr}; + CriticalJSpan ref{env, jarr}; if (!ref) { return {}; } @@ -214,7 +214,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setRaw indexOobEx.Throw(env, "len must be >= 0"); return false; } - CriticalJByteArrayRef cvalue{env, value}; + CriticalJSpan cvalue{env, value}; if (static_cast(start + len) > cvalue.size()) { indexOobEx.Throw(env, "start + len must be smaller than array length"); return false; @@ -243,7 +243,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setRawBuffer indexOobEx.Throw(env, "len must be >= 0"); return false; } - JByteArrayRef cvalue{env, value, start + len}; + JSpan cvalue{env, value, static_cast(start + len)}; if (!cvalue) { illegalArgEx.Throw(env, "value must be a native ByteBuffer"); return false; @@ -310,7 +310,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setDefaultRaw indexOobEx.Throw(env, "len must be >= 0"); return false; } - CriticalJByteArrayRef cvalue{env, defaultValue}; + CriticalJSpan cvalue{env, defaultValue}; if (static_cast(start + len) > cvalue.size()) { indexOobEx.Throw(env, "start + len must be smaller than array length"); return false; @@ -339,7 +339,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setDefaultRawBuffer indexOobEx.Throw(env, "len must be >= 0"); return false; } - JByteArrayRef cvalue{env, defaultValue, start + len}; + JSpan cvalue{env, defaultValue, static_cast(start + len)}; if (!cvalue) { illegalArgEx.Throw(env, "value must be a native ByteBuffer"); return false; diff --git a/ntcore/src/generate/types.json b/ntcore/src/generate/types.json index 55f5741119..3f9f252d54 100644 --- a/ntcore/src/generate/types.json +++ b/ntcore/src/generate/types.json @@ -190,7 +190,7 @@ "jtype": "jbyteArray", "jtypestr": "[B", "JavaObject": true, - "FromJavaBegin": "CriticalJByteArrayRef{env, ", + "FromJavaBegin": "CriticalJSpan{env, ", "FromJavaEnd": "}.uarray()", "ToJavaBegin": "MakeJByteArray(env, ", "ToJavaEnd": ")", @@ -262,7 +262,7 @@ "jtype": "jlongArray", "jtypestr": "[J", "JavaObject": true, - "FromJavaBegin": "CriticalJLongArrayRef{env, ", + "FromJavaBegin": "CriticalJSpan{env, ", "FromJavaEnd": "}", "ToJavaBegin": "MakeJLongArray(env, ", "ToJavaEnd": ")", @@ -298,7 +298,7 @@ "jtype": "jfloatArray", "jtypestr": "[F", "JavaObject": true, - "FromJavaBegin": "CriticalJFloatArrayRef{env, ", + "FromJavaBegin": "CriticalJSpan{env, ", "FromJavaEnd": "}", "ToJavaBegin": "MakeJFloatArray(env, ", "ToJavaEnd": ")", @@ -334,7 +334,7 @@ "jtype": "jdoubleArray", "jtypestr": "[D", "JavaObject": true, - "FromJavaBegin": "CriticalJDoubleArrayRef{env, ", + "FromJavaBegin": "CriticalJSpan{env, ", "FromJavaEnd": "}", "ToJavaBegin": "MakeJDoubleArray(env, ", "ToJavaEnd": ")", diff --git a/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp b/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp index 22e5bbf181..d4679a3c21 100644 --- a/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp +++ b/ntcore/src/main/native/cpp/jni/NetworkTablesJNI.cpp @@ -1262,7 +1262,7 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setServer__I_3Ljava_lang_Strin "serverNames and ports arrays must be the same size"); return; } - jint* portInts = env->GetIntArrayElements(ports, nullptr); + JSpan portInts{env, ports}; if (!portInts) { return; } @@ -1282,7 +1282,6 @@ Java_edu_wpi_first_networktables_NetworkTablesJNI_setServer__I_3Ljava_lang_Strin servers.emplace_back( std::make_pair(std::string_view{names.back()}, portInts[i])); } - env->ReleaseIntArrayElements(ports, portInts, JNI_ABORT); nt::SetServer(inst, servers); } diff --git a/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp b/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp index e1d3af820a..d9a4969628 100644 --- a/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp +++ b/wpimath/src/main/native/cpp/jni/WPIMathJNI.cpp @@ -72,23 +72,23 @@ Java_edu_wpi_first_math_WPIMathJNI_dareDetailABQR (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q, jdoubleArray R, jint states, jint inputs, jdoubleArray S) { - JDoubleArrayRef nativeA{env, A}; - JDoubleArrayRef nativeB{env, B}; - JDoubleArrayRef nativeQ{env, Q}; - JDoubleArrayRef nativeR{env, R}; + JSpan nativeA{env, A}; + JSpan nativeB{env, B}; + JSpan nativeQ{env, Q}; + JSpan nativeR{env, R}; Eigen::Map> - Amat{nativeA.array().data(), states, states}; + Amat{nativeA.data(), states, states}; Eigen::Map> - Bmat{nativeB.array().data(), states, inputs}; + Bmat{nativeB.data(), states, inputs}; Eigen::Map> - Qmat{nativeQ.array().data(), states, states}; + Qmat{nativeQ.data(), states, states}; Eigen::Map> - Rmat{nativeR.array().data(), inputs, inputs}; + Rmat{nativeR.data(), inputs, inputs}; Eigen::MatrixXd RmatCopy{Rmat}; auto R_llt = RmatCopy.llt(); @@ -109,27 +109,27 @@ Java_edu_wpi_first_math_WPIMathJNI_dareDetailABQRN (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q, jdoubleArray R, jdoubleArray N, jint states, jint inputs, jdoubleArray S) { - JDoubleArrayRef nativeA{env, A}; - JDoubleArrayRef nativeB{env, B}; - JDoubleArrayRef nativeQ{env, Q}; - JDoubleArrayRef nativeR{env, R}; - JDoubleArrayRef nativeN{env, N}; + JSpan nativeA{env, A}; + JSpan nativeB{env, B}; + JSpan nativeQ{env, Q}; + JSpan nativeR{env, R}; + JSpan nativeN{env, N}; Eigen::Map> - Amat{nativeA.array().data(), states, states}; + Amat{nativeA.data(), states, states}; Eigen::Map> - Bmat{nativeB.array().data(), states, inputs}; + Bmat{nativeB.data(), states, inputs}; Eigen::Map> - Qmat{nativeQ.array().data(), states, states}; + Qmat{nativeQ.data(), states, states}; Eigen::Map> - Rmat{nativeR.array().data(), inputs, inputs}; + Rmat{nativeR.data(), inputs, inputs}; Eigen::Map> - Nmat{nativeN.array().data(), states, inputs}; + Nmat{nativeN.data(), states, inputs}; Eigen::MatrixXd Rcopy{Rmat}; auto R_llt = Rcopy.llt(); @@ -150,23 +150,23 @@ Java_edu_wpi_first_math_WPIMathJNI_dareABQR (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q, jdoubleArray R, jint states, jint inputs, jdoubleArray S) { - JDoubleArrayRef nativeA{env, A}; - JDoubleArrayRef nativeB{env, B}; - JDoubleArrayRef nativeQ{env, Q}; - JDoubleArrayRef nativeR{env, R}; + JSpan nativeA{env, A}; + JSpan nativeB{env, B}; + JSpan nativeQ{env, Q}; + JSpan nativeR{env, R}; Eigen::Map> - Amat{nativeA.array().data(), states, states}; + Amat{nativeA.data(), states, states}; Eigen::Map> - Bmat{nativeB.array().data(), states, inputs}; + Bmat{nativeB.data(), states, inputs}; Eigen::Map> - Qmat{nativeQ.array().data(), states, states}; + Qmat{nativeQ.data(), states, states}; Eigen::Map> - Rmat{nativeR.array().data(), inputs, inputs}; + Rmat{nativeR.data(), inputs, inputs}; try { Eigen::MatrixXd result = @@ -191,27 +191,27 @@ Java_edu_wpi_first_math_WPIMathJNI_dareABQRN (JNIEnv* env, jclass, jdoubleArray A, jdoubleArray B, jdoubleArray Q, jdoubleArray R, jdoubleArray N, jint states, jint inputs, jdoubleArray S) { - JDoubleArrayRef nativeA{env, A}; - JDoubleArrayRef nativeB{env, B}; - JDoubleArrayRef nativeQ{env, Q}; - JDoubleArrayRef nativeR{env, R}; - JDoubleArrayRef nativeN{env, N}; + JSpan nativeA{env, A}; + JSpan nativeB{env, B}; + JSpan nativeQ{env, Q}; + JSpan nativeR{env, R}; + JSpan nativeN{env, N}; Eigen::Map> - Amat{nativeA.array().data(), states, states}; + Amat{nativeA.data(), states, states}; Eigen::Map> - Bmat{nativeB.array().data(), states, inputs}; + Bmat{nativeB.data(), states, inputs}; Eigen::Map> - Qmat{nativeQ.array().data(), states, states}; + Qmat{nativeQ.data(), states, states}; Eigen::Map> - Rmat{nativeR.array().data(), inputs, inputs}; + Rmat{nativeR.data(), inputs, inputs}; Eigen::Map> - Nmat{nativeN.array().data(), states, inputs}; + Nmat{nativeN.data(), states, inputs}; try { Eigen::MatrixXd result = @@ -235,11 +235,11 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_math_WPIMathJNI_exp (JNIEnv* env, jclass, jdoubleArray src, jint rows, jdoubleArray dst) { - JDoubleArrayRef arrayBody{env, src}; + JSpan arrayBody{env, src}; Eigen::Map> - Amat{arrayBody.array().data(), rows, rows}; + Amat{arrayBody.data(), rows, rows}; Eigen::Matrix Aexp = Amat.exp(); @@ -256,11 +256,11 @@ Java_edu_wpi_first_math_WPIMathJNI_pow (JNIEnv* env, jclass, jdoubleArray src, jint rows, jdouble exponent, jdoubleArray dst) { - JDoubleArrayRef arrayBody{env, src}; + JSpan arrayBody{env, src}; Eigen::Map> - Amat{arrayBody.array().data(), rows, rows}; // NOLINT + Amat{arrayBody.data(), rows, rows}; // NOLINT Eigen::Matrix Apow = Amat.pow(exponent); @@ -331,16 +331,16 @@ Java_edu_wpi_first_math_WPIMathJNI_isStabilizable (JNIEnv* env, jclass, jint states, jint inputs, jdoubleArray aSrc, jdoubleArray bSrc) { - JDoubleArrayRef nativeA{env, aSrc}; - JDoubleArrayRef nativeB{env, bSrc}; + JSpan nativeA{env, aSrc}; + JSpan nativeB{env, bSrc}; Eigen::Map> - A{nativeA.array().data(), states, states}; + A{nativeA.data(), states, states}; Eigen::Map> - B{nativeB.array().data(), states, inputs}; + B{nativeB.data(), states, inputs}; bool isStabilizable = frc::IsStabilizable(A, B); @@ -382,7 +382,7 @@ Java_edu_wpi_first_math_WPIMathJNI_toPathweaverJson { try { auto trajectory = - CreateTrajectoryFromElements(JDoubleArrayRef{env, elements}); + CreateTrajectoryFromElements(JSpan{env, elements}); frc::TrajectoryUtil::ToPathweaverJson(trajectory, JStringRef{env, path}.c_str()); } catch (std::exception& e) { @@ -429,7 +429,7 @@ Java_edu_wpi_first_math_WPIMathJNI_serializeTrajectory { try { auto trajectory = - CreateTrajectoryFromElements(JDoubleArrayRef{env, elements}); + CreateTrajectoryFromElements(JSpan{env, elements}); return MakeJString(env, frc::TrajectoryUtil::SerializeTrajectory(trajectory)); } catch (std::exception& e) { @@ -453,24 +453,20 @@ Java_edu_wpi_first_math_WPIMathJNI_rankUpdate (JNIEnv* env, jclass, jdoubleArray mat, jint rows, jdoubleArray vec, jdouble sigma, jboolean lowerTriangular) { - // TODO: Replace with JSpan matBody{env, mat} when that exists - jdouble* matBody = env->GetDoubleArrayElements(mat, nullptr); - JDoubleArrayRef vecBody{env, vec}; + JSpan matBody{env, mat}; + JSpan vecBody{env, vec}; Eigen::Map< Eigen::Matrix> - L{matBody, rows, rows}; - Eigen::Map> v{ - vecBody.array().data(), rows}; + L{matBody.data(), rows, rows}; + Eigen::Map> v{vecBody.data(), + rows}; if (lowerTriangular == JNI_TRUE) { Eigen::internal::llt_inplace::rankUpdate(L, v, sigma); } else { Eigen::internal::llt_inplace::rankUpdate(L, v, sigma); } - - // TODO: Remove this after JSpan transition - env->ReleaseDoubleArrayElements(mat, matBody, 0); } /* @@ -483,15 +479,15 @@ Java_edu_wpi_first_math_WPIMathJNI_solveFullPivHouseholderQr (JNIEnv* env, jclass, jdoubleArray A, jint Arows, jint Acols, jdoubleArray B, jint Brows, jint Bcols, jdoubleArray dst) { - JDoubleArrayRef nativeA{env, A}; - JDoubleArrayRef nativeB{env, B}; + JSpan nativeA{env, A}; + JSpan nativeB{env, B}; Eigen::Map> - Amat{nativeA.array().data(), Arows, Acols}; + Amat{nativeA.data(), Arows, Acols}; Eigen::Map> - Bmat{nativeB.array().data(), Brows, Bcols}; + Bmat{nativeB.data(), Brows, Bcols}; Eigen::Matrix Xmat = Amat.fullPivHouseholderQr().solve(Bmat); diff --git a/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp b/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp index faf910dc65..a68f08ce99 100644 --- a/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp +++ b/wpiutil/src/main/native/cpp/jni/DataLogJNI.cpp @@ -201,7 +201,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_appendRaw wpi::ThrowIndexOobException(env, "length must be >= 0"); return; } - CriticalJByteArrayRef cvalue{env, value}; + CriticalJSpan cvalue{env, value}; if (static_cast(start + length) > cvalue.size()) { wpi::ThrowIndexOobException( env, "start + len must be smaller than array length"); @@ -237,7 +237,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_appendRawBuffer wpi::ThrowIndexOobException(env, "length must be >= 0"); return; } - JByteArrayRef cvalue{env, value, start + length}; + JSpan cvalue{env, value, static_cast(start + length)}; if (!cvalue) { wpi::ThrowIllegalArgumentException(env, "value must be a native ByteBuffer"); @@ -347,7 +347,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_appendBooleanArray return; } reinterpret_cast(impl)->AppendBooleanArray( - entry, JBooleanArrayRef{env, value}, timestamp); + entry, JSpan{env, value}, timestamp); } /* @@ -368,17 +368,15 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_appendIntegerArray wpi::ThrowNullPointerException(env, "value is null"); return; } - JLongArrayRef jarr{env, value}; + JSpan jarr{env, value}; if constexpr (sizeof(jlong) == sizeof(int64_t)) { reinterpret_cast(impl)->AppendIntegerArray( - entry, - {reinterpret_cast(jarr.array().data()), - jarr.array().size()}, + entry, {reinterpret_cast(jarr.data()), jarr.size()}, timestamp); } else { wpi::SmallVector arr; arr.reserve(jarr.size()); - for (auto v : jarr.array()) { + for (auto v : jarr) { arr.push_back(v); } reinterpret_cast(impl)->AppendIntegerArray(entry, arr, timestamp); @@ -404,7 +402,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_appendFloatArray return; } reinterpret_cast(impl)->AppendFloatArray( - entry, JFloatArrayRef{env, value}, timestamp); + entry, JSpan{env, value}, timestamp); } /* @@ -426,7 +424,7 @@ Java_edu_wpi_first_util_datalog_DataLogJNI_appendDoubleArray return; } reinterpret_cast(impl)->AppendDoubleArray( - entry, JDoubleArrayRef{env, value}, timestamp); + entry, JSpan{env, value}, timestamp); } /* diff --git a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp index b9e98970fc..77f06b5332 100644 --- a/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp +++ b/wpiutil/src/main/native/cpp/jni/WPIUtilJNI.cpp @@ -275,11 +275,11 @@ JNIEXPORT jintArray JNICALL Java_edu_wpi_first_util_WPIUtilJNI_waitForObjects (JNIEnv* env, jclass, jintArray handles) { - JIntArrayRef handlesArr{env, handles}; + JSpan handlesArr{env, handles}; wpi::SmallVector signaledBuf; signaledBuf.resize(handlesArr.size()); std::span handlesArr2{ - reinterpret_cast(handlesArr.array().data()), + reinterpret_cast(handlesArr.data()), handlesArr.size()}; auto signaled = wpi::WaitForObjects(handlesArr2, signaledBuf); @@ -299,11 +299,11 @@ JNIEXPORT jintArray JNICALL Java_edu_wpi_first_util_WPIUtilJNI_waitForObjectsTimeout (JNIEnv* env, jclass, jintArray handles, jdouble timeout) { - JIntArrayRef handlesArr{env, handles}; + JSpan handlesArr{env, handles}; wpi::SmallVector signaledBuf; signaledBuf.resize(handlesArr.size()); std::span handlesArr2{ - reinterpret_cast(handlesArr.array().data()), + reinterpret_cast(handlesArr.data()), handlesArr.size()}; bool timedOut; diff --git a/wpiutil/src/main/native/include/wpi/jni_util.h b/wpiutil/src/main/native/include/wpi/jni_util.h index d34faccf64..bc4488a795 100644 --- a/wpiutil/src/main/native/include/wpi/jni_util.h +++ b/wpiutil/src/main/native/include/wpi/jni_util.h @@ -175,207 +175,242 @@ class JStringRef { SmallString<128> m_str; }; -// Details for J*ArrayRef and CriticalJ*ArrayRef namespace detail { -template -class JArrayRefInner { +template +struct ArrayHelper {}; + +#define WPI_JNI_ARRAYHELPER(T, F) \ + template <> \ + struct ArrayHelper { \ + using jarray_type = T##Array; \ + static T* GetArrayElements(JNIEnv* env, jarray_type jarr) { \ + return env->Get##F##ArrayElements(jarr, nullptr); \ + } \ + static void ReleaseArrayElements(JNIEnv* env, jarray_type jarr, T* elems, \ + jint mode) { \ + env->Release##F##ArrayElements(jarr, elems, mode); \ + } \ + }; + +WPI_JNI_ARRAYHELPER(jboolean, Boolean) +WPI_JNI_ARRAYHELPER(jbyte, Byte) +WPI_JNI_ARRAYHELPER(jshort, Short) +WPI_JNI_ARRAYHELPER(jint, Int) +WPI_JNI_ARRAYHELPER(jlong, Long) +WPI_JNI_ARRAYHELPER(jfloat, Float) +WPI_JNI_ARRAYHELPER(jdouble, Double) + +#undef WPI_JNI_ARRAYHELPER + +template +concept JArrayType = + requires { typename ArrayHelper>::jarray_type; }; + +template +struct copy_cv { + private: + using U0 = std::remove_cv_t; + using U1 = std::conditional_t, const U0, U0>; + using U2 = std::conditional_t, volatile U1, U1>; + public: - operator std::span() const { // NOLINT - return static_cast(this)->array(); - } + using type = U2; }; -/** - * Specialization of JArrayRefBase to provide std::string_view conversion - * and span conversion. - */ -template -class JArrayRefInner { - public: - operator std::string_view() const { return str(); } +template +using copy_cv_t = typename copy_cv::type; - std::string_view str() const { - auto arr = static_cast(this)->array(); +template +constexpr bool is_qualification_convertible_v = + !(std::is_const_v && !std::is_const_v)&&!( + std::is_volatile_v && !std::is_volatile_v); + +/** + * Helper class for working with JNI arrays. + * + * This class exposes an is_valid() member and an explicit conversion to bool + * which indicate if the span is valid. Operations on invalid spans are + * undefined. + * + * Note that SetArrayRegion may be faster for pure writes since + * it avoids copying the elements from Java to C++. + * + * @tparam T The element type of the array (e.g., jdouble). + * @tparam IsCritical If true, Get/ReleasePrimitiveArrayCritical will be used + * instead of Get/Release\ArrayElements. + * @tparam Size The number of elements in the span. + */ +template +class JSpanBase { + using ArrHelper = ArrayHelper>; + using jarray_type = typename ArrHelper::jarray_type; + + public: + JSpanBase(const JSpanBase&) = delete; + JSpanBase& operator=(const JSpanBase&) = delete; + + JSpanBase(JSpanBase&& other) + : m_valid{other.m_valid}, + m_env{other.m_env}, + m_jarr{other.m_jarr}, + m_size{other.m_size}, + m_elements{other.m_elements} { + other.m_jarr = nullptr; + other.m_elements = nullptr; + } + + JSpanBase& operator=(JSpanBase&& other) { + m_valid = other.m_valid; + m_env = other.m_env; + m_jarr = other.m_jarr; + m_size = other.m_size; + m_elements = other.m_elements; + other.m_valid = false; + other.m_jarr = nullptr; + other.m_elements = nullptr; + return *this; + } + + JSpanBase(JNIEnv* env, jobject bb, size_t size) + requires(!IsCritical) + : m_valid{Size == std::dynamic_extent || size == Size}, + m_env{env}, + m_jarr{nullptr}, + m_size{size}, + m_elements{static_cast*>( + bb ? env->GetDirectBufferAddress(bb) : nullptr)} { + if (!bb) { + errs() << "JSpan was passed a null pointer at \n" + << GetJavaStackTrace(env); + } + } + + JSpanBase(JNIEnv* env, jarray_type jarr, size_t size) + : m_valid{Size == std::dynamic_extent || size == Size}, + m_env{env}, + m_jarr{jarr}, + m_size{size}, + m_elements{nullptr} { + if (jarr) { + if constexpr (IsCritical) { + m_elements = static_cast*>( + env->GetPrimitiveArrayCritical(jarr, nullptr)); + } else { + m_elements = ArrHelper::GetArrayElements(env, jarr); + } + } else { + errs() << "JSpan was passed a null pointer at \n" + << GetJavaStackTrace(env); + } + } + + JSpanBase(JNIEnv* env, jarray_type jarr) + : JSpanBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {} + + ~JSpanBase() { + if (m_jarr && m_elements) { + constexpr jint mode = std::is_const_v ? JNI_ABORT : 0; + if constexpr (IsCritical) { + m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, mode); + } else { + ArrHelper::ReleaseArrayElements(m_env, m_jarr, m_elements, mode); + } + } + } + + operator std::span() const { return array(); } + + std::span array() const { + // If Size is dynamic_extent, can return empty span + // Unfortunately, sized spans will return a span over nullptr if m_elements + // is nullptr + if constexpr (Size == std::dynamic_extent) { + if (!m_elements) { + return {}; + } + } + return std::span{m_elements, m_size}; + } + + T* begin() const { return m_elements; } + + T* end() const { return m_elements + m_size; } + + bool is_valid() const { return m_valid && m_elements != nullptr; } + + explicit operator bool() const { return is_valid(); } + + T* data() const { return m_elements; } + + size_t size() const { return m_size; } + + const T& operator[](size_t i) const { return m_elements[i]; } + + T& operator[](size_t i) + requires(!std::is_const_v) + { + return m_elements[i]; + } + + // Provide std::string_view and span conversions for jbyte + + operator std::string_view() const + requires std::is_same_v, jbyte> + { + return str(); + } + + std::string_view str() const + requires std::is_same_v, jbyte> + { + auto arr = array(); if (arr.empty()) { return {}; } return {reinterpret_cast(arr.data()), arr.size()}; } - std::span uarray() const { - auto arr = static_cast(this)->array(); + std::span, Size> uarray() const + requires std::is_same_v, jbyte> + { + auto arr = array(); if (arr.empty()) { return {}; } return {reinterpret_cast(arr.data()), arr.size()}; } -}; -/** - * Specialization of JArrayRefBase to handle both "long long" and "long" on - * 64-bit systems. - */ -template -class JArrayRefInner { - public: + // Support both "long long" and "long" on 64-bit systems + template - requires(sizeof(U) == sizeof(jlong) && std::integral) - operator std::span() const { // NOLINT - auto arr = static_cast(this)->array(); + requires(sizeof(U) == sizeof(jlong) && std::integral && + is_qualification_convertible_v) + operator std::span() const + requires std::is_same_v, jlong> + { + auto arr = array(); if (arr.empty()) { return {}; } - return {reinterpret_cast(arr.data()), arr.size()}; - } -}; - -/** - * Base class for J*ArrayRef and CriticalJ*ArrayRef - */ -template -class JArrayRefBase : public JArrayRefInner, T> { - public: - explicit operator bool() const { return this->m_elements != nullptr; } - - std::span array() const { - if (!this->m_elements) { - return {}; - } - return {this->m_elements, this->m_size}; + return {reinterpret_cast(arr.data()), arr.size()}; } - size_t size() const { return this->m_size; } - T& operator[](size_t i) { return this->m_elements[i]; } - const T& operator[](size_t i) const { return this->m_elements[i]; } - - JArrayRefBase(const JArrayRefBase&) = delete; - JArrayRefBase& operator=(const JArrayRefBase&) = delete; - - JArrayRefBase(JArrayRefBase&& oth) - : m_env(oth.m_env), - m_jarr(oth.m_jarr), - m_size(oth.m_size), - m_elements(oth.m_elements) { - oth.m_jarr = nullptr; - oth.m_elements = nullptr; - } - - JArrayRefBase& operator=(JArrayRefBase&& oth) { - this->m_env = oth.m_env; - this->m_jarr = oth.m_jarr; - this->m_size = oth.m_size; - this->m_elements = oth.m_elements; - oth.m_jarr = nullptr; - oth.m_elements = nullptr; - return *this; - } - - protected: - JArrayRefBase(JNIEnv* env, T* elements, size_t size) { - this->m_env = env; - this->m_jarr = nullptr; - this->m_size = size; - this->m_elements = elements; - } - - JArrayRefBase(JNIEnv* env, jarray jarr, size_t size) { - this->m_env = env; - this->m_jarr = jarr; - this->m_size = size; - this->m_elements = nullptr; - } - - JArrayRefBase(JNIEnv* env, jarray jarr) - : JArrayRefBase(env, jarr, jarr ? env->GetArrayLength(jarr) : 0) {} - + private: + bool m_valid; JNIEnv* m_env; - jarray m_jarr = nullptr; + jarray_type m_jarr = nullptr; size_t m_size; - T* m_elements; + std::remove_cv_t* m_elements; }; } // namespace detail -// Java array / DirectBuffer reference. +template +using JSpan = detail::JSpanBase; -#define WPI_JNI_JARRAYREF(T, F) \ - class J##F##ArrayRef : public detail::JArrayRefBase { \ - public: \ - J##F##ArrayRef(JNIEnv* env, jobject bb, int len) \ - : detail::JArrayRefBase( \ - env, \ - static_cast(bb ? env->GetDirectBufferAddress(bb) : nullptr), \ - len) { \ - if (!bb) { \ - errs() << "JArrayRef was passed a null pointer at \n" \ - << GetJavaStackTrace(env); \ - } \ - } \ - J##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \ - : detail::JArrayRefBase(env, jarr, len) { \ - if (jarr) { \ - m_elements = env->Get##F##ArrayElements(jarr, nullptr); \ - } else { \ - errs() << "JArrayRef was passed a null pointer at \n" \ - << GetJavaStackTrace(env); \ - } \ - } \ - J##F##ArrayRef(JNIEnv* env, T##Array jarr) \ - : detail::JArrayRefBase(env, jarr) { \ - if (jarr) { \ - m_elements = env->Get##F##ArrayElements(jarr, nullptr); \ - } else { \ - errs() << "JArrayRef was passed a null pointer at \n" \ - << GetJavaStackTrace(env); \ - } \ - } \ - ~J##F##ArrayRef() { \ - if (m_jarr && m_elements) { \ - m_env->Release##F##ArrayElements(static_cast(m_jarr), \ - m_elements, JNI_ABORT); \ - } \ - } \ - }; \ - \ - class CriticalJ##F##ArrayRef : public detail::JArrayRefBase { \ - public: \ - CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr, int len) \ - : detail::JArrayRefBase(env, jarr, len) { \ - if (jarr) { \ - m_elements = \ - static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); \ - } else { \ - errs() << "JArrayRef was passed a null pointer at \n" \ - << GetJavaStackTrace(env); \ - } \ - } \ - CriticalJ##F##ArrayRef(JNIEnv* env, T##Array jarr) \ - : detail::JArrayRefBase(env, jarr) { \ - if (jarr) { \ - m_elements = \ - static_cast(env->GetPrimitiveArrayCritical(jarr, nullptr)); \ - } else { \ - errs() << "JArrayRef was passed a null pointer at \n" \ - << GetJavaStackTrace(env); \ - } \ - } \ - ~CriticalJ##F##ArrayRef() { \ - if (m_jarr && m_elements) { \ - m_env->ReleasePrimitiveArrayCritical(m_jarr, m_elements, JNI_ABORT); \ - } \ - } \ - }; - -WPI_JNI_JARRAYREF(jboolean, Boolean) -WPI_JNI_JARRAYREF(jbyte, Byte) -WPI_JNI_JARRAYREF(jshort, Short) -WPI_JNI_JARRAYREF(jint, Int) -WPI_JNI_JARRAYREF(jlong, Long) -WPI_JNI_JARRAYREF(jfloat, Float) -WPI_JNI_JARRAYREF(jdouble, Double) - -#undef WPI_JNI_JARRAYREF +template +using CriticalJSpan = detail::JSpanBase; // // Conversions from C++ to Java objects