From baa0fbf127d19ae106bb998149a261ffa4fd52e8 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Sun, 22 Feb 2026 21:38:15 -0800 Subject: [PATCH 1/6] Fix check-after-use bug in JNI scalar type lookup scalar_type_to_java_dtype.at(scalarType) throws std::out_of_range for unknown types before the .count() guard on the next line can run, making the error handling dead code. Move the .count() check before .at() so unsupported scalar types throw a proper Java exception instead of crashing. --- extension/android/jni/jni_layer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extension/android/jni/jni_layer.cpp b/extension/android/jni/jni_layer.cpp index 7c5c64e8cf0..113d0e2c0c6 100644 --- a/extension/android/jni/jni_layer.cpp +++ b/extension/android/jni/jni_layer.cpp @@ -58,14 +58,15 @@ class TensorHybrid : public facebook::jni::HybridClass { // Java wrapper currently only supports contiguous tensors. const auto scalarType = tensor.scalar_type(); - int jdtype = scalar_type_to_java_dtype.at(scalarType); if (scalar_type_to_java_dtype.count(scalarType) == 0) { std::stringstream ss; - ss << "executorch::aten::Tensor scalar [java] type: " << jdtype + ss << "executorch::aten::Tensor scalar type " + << static_cast(scalarType) << " is not supported on java side"; jni_helper::throwExecutorchException( static_cast(Error::InvalidArgument), ss.str().c_str()); } + int jdtype = scalar_type_to_java_dtype.at(scalarType); const auto& tensor_shape = tensor.sizes(); std::vector tensor_shape_vec; From 6afc31542b14b5f86579b7bd8c348826e1eaacc2 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Feb 2026 13:24:36 -0800 Subject: [PATCH 2/6] Linter --- extension/android/jni/jni_layer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extension/android/jni/jni_layer.cpp b/extension/android/jni/jni_layer.cpp index 113d0e2c0c6..812689ebb3d 100644 --- a/extension/android/jni/jni_layer.cpp +++ b/extension/android/jni/jni_layer.cpp @@ -61,8 +61,7 @@ class TensorHybrid : public facebook::jni::HybridClass { if (scalar_type_to_java_dtype.count(scalarType) == 0) { std::stringstream ss; ss << "executorch::aten::Tensor scalar type " - << static_cast(scalarType) - << " is not supported on java side"; + << static_cast(scalarType) << " is not supported on java side"; jni_helper::throwExecutorchException( static_cast(Error::InvalidArgument), ss.str().c_str()); } From 143a9be8f3fbb69c42efd44429678b3292615858 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Feb 2026 13:37:44 -0800 Subject: [PATCH 3/6] Fix typos in Module.java: initHybrid parameter name and missing space in error message --- .../src/main/java/org/pytorch/executorch/Module.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java index 6da76bf4b74..20e7685d5b3 100644 --- a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java +++ b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/Module.java @@ -54,7 +54,7 @@ public class Module { @DoNotStrip private static native HybridData initHybrid( - String moduleAbsolutePath, int loadMode, int initHybrid); + String moduleAbsolutePath, int loadMode, int numThreads); private Module(String moduleAbsolutePath, int loadMode, int numThreads) { ExecuTorchRuntime runtime = ExecuTorchRuntime.getRuntime(); @@ -201,7 +201,7 @@ public int loadMethod(String methodName) { */ public MethodMetadata getMethodMetadata(String name) { if (!mMethodMetadata.containsKey(name)) { - throw new RuntimeException("method " + name + "does not exist for this module"); + throw new RuntimeException("method " + name + " does not exist for this module"); } MethodMetadata methodMetadata = mMethodMetadata.get(name); From 6c312e55c872844c80bfb24db8ef6e5a69b59c59 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Feb 2026 13:41:27 -0800 Subject: [PATCH 4/6] Modernize Android build config: Java 11, compileSdk 35, update test deps Remove dead root ext properties (minSdkVersion, targetSdkVersion, compileSdkVersion, buildToolsVersion) that were unused by the module. --- extension/android/build.gradle | 5 ----- .../android/executorch_android/build.gradle | 16 ++++++++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/extension/android/build.gradle b/extension/android/build.gradle index 86e53d5873f..59d524500fd 100644 --- a/extension/android/build.gradle +++ b/extension/android/build.gradle @@ -1,11 +1,6 @@ allprojects { buildscript { ext { - minSdkVersion = 21 - targetSdkVersion = 34 - compileSdkVersion = 34 - buildToolsVersion = '33.0.1' - fbjniJavaOnlyVersion = "0.7.0" soLoaderNativeLoaderVersion = "0.10.5" } diff --git a/extension/android/executorch_android/build.gradle b/extension/android/executorch_android/build.gradle index 536c80d40bc..384c737aab9 100644 --- a/extension/android/executorch_android/build.gradle +++ b/extension/android/executorch_android/build.gradle @@ -27,7 +27,7 @@ def flavor = System.properties['flavor'] android { namespace = "org.pytorch.executorch" - compileSdk = 34 + compileSdk = 35 defaultConfig { minSdk = 23 @@ -36,8 +36,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } sourceSets { @@ -49,7 +49,7 @@ android { } } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "11" } } @@ -61,12 +61,12 @@ dependencies { implementation 'com.facebook.fbjni:fbjni:0.7.0' implementation 'com.facebook.soloader:nativeloader:0.10.5' implementation libs.core.ktx - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.assertj:assertj-core:3.27.2' testImplementation 'org.jetbrains.kotlin:kotlin-test:1.9.23' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'commons-io:commons-io:2.4' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test:rules:1.6.1' + androidTestImplementation 'commons-io:commons-io:2.18.0' androidTestImplementation 'org.json:json:20250107' androidTestImplementation 'org.jetbrains.kotlin:kotlin-test:1.9.23' if (qnnVersion) { From 0e17fc1e7393b09b4adcd389a9cb67e5621cd347 Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Feb 2026 13:49:49 -0800 Subject: [PATCH 5/6] Return immediately after throwExecutorchException in JNI tensor helpers throwExecutorchException only sets a pending Java exception and does not abort C++ execution. Without an early return, subsequent code (e.g. map::at()) can throw std::out_of_range before the JVM sees the pending exception. --- extension/android/jni/jni_layer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extension/android/jni/jni_layer.cpp b/extension/android/jni/jni_layer.cpp index 812689ebb3d..96bf820c459 100644 --- a/extension/android/jni/jni_layer.cpp +++ b/extension/android/jni/jni_layer.cpp @@ -64,6 +64,7 @@ class TensorHybrid : public facebook::jni::HybridClass { << static_cast(scalarType) << " is not supported on java side"; jni_helper::throwExecutorchException( static_cast(Error::InvalidArgument), ss.str().c_str()); + return nullptr; } int jdtype = scalar_type_to_java_dtype.at(scalarType); @@ -131,6 +132,7 @@ class TensorHybrid : public facebook::jni::HybridClass { ss << "Unknown Tensor jdtype: [" << jdtype << "]"; jni_helper::throwExecutorchException( static_cast(Error::InvalidArgument), ss.str().c_str()); + return nullptr; } ScalarType scalar_type = java_dtype_to_scalar_type.at(jdtype); const jlong dataCapacity = jni->GetDirectBufferCapacity(jbuffer.get()); @@ -139,6 +141,7 @@ class TensorHybrid : public facebook::jni::HybridClass { ss << "Tensor buffer is not direct or has invalid capacity"; jni_helper::throwExecutorchException( static_cast(Error::InvalidArgument), ss.str().c_str()); + return nullptr; } const size_t elementSize = executorch::runtime::elementSize(scalar_type); const jlong expectedElements = static_cast(numel); @@ -153,6 +156,7 @@ class TensorHybrid : public facebook::jni::HybridClass { << " (element size bytes: " << elementSize << ")"; jni_helper::throwExecutorchException( static_cast(Error::InvalidArgument), ss.str().c_str()); + return nullptr; } return from_blob( jni->GetDirectBufferAddress(jbuffer.get()), shape_vec, scalar_type); From 0521e2a86059cb78df787550c909a8de61e53a7d Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Mon, 23 Feb 2026 14:20:37 -0800 Subject: [PATCH 6/6] Revert compileSdk 35 back to 34 to fix CI --- extension/android/executorch_android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/android/executorch_android/build.gradle b/extension/android/executorch_android/build.gradle index 384c737aab9..3733c8691be 100644 --- a/extension/android/executorch_android/build.gradle +++ b/extension/android/executorch_android/build.gradle @@ -27,7 +27,7 @@ def flavor = System.properties['flavor'] android { namespace = "org.pytorch.executorch" - compileSdk = 35 + compileSdk = 34 defaultConfig { minSdk = 23