From 5a1ded07a5d3308267c8d7612876fd336221b18b Mon Sep 17 00:00:00 2001 From: Jamieson Pryor Date: Thu, 2 Jan 2025 13:55:56 -0800 Subject: [PATCH] Update some metafunctions for increased portability. As the matrix of supported compilers an increasing number of compiler inconsistencies are unfortunately showing, both across clang and gcc, but also across platforms (mac and Linux) and versions (C++17 and C++20). gcc does not seem to permit template specialisations within class definitions so inner helper classes have been moved out of classes (despite the current form being cleaner). `InvocableMap`s have been updated not to have derived non-type template parameters, so they're now just passed as explicit types. This is objectively absurd, as what I wrote is valid C+++ and gcc should know better, but it just straight up segfaults so better luck next time gcc. Also removed `conjunction` and `lambda_compatible` which depended on the full form. Since they are not used I will just remove them. PiperOrigin-RevId: 711515926 --- .github/workflows/ci.yml | 4 +- .gitignore | 1 + BUILD | 1 + implementation/BUILD | 9 +- implementation/array_test.cc | 3 - implementation/array_type_conversion_test.cc | 4 +- implementation/array_view_test.cc | 48 +- implementation/class.h | 13 +- .../class_loader_ref_second_test.cc | 2 +- implementation/class_loader_ref_test.cc | 17 +- implementation/class_loader_test.cc | 2 - implementation/class_test.cc | 1 - implementation/constructor_test.cc | 1 + implementation/field_ref_test.cc | 49 +- implementation/global_object_test.cc | 26 +- implementation/id_test.cc | 2 - implementation/jni_type_test.cc | 2 - implementation/jvm_test.cc | 6 - implementation/legacy/BUILD | 245 +++++++++ implementation/legacy/README.md | 3 + implementation/legacy/array_view_test.cc | 443 ++++++++++++++++ .../legacy/class_loader_ref_second_test.cc | 99 ++++ .../legacy/class_loader_ref_test.cc | 317 ++++++++++++ implementation/legacy/field_ref_test.cc | 170 +++++++ implementation/legacy/global_object_test.cc | 285 +++++++++++ ...local_array_field_multidimensional_test.cc | 152 ++++++ .../legacy/local_array_field_test.cc | 480 ++++++++++++++++++ .../legacy/local_array_iteration_test.cc | 262 ++++++++++ ...ocal_array_method_multidimensional_test.cc | 119 +++++ .../legacy/local_array_method_test.cc | 228 +++++++++ .../local_array_multidimensional_test.cc | 112 ++++ .../legacy/local_array_string_test.cc | 204 ++++++++ implementation/legacy/local_object_test.cc | 327 ++++++++++++ implementation/legacy/method_ref_test.cc | 301 +++++++++++ implementation/legacy/multi_type_test.cc | 114 +++++ implementation/legacy/overload_ref_test.cc | 126 +++++ implementation/legacy/self_test.cc | 68 +++ implementation/legacy/static_ref_test.cc | 373 ++++++++++++++ implementation/legacy/string_ref_test.cc | 238 +++++++++ implementation/local_array_field_test.cc | 173 ++++--- ...ocal_array_method_multidimensional_test.cc | 15 +- implementation/local_array_method_test.cc | 53 +- implementation/local_array_string_test.cc | 12 +- implementation/local_object_test.cc | 35 +- implementation/method_ref_test.cc | 37 +- implementation/multi_type_test.cc | 36 +- implementation/object_ref.h | 50 +- implementation/overload_ref_test.cc | 34 +- implementation/proxy_test.cc | 17 +- implementation/return.h | 4 +- implementation/self_test.cc | 6 +- implementation/static_ref.h | 73 ++- implementation/static_ref_test.cc | 68 +-- implementation/string_ref_test.cc | 15 +- java/com/google/main.cc | 2 +- .../com/jnibind/android/array_test_jni.cc | 87 ++-- .../jnibind/android/class_loader_test_jni.cc | 10 +- .../com/jnibind/android/context_test_jni.cc | 26 +- .../com/jnibind/android/field_test_jni.cc | 12 +- .../jnibind/android/global_object_test_jni.cc | 17 +- .../jnibind/android/local_object_test_jni.cc | 17 +- .../com/jnibind/android/method_test_jni.cc | 109 ++-- .../com/jnibind/android/string_test_jni.cc | 63 +-- .../com/jnibind/android/thread_test_jni.cc | 2 +- javatests/com/jnibind/test/BUILD | 1 + .../test/array_test_field_rank_1_jni.cc | 104 ++-- .../test/array_test_field_rank_2_jni.cc | 98 ++-- .../test/array_test_method_rank_1_jni.cc | 93 ++-- .../test/array_test_method_rank_2_jni.cc | 61 +-- javatests/com/jnibind/test/builder_jni.cc | 7 +- .../com/jnibind/test/context_test_jni.cc | 2 +- javatests/com/jnibind/test/field_test_jni.cc | 23 +- .../jnibind/test/global_object_test_jni.cc | 14 +- .../com/jnibind/test/local_object_test_jni.cc | 14 +- javatests/com/jnibind/test/method_test_jni.cc | 58 +-- javatests/com/jnibind/test/static_test_jni.cc | 56 +- javatests/com/jnibind/test/thread_test_jni.cc | 2 +- jni_bind.h | 1 + metaprogramming/BUILD | 115 ++--- metaprogramming/conjunction.h | 44 -- metaprogramming/conjunction_test.cc | 80 --- metaprogramming/even_odd.h | 56 +- metaprogramming/invocable_map.h | 19 +- metaprogramming/invocable_map_20.h | 98 ++++ metaprogramming/invocable_map_20_test.cc | 82 +++ metaprogramming/lambda_compatible.h | 75 --- metaprogramming/lambda_compatible_test.cc | 73 --- metaprogramming/n_bit_sequence.h | 57 ++- metaprogramming/next.h | 2 +- metaprogramming/next_test.cc | 13 +- metaprogramming/pack_discriminator.h | 25 +- metaprogramming/pack_discriminator_test.cc | 20 - metaprogramming/queryable_map_20.h | 90 ++++ metaprogramming/queryable_map_20_test.cc | 81 +++ metaprogramming/string_literal.h | 4 +- 95 files changed, 6072 insertions(+), 1226 deletions(-) create mode 100644 implementation/legacy/BUILD create mode 100644 implementation/legacy/README.md create mode 100644 implementation/legacy/array_view_test.cc create mode 100644 implementation/legacy/class_loader_ref_second_test.cc create mode 100644 implementation/legacy/class_loader_ref_test.cc create mode 100644 implementation/legacy/field_ref_test.cc create mode 100644 implementation/legacy/global_object_test.cc create mode 100644 implementation/legacy/local_array_field_multidimensional_test.cc create mode 100644 implementation/legacy/local_array_field_test.cc create mode 100644 implementation/legacy/local_array_iteration_test.cc create mode 100644 implementation/legacy/local_array_method_multidimensional_test.cc create mode 100644 implementation/legacy/local_array_method_test.cc create mode 100644 implementation/legacy/local_array_multidimensional_test.cc create mode 100644 implementation/legacy/local_array_string_test.cc create mode 100644 implementation/legacy/local_object_test.cc create mode 100644 implementation/legacy/method_ref_test.cc create mode 100644 implementation/legacy/multi_type_test.cc create mode 100644 implementation/legacy/overload_ref_test.cc create mode 100644 implementation/legacy/self_test.cc create mode 100644 implementation/legacy/static_ref_test.cc create mode 100644 implementation/legacy/string_ref_test.cc delete mode 100644 metaprogramming/conjunction.h delete mode 100644 metaprogramming/conjunction_test.cc create mode 100644 metaprogramming/invocable_map_20.h create mode 100644 metaprogramming/invocable_map_20_test.cc delete mode 100644 metaprogramming/lambda_compatible.h delete mode 100644 metaprogramming/lambda_compatible_test.cc create mode 100644 metaprogramming/queryable_map_20.h create mode 100644 metaprogramming/queryable_map_20_test.cc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2c0b759..f1664dff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: test - run: bazel query --output=label 'kind("...", //...) except attr("tags", "cpp20", "//...")' | xargs bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors + run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors //implementation/legacy/... ubuntu-latest-cpp20: runs-on: ubuntu-latest @@ -29,7 +29,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: test - run: bazel query --output=label 'kind("...", //...) except attr(tags, "cpp20", //...)' | xargs bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors + run: bazel test --cxxopt='-Werror' --cxxopt='-std=c++17' --repo_env=CC=clang --test_output=errors //implementation/legacy/... macos-latest-cpp20: runs-on: macos-latest diff --git a/.gitignore b/.gitignore index bd12961c..80835edd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bazel* +.vscode diff --git a/BUILD b/BUILD index 7949e520..f3d79daf 100644 --- a/BUILD +++ b/BUILD @@ -82,6 +82,7 @@ cc_library( "//implementation/jni_helper:static_field_value", "//metaprogramming:corpus", "//metaprogramming:corpus_tag", + "//metaprogramming:string_literal", ], ) diff --git a/implementation/BUILD b/implementation/BUILD index 5044af27..32f82380 100644 --- a/implementation/BUILD +++ b/implementation/BUILD @@ -923,8 +923,11 @@ cc_library( "//:jni_dep", "//implementation/jni_helper:lifecycle", "//metaprogramming:invocable_map", + "//metaprogramming:invocable_map_20", "//metaprogramming:queryable_map", + "//metaprogramming:queryable_map_20", "//metaprogramming:string_contains", + "//metaprogramming:string_literal", ], ) @@ -1071,7 +1074,7 @@ cc_test( deps = [ "//:jni_bind", "//:jni_test", - "//metaprogramming:concatenate", + "//metaprogramming:type_to_type_map", "@googletest//:gtest_main", ], ) @@ -1237,9 +1240,11 @@ cc_library( ":method_selection", ":no_idx", "//:jni_dep", - "//implementation/jni_helper:invoke_static", "//metaprogramming:invocable_map", + "//metaprogramming:invocable_map_20", "//metaprogramming:queryable_map", + "//metaprogramming:queryable_map_20", + "//metaprogramming:string_literal", ], ) diff --git a/implementation/array_test.cc b/implementation/array_test.cc index cc9858a7..025a3dd2 100644 --- a/implementation/array_test.cc +++ b/implementation/array_test.cc @@ -16,10 +16,7 @@ #include -#include -#include #include "jni_bind.h" -#include "jni_test.h" namespace { diff --git a/implementation/array_type_conversion_test.cc b/implementation/array_type_conversion_test.cc index eb1084cd..731ff168 100644 --- a/implementation/array_type_conversion_test.cc +++ b/implementation/array_type_conversion_test.cc @@ -14,8 +14,8 @@ * limitations under the License. */ -#include -#include +#include + #include "jni_bind.h" namespace { diff --git a/implementation/array_view_test.cc b/implementation/array_view_test.cc index 8f1ff7ab..2403b8fc 100644 --- a/implementation/array_view_test.cc +++ b/implementation/array_view_test.cc @@ -14,6 +14,7 @@ * limitations under the License. */ #include +#include #include #include @@ -50,51 +51,43 @@ TEST_F(JniTest, ArrayView_CallsLengthProperly) { TEST_F(JniTest, ArrayView_GetsAndReleaseArrayBuffer) { EXPECT_CALL(*env_, GetBooleanArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, ReleaseBooleanArrayElements( - Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseBooleanArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetByteArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, - ReleaseByteArrayElements(Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseByteArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetCharArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, - ReleaseCharArrayElements(Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseCharArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetShortArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL( - *env_, ReleaseShortArrayElements(Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseShortArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetIntArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, - ReleaseIntArrayElements(Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseIntArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetLongArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, - ReleaseLongArrayElements(Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseLongArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetFloatArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL( - *env_, ReleaseFloatArrayElements(Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseFloatArrayElements(Eq(Fake()), + Eq(Fake()), 0)); EXPECT_CALL(*env_, GetDoubleArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, ReleaseDoubleArrayElements( - Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseDoubleArrayElements(Eq(Fake()), + Eq(Fake()), 0)); LocalArray boolean_array{AdoptLocal{}, Fake()}; LocalArray byte_array{AdoptLocal{}, Fake()}; @@ -118,9 +111,8 @@ TEST_F(JniTest, ArrayView_GetsAndReleaseArrayBuffer) { TEST_F(JniTest, LocalArrayView_AllowsCTAD) { EXPECT_CALL(*env_, GetBooleanArrayElements(Eq(Fake()), _)) .WillOnce(::testing::Return(Fake())); - EXPECT_CALL(*env_, ReleaseBooleanArrayElements( - Eq(Fake()), - Eq(Fake()), 0)); + EXPECT_CALL(*env_, ReleaseBooleanArrayElements(Eq(Fake()), + Eq(Fake()), 0)); LocalArray boolean_array{AdoptLocal{}, Fake()}; ArrayView ctad_array_view{boolean_array.Pin()}; @@ -326,7 +318,7 @@ TEST_F(JniTest, ArrayView_RichObjectsAreIterable) { int fake_result = 123; for (LocalObject obj : obj_view) { EXPECT_CALL(*env_, CallIntMethodV).WillOnce(::testing::Return(fake_result)); - EXPECT_EQ(obj("Foo"), fake_result); + EXPECT_EQ(obj.Call<"Foo">(), fake_result); fake_result++; } } diff --git a/implementation/class.h b/implementation/class.h index 81dee205..18a52196 100644 --- a/implementation/class.h +++ b/implementation/class.h @@ -36,7 +36,10 @@ namespace jni { template -struct Class {}; +struct Class { + constexpr Class() = default; + constexpr Class(const char* name) {} +}; template , // provided where they are and aren't present. //////////////////////////////////////////////////////////////////////////////// + // To stifle a test failure. + constexpr Class() + : Object("__JNI_BIND__NO_CLASS__"), + constructors_(Constructor<>{}), + static_(), + methods_(), + fields_() {} + // Methods + Fields. explicit constexpr Class(const char* class_name, Methods_... methods, Fields_... fields) diff --git a/implementation/class_loader_ref_second_test.cc b/implementation/class_loader_ref_second_test.cc index 50424733..de27b6dc 100644 --- a/implementation/class_loader_ref_second_test.cc +++ b/implementation/class_loader_ref_second_test.cc @@ -91,7 +91,7 @@ TEST_F(JniTestWithNoDefaultJvmRef, auto second_custom_loader_object = class_loader.BuildLocalObject(jint{2}); - EXPECT_EQ(custom_loader_object("methodNoCrossTalk", jint{2}), 123); + EXPECT_EQ(custom_loader_object.Call<"methodNoCrossTalk">(jint{2}), 123); TearDown(); } diff --git a/implementation/class_loader_ref_test.cc b/implementation/class_loader_ref_test.cc index 3868736e..c743b2fb 100644 --- a/implementation/class_loader_ref_test.cc +++ b/implementation/class_loader_ref_test.cc @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include #include #include @@ -119,7 +118,7 @@ TEST_F(JniTest, LocalObject_SupportsPassingAnObjectWithAClassLoader) { // LocalObject a{}; // doesn't compile (good). LocalObject a{Fake()}; LocalObject b{}; - b("Foo", a); + b.Call<"Foo">(a); default_globals_made_that_should_be_released_.clear(); } @@ -131,7 +130,7 @@ TEST_F(JniTestForClassLoaders, LocalClassLoader local_class_loader{Fake()}; auto a = local_class_loader.BuildLocalObject(); LocalObject b{a}; - b("Foo", a); + b.Call<"Foo">(a); default_globals_made_that_should_be_released_.clear(); TearDown(); @@ -144,7 +143,7 @@ TEST_F(JniTestForClassLoaders, LocalClassLoader local_class_loader{Fake()}; auto a = local_class_loader.BuildGlobalObject(); LocalObject b{a}; - b("Foo", a); + b.Call<"Foo">(a); default_globals_made_that_should_be_released_.clear(); TearDown(); @@ -161,7 +160,7 @@ TEST_F(JniTestForClassLoaders, LocalObject a = local_class_loader.BuildLocalObject(); LocalObject b{a}; - b("Foo", a); + b.Call<"Foo">(a); TearDown(); } @@ -173,7 +172,7 @@ TEST_F(JniTestForClassLoaders, ClassLoaderRefTest_ConstructsFromRValue) { LocalObject b{ local_class_loader.BuildLocalObject()}; - LocalObject c{b("Foo")}; + LocalObject c{b.Call<"Foo">()}; TearDown(); } @@ -187,7 +186,7 @@ TEST_F(JniTestForClassLoaders, LocalObject a{}; LocalObject b = local_class_loader.BuildLocalObject(a); - b("Foo", a); + b.Call<"Foo">(a); TearDown(); } @@ -236,7 +235,7 @@ TEST_F(JniTestWithNoDefaultJvmRef, LocalObject a = local_class_loader.BuildLocalObject(); LocalObject b{}; - b("Foo", a); + b.Call<"Foo">(a); TearDown(); } @@ -310,7 +309,7 @@ TEST_F(JniTestWithNoDefaultJvmRef, kDefaultConfiguration}; jni::LocalObject obj1{AdoptLocal{}, Fake(1)}; - obj1("Foo"); + obj1.Call<"Foo">(); this->TearDown(); } diff --git a/implementation/class_loader_test.cc b/implementation/class_loader_test.cc index 8ca0d6fe..29b3962c 100644 --- a/implementation/class_loader_test.cc +++ b/implementation/class_loader_test.cc @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include #include "jni_bind.h" -#include "jni_test.h" namespace { diff --git a/implementation/class_test.cc b/implementation/class_test.cc index ec490a2f..44bb0fbb 100644 --- a/implementation/class_test.cc +++ b/implementation/class_test.cc @@ -14,7 +14,6 @@ * limitations under the License. */ -#include #include #include "jni_bind.h" #include "jni_test.h" diff --git a/implementation/constructor_test.cc b/implementation/constructor_test.cc index 4e52042d..c51bb097 100644 --- a/implementation/constructor_test.cc +++ b/implementation/constructor_test.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include "jni_bind.h" #include "jni_test.h" diff --git a/implementation/field_ref_test.cc b/implementation/field_ref_test.cc index a6bf521a..2ae6be6f 100644 --- a/implementation/field_ref_test.cc +++ b/implementation/field_ref_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #include #include @@ -59,8 +59,8 @@ TEST_F(JniTest, Field_SimpleGetAndSet) { Field("SomeField", jint{})}; GlobalObject obj{}; - EXPECT_EQ(999, obj["SomeField"].Get()); - obj["SomeField"].Set(123); + EXPECT_EQ(999, obj.Access<"SomeField">().Get()); + obj.Access<"SomeField">().Set(123); } TEST_F(JniTest, Field_BooleanField) { @@ -68,8 +68,8 @@ TEST_F(JniTest, Field_BooleanField) { EXPECT_CALL(*env_, SetBooleanField); LocalObject obj{}; - obj["booleanField"].Get(); - obj["booleanField"].Set(true); + obj.Access<"booleanField">().Get(); + obj.Access<"booleanField">().Set(true); } TEST_F(JniTest, Field_ByteField) { @@ -77,8 +77,8 @@ TEST_F(JniTest, Field_ByteField) { EXPECT_CALL(*env_, SetByteField); LocalObject obj{}; - obj["byteField"].Get(); - obj["byteField"].Set(jbyte{123}); + obj.Access<"byteField">().Get(); + obj.Access<"byteField">().Set(jbyte{123}); } TEST_F(JniTest, Field_CharField) { @@ -86,8 +86,8 @@ TEST_F(JniTest, Field_CharField) { EXPECT_CALL(*env_, SetCharField); LocalObject obj{}; - obj["charField"].Get(); - obj["charField"].Set(jchar{'a'}); + obj.Access<"charField">().Get(); + obj.Access<"charField">().Set(jchar{'a'}); } TEST_F(JniTest, Field_ShortField) { @@ -95,8 +95,8 @@ TEST_F(JniTest, Field_ShortField) { EXPECT_CALL(*env_, SetShortField); LocalObject obj{}; - obj["shortField"].Get(); - obj["shortField"].Set(jshort{123}); + obj.Access<"shortField">().Get(); + obj.Access<"shortField">().Set(jshort{123}); } TEST_F(JniTest, Field_intField) { @@ -104,8 +104,8 @@ TEST_F(JniTest, Field_intField) { EXPECT_CALL(*env_, SetIntField); LocalObject obj{}; - obj["intField"].Get(); - obj["intField"].Set(123); + obj.Access<"intField">().Get(); + obj.Access<"intField">().Set(123); } TEST_F(JniTest, Field_longField) { @@ -113,24 +113,24 @@ TEST_F(JniTest, Field_longField) { EXPECT_CALL(*env_, SetLongField); LocalObject obj{}; - obj["longField"].Get(); - obj["longField"].Set(123); + obj.Access<"longField">().Get(); + obj.Access<"longField">().Set(123); } TEST_F(JniTest, Field_floatField) { LocalObject obj{}; EXPECT_CALL(*env_, GetFloatField); EXPECT_CALL(*env_, SetFloatField); - obj["floatField"].Get(); - obj["floatField"].Set(123.f); + obj.Access<"floatField">().Get(); + obj.Access<"floatField">().Set(123.f); } TEST_F(JniTest, Field_doubleField) { LocalObject obj{}; EXPECT_CALL(*env_, GetDoubleField); EXPECT_CALL(*env_, SetDoubleField); - obj["doubleField"].Get(); - obj["doubleField"].Set(123.); + obj.Access<"doubleField">().Get(); + obj.Access<"doubleField">().Set(123.); } TEST_F(JniTest, Field_ObjectGet) { @@ -144,7 +144,7 @@ TEST_F(JniTest, Field_ObjectGet) { static constexpr Class kClass2{"kClass2"}; LocalObject obj{}; - LocalObject obj2 = obj["classField"].Get(); + LocalObject obj2 = obj.Access<"classField">().Get(); } TEST_F(JniTest, Field_ObjectSet) { @@ -161,10 +161,11 @@ TEST_F(JniTest, Field_ObjectSet) { LocalObject obj{}; LocalObject some_obj{AdoptLocal{}, Fake()}; - obj["classField"].Set(Fake()); - obj["classField"].Set(LocalObject{AdoptLocal{}, Fake()}); - obj["classField"].Set(some_obj); - obj["classField"].Set(std::move(some_obj)); + obj.Access<"classField">().Set(Fake()); + obj.Access<"classField">().Set( + LocalObject{AdoptLocal{}, Fake()}); + obj.Access<"classField">().Set(some_obj); + obj.Access<"classField">().Set(std::move(some_obj)); } } // namespace diff --git a/implementation/global_object_test.cc b/implementation/global_object_test.cc index 7e19f79d..9669307b 100644 --- a/implementation/global_object_test.cc +++ b/implementation/global_object_test.cc @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include +#include #include #include @@ -200,15 +200,15 @@ TEST_F(JniTest, GlobalObject_ValuesWorkAfterMoveConstructor) { Method{"Foo", jni::Return{}, Params{}}, Field{"BarField", jint{}}}; GlobalObject global_object_1{AdoptGlobal{}, Fake(1)}; - global_object_1("Foo", 1); - global_object_1("Foo", 2); - global_object_1["BarField"].Set(1); + global_object_1.Call<"Foo">(1); + global_object_1.Call<"Foo">(2); + global_object_1.Access<"BarField">().Set(1); GlobalObject global_object_2{std::move(global_object_1)}; - global_object_2("Foo", 3); - global_object_2["BarField"].Set(2); - global_object_2["BarField"].Set(3); - global_object_2["BarField"].Set(4); + global_object_2.Call<"Foo">(3); + global_object_2.Access<"BarField">().Set(2); + global_object_2.Access<"BarField">().Set(3); + global_object_2.Access<"BarField">().Set(4); GlobalObject global_object_3{AdoptGlobal{}, Fake(1)}; } @@ -245,8 +245,8 @@ TEST_F(JniTest, GlobalObject_ObjectReturnsInstanceMethods) { Params{}}}; GlobalObject global_object{}; - global_object("Foo", 1); - global_object("Baz", 2.f); + global_object.Call<"Foo">(1); + global_object.Call<"Baz">(2.f); global_object( "AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplatesToHandle", int{}, float{}, int{}, float{}, double{}); @@ -268,7 +268,7 @@ TEST_F(JniTest, GlobalObject_SupportsPassingAPrvalue) { GlobalObject a{}; GlobalObject b{}; - b("Foo", std::move(a)); + b.Call<"Foo">(std::move(a)); } TEST_F(JniTest, GlobalObjects_PromoteRValuesFromEmittedLValues) { @@ -277,9 +277,9 @@ TEST_F(JniTest, GlobalObjects_PromoteRValuesFromEmittedLValues) { "TestClass2", Method{"Foo", jni::Return{kClass1}, jni::Params{}}}; LocalObject b{}; - GlobalObject a{b("Foo")}; + GlobalObject a{b.Call<"Foo">()}; - a = b("Foo"); + a = b.Call<"Foo">(); } } // namespace diff --git a/implementation/id_test.cc b/implementation/id_test.cc index b5826a7f..979fb1bf 100644 --- a/implementation/id_test.cc +++ b/implementation/id_test.cc @@ -16,8 +16,6 @@ #include -#include -#include #include "jni_bind.h" namespace { diff --git a/implementation/jni_type_test.cc b/implementation/jni_type_test.cc index df2d2d7f..8c1b55b4 100644 --- a/implementation/jni_type_test.cc +++ b/implementation/jni_type_test.cc @@ -15,8 +15,6 @@ */ #include -#include -#include #include "jni_bind.h" namespace { diff --git a/implementation/jvm_test.cc b/implementation/jvm_test.cc index 0cfed6fe..66008e7e 100644 --- a/implementation/jvm_test.cc +++ b/implementation/jvm_test.cc @@ -14,14 +14,8 @@ * limitations under the License. */ -#include - -#include #include -#include "implementation/jni_helper/jni_env.h" -#include "implementation/jni_helper/jni_helper.h" #include "jni_bind.h" -#include "jni_test.h" namespace { diff --git a/implementation/legacy/BUILD b/implementation/legacy/BUILD new file mode 100644 index 00000000..76591293 --- /dev/null +++ b/implementation/legacy/BUILD @@ -0,0 +1,245 @@ +package( + licenses = ["notice"], +) + +################################################################################ +# Array. +################################################################################ +cc_test( + name = "array_view_test", + srcs = ["array_view_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# ClassLoader. +################################################################################ +cc_test( + name = "class_loader_ref_test", + srcs = ["class_loader_ref_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation:configuration", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "class_loader_ref_second_test", + srcs = ["class_loader_ref_second_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# Field. +################################################################################ +cc_test( + name = "field_ref_test", + srcs = ["field_ref_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# GlobalObject. +################################################################################ +cc_test( + name = "global_object_test", + srcs = ["global_object_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# LocalArray. +################################################################################ +cc_test( + name = "local_array_field_multidimensional_test", + srcs = ["local_array_field_multidimensional_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "local_array_field_test", + srcs = ["local_array_field_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "local_array_iteration_test", + srcs = ["local_array_iteration_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "local_array_multidimensional_test", + srcs = ["local_array_multidimensional_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "local_array_method_test", + srcs = ["local_array_method_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "local_array_method_multidimensional_test", + srcs = ["local_array_method_multidimensional_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "local_array_string_test", + srcs = ["local_array_string_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# LocalObject. +################################################################################ +cc_test( + name = "local_object_test", + srcs = ["local_object_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# Method. +################################################################################ +cc_test( + name = "method_ref_test", + srcs = ["method_ref_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# Multi type test. +################################################################################ +cc_test( + name = "multi_type_test", + srcs = ["multi_type_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# Object. +################################################################################ +cc_test( + name = "overload_ref_test", + srcs = ["overload_ref_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# Static. +################################################################################ +cc_test( + name = "static_ref_test", + srcs = ["static_ref_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "string_ref_test", + srcs = ["string_ref_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "//implementation/jni_helper:fake_test_constants", + "@googletest//:gtest_main", + ], +) + +################################################################################ +# Self. +################################################################################ +cc_test( + name = "self_test", + srcs = ["self_test.cc"], + deps = [ + "//:jni_bind", + "//:jni_test", + "@googletest//:gtest_main", + ], +) diff --git a/implementation/legacy/README.md b/implementation/legacy/README.md new file mode 100644 index 00000000..1677833d --- /dev/null +++ b/implementation/legacy/README.md @@ -0,0 +1,3 @@ +This directory contains a copy of the tests from implementation but using the legacy `obj.Foo("methodName", args...);` syntax. + +These tests are effectively pure duplication and may be removed at some future point. They are here to maintain coverage as JNI Bind migrates to a C++20 friendly syntax. diff --git a/implementation/legacy/array_view_test.cc b/implementation/legacy/array_view_test.cc new file mode 100644 index 00000000..502f0bb8 --- /dev/null +++ b/implementation/legacy/array_view_test.cc @@ -0,0 +1,443 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::ArrayView; +using ::jni::CDecl_t; +using ::jni::Class; +using ::jni::Fake; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::Return; +using ::jni::test::AsNewLocalReference; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Eq; + +//////////////////////////////////////////////////////////////////////////////// +// Pin Tests. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ArrayView_CallsLengthProperly) { + EXPECT_CALL(*env_, GetArrayLength).WillOnce(::testing::Return(3)); + + LocalArray local_int_array{5}; + EXPECT_EQ(local_int_array.Length(), 3); +} + +TEST_F(JniTest, ArrayView_GetsAndReleaseArrayBuffer) { + EXPECT_CALL(*env_, GetBooleanArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseBooleanArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetByteArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseByteArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetCharArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseCharArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetShortArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseShortArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetIntArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseIntArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetLongArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseLongArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetFloatArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseFloatArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + EXPECT_CALL(*env_, GetDoubleArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseDoubleArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + LocalArray boolean_array{AdoptLocal{}, Fake()}; + LocalArray byte_array{AdoptLocal{}, Fake()}; + LocalArray char_array{AdoptLocal{}, Fake()}; + LocalArray short_array{AdoptLocal{}, Fake()}; + LocalArray int_array{AdoptLocal{}, Fake()}; + LocalArray long_array{AdoptLocal{}, Fake()}; + LocalArray float_array{AdoptLocal{}, Fake()}; + LocalArray double_array{AdoptLocal{}, Fake()}; + + ArrayView boolean_array_pin = {boolean_array.Pin()}; + ArrayView byte_array_pin = {byte_array.Pin()}; + ArrayView int_array_pin = {int_array.Pin()}; + ArrayView char_array_pin = {char_array.Pin()}; + ArrayView short_array_pin = {short_array.Pin()}; + ArrayView long_array_pin = {long_array.Pin()}; + ArrayView float_array_pin = {float_array.Pin()}; + ArrayView double_array_pin = {double_array.Pin()}; +} + +TEST_F(JniTest, LocalArrayView_AllowsCTAD) { + EXPECT_CALL(*env_, GetBooleanArrayElements(Eq(Fake()), _)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, ReleaseBooleanArrayElements(Eq(Fake()), + Eq(Fake()), 0)); + + LocalArray boolean_array{AdoptLocal{}, Fake()}; + ArrayView ctad_array_view{boolean_array.Pin()}; + + // Despite supporting construction from xvalue, move ctor is deleted (good). + // ArrayView ctad_array_view_2 {std::move(ctad_array_view)}; +} + +TEST_F(JniTest, ArrayView_ConstructsFromAnObject) { + static constexpr Class kClass{"kClass"}; + LocalArray local_obj_array{1, LocalObject{}}; +} + +TEST_F(JniTest, ArrayView_ConstructsFromAnObjectRValueWithCTAD) { + static constexpr Class kClass{"kClass"}; + LocalArray local_obj_array{1, LocalObject{}}; +} + +TEST_F(JniTest, ArrayView_GetsAnObject) { + static constexpr Class kClass{"kClass"}; + + EXPECT_CALL(*env_, GetObjectArrayElement(_, _)); + LocalArray local_obj_array{1, LocalObject{}}; + local_obj_array.Get(0); +} + +TEST_F(JniTest, ArrayView_GetsAnObjectWithCTAD) { + static constexpr Class kClass{"kClass"}; + + EXPECT_CALL(*env_, GetObjectArrayElement(_, _)); + LocalArray local_obj_array{1, LocalObject{}}; + local_obj_array.Get(0); +} + +//////////////////////////////////////////////////////////////////////////////// +// Iteration Tests: Primitives. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ArrayView_BooleanIsIterable) { + std::array fake_vals{jboolean{true}, jboolean{false}, jboolean{true}}; + EXPECT_CALL(*env_, NewBooleanArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetBooleanArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_ByteIsIterable) { + std::array fake_vals{jbyte{true}, jbyte{false}, jbyte{true}}; + EXPECT_CALL(*env_, NewByteArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(testing::Return(3)); + EXPECT_CALL(*env_, GetByteArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_CharIsIterable) { + std::array fake_vals{jchar{true}, jchar{false}, jchar{true}}; + EXPECT_CALL(*env_, NewCharArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetCharArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_ShortIsIterable) { + std::array fake_vals{jshort{true}, jshort{false}, jshort{true}}; + EXPECT_CALL(*env_, NewShortArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetShortArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_IntIsIterable) { + std::array fake_vals{jint{1}, jint{2}, jint{3}}; + + EXPECT_CALL(*env_, NewIntArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetIntArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray int_arr{3}; + ArrayView int_view = int_arr.Pin(); + + EXPECT_TRUE(std::equal(int_view.begin(), int_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_LongIsIterable) { + std::array fake_vals{jlong{true}, jlong{false}, jlong{true}}; + EXPECT_CALL(*env_, NewLongArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetLongArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_FloatIsIterable) { + std::array fake_vals{jfloat{true}, jfloat{false}, jfloat{true}}; + + EXPECT_CALL(*env_, NewFloatArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetFloatArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_DoubleIsIterable) { + std::array fake_vals{jdouble{true}, jdouble{false}, jdouble{true}}; + EXPECT_CALL(*env_, NewDoubleArray(3)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetDoubleArrayElements) + .WillOnce(::testing::Return(fake_vals.data())); + + LocalArray bool_arr{3}; + ArrayView bool_view = bool_arr.Pin(); + + EXPECT_TRUE(std::equal(bool_view.begin(), bool_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +//////////////////////////////////////////////////////////////////////////////// +// Iteration Tests: Objects. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ArrayView_ShallowObjectsAreIterable) { + std::array fake_vals{Fake(1), Fake(2), Fake(3)}; + + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(1))) + .WillOnce(::testing::Return(Fake(2))) + .WillOnce(::testing::Return(Fake(3))); + + LocalArray obj_arr{AdoptLocal{}, Fake()}; + ArrayView obj_view = obj_arr.Pin(); + + EXPECT_TRUE(std::equal(obj_view.begin(), obj_view.end(), fake_vals.begin(), + fake_vals.end())); +} + +TEST_F(JniTest, ArrayView_RichObjectsAreIterable) { + static constexpr Class kClass{"kClass", Method{"Foo", Return{}}}; + + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(1))) + .WillOnce(::testing::Return(Fake(2))) + .WillOnce(::testing::Return(Fake(3))); + + LocalArray obj_arr{AdoptLocal{}, Fake()}; + auto obj_view = obj_arr.Pin(); + + // Note: GlobalObject will fail to compile here. This is good, the user + // should be forced to explicitly promote the local. + int fake_result = 123; + for (LocalObject obj : obj_view) { + EXPECT_CALL(*env_, CallIntMethodV).WillOnce(::testing::Return(fake_result)); + EXPECT_EQ(obj("Foo"), fake_result); + fake_result++; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Iteration Tests: Rank 2 Iterations. +// +// Note: Writing through every type would be tedious, however, if these tests +// could be generalised across the universe of types it would be better. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ArrayView_Rank2IntArraysAreIterable) { + std::array fake_vals{Fake(1), Fake(2), + Fake(3)}; + + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 0)) + .WillOnce(::testing::Return(Fake(1))); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 1)) + .WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 2)) + .WillOnce(::testing::Return(Fake(3))); + + LocalArray int_arr_rank_2{AdoptLocal{}, Fake()}; + ArrayView int_rank2_view = int_arr_rank_2.Pin(); + + EXPECT_TRUE(std::equal(int_rank2_view.begin(), int_rank2_view.end(), + fake_vals.begin(), fake_vals.end())); + + /* + // Also viable to write this: + // for (LocalArray jint_array : int_rank2_view) { } + */ +} + +TEST_F(JniTest, ArrayView_Rank2ObjectkArraysAreIterable) { + std::array fake_vals{Fake(1), Fake(2), + Fake(3)}; + + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 0)) + .WillOnce(::testing::Return(Fake(1))); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 1)) + .WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 2)) + .WillOnce(::testing::Return(Fake(3))); + + LocalArray int_arr_rank_2{AdoptLocal{}, Fake()}; + ArrayView int_rank2_view = int_arr_rank_2.Pin(); + + EXPECT_TRUE(std::equal(int_rank2_view.begin(), int_rank2_view.end(), + fake_vals.begin(), fake_vals.end())); + + /* + // Also viable to write this: + // for (LocalArray jint_array : int_rank2_view) { } + */ +} + +//////////////////////////////////////////////////////////////////////////////// +// Iteration Tests: Rank 3 Iterations. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ArrayView_Rank3IntArraysAreIterable) { + std::array fake_vals{Fake(), Fake(), + Fake()}; + + EXPECT_CALL(*env_, GetArrayLength(Fake())) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 0)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 1)) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 2)) + .WillOnce(::testing::Return(Fake())); + + LocalArray int_arr_rank_3{AdoptLocal{}, Fake()}; + ArrayView int_rank_3_view = int_arr_rank_3.Pin(); + + EXPECT_TRUE(std::equal(int_rank_3_view.begin(), int_rank_3_view.end(), + fake_vals.begin(), fake_vals.end())); + + // Also viable to write this: + // for (LocalArray jint_array : int_rank_3_view) { } +} + +TEST_F(JniTest, ArrayView_Rank3ObjectkArraysAreIterable) { + std::array fake_vals{Fake(1), Fake(2), + Fake(3)}; + + EXPECT_CALL(*env_, GetArrayLength(Fake(0))) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 0)) + .WillOnce(::testing::Return(Fake(1))); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 1)) + .WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, GetObjectArrayElement( + AsNewLocalReference(Fake()), 2)) + .WillOnce(::testing::Return(Fake(3))); + + LocalArray object_arr_rank_3{AdoptLocal{}, Fake(0)}; + ArrayView object_rank_3_view = object_arr_rank_3.Pin(); + + EXPECT_TRUE(std::equal(object_rank_3_view.begin(), object_rank_3_view.end(), + fake_vals.begin(), fake_vals.end())); + + // Also viable to write this: + // for (LocalArray jobject_array : object_rank_3_view) { } +} + +} // namespace diff --git a/implementation/legacy/class_loader_ref_second_test.cc b/implementation/legacy/class_loader_ref_second_test.cc new file mode 100644 index 00000000..50424733 --- /dev/null +++ b/implementation/legacy/class_loader_ref_second_test.cc @@ -0,0 +1,99 @@ +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::Class; +using ::jni::ClassLoader; +using ::jni::Constructor; +using ::jni::Fake; +using ::jni::Jvm; +using ::jni::JvmRef; +using ::jni::kNullClassLoader; +using ::jni::LocalClassLoader; +using ::jni::Method; +using ::jni::Params; +using ::jni::Return; +using ::jni::SupportedClassSet; +using ::jni::test::AsGlobal; +using ::jni::test::JniTestWithNoDefaultJvmRef; +using ::testing::_; +using ::testing::InSequence; +using ::testing::StrEq; + +// This test is isolated to correctly observe querying to Class + ClassLoader +// class definitions. Ids are now cached statically against their name, so this +// results in crosstalk. +TEST_F(JniTestWithNoDefaultJvmRef, + ClassLoaderRefTest_ClassLoadersDoNotConflict) { + static constexpr Class kClass{ + "com/google/kClass", Constructor{}, + Method{"methodNoCrossTalk", Return{}, Params{}}}; + static constexpr ClassLoader kClassLoader{kNullClassLoader, + SupportedClassSet{kClass}}; + + // We will use this ClassLoader instead of the default loader to load + // Class. + static constexpr Jvm kClassLoaderJvm{kClassLoader}; + + InSequence sequence; + + EXPECT_CALL(*env_, FindClass(StrEq("java/lang/ClassLoader"))) + .WillOnce(testing::Return(Fake(1))); + + EXPECT_CALL(*env_, NewGlobalRef(Fake(1))) + .WillOnce(testing::Return(AsGlobal(Fake(1)))); + + EXPECT_CALL(*env_, + GetMethodID(AsGlobal(Fake(1)), StrEq("loadClass"), + StrEq("(Ljava/lang/String;)Ljava/lang/Class;"))) + .WillOnce(testing::Return(Fake(1))); + + EXPECT_CALL(*env_, NewStringUTF(_)) + .WillOnce(testing::Return(Fake())); + + // We should only try to load the class once even if we create multiple + // instances. + EXPECT_CALL(*env_, CallObjectMethodV(Fake(1), Fake(1), _)) + .WillOnce(testing::Return(Fake(2))); + + EXPECT_CALL(*env_, NewGlobalRef(Fake(2))) + .WillOnce(testing::Return(AsGlobal(Fake(2)))); + EXPECT_CALL(*env_, GetMethodID(AsGlobal(Fake(2)), StrEq(""), + StrEq("(I)V"))) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, + NewObjectV(AsGlobal(Fake(2)), Fake(2), _)) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, + NewObjectV(AsGlobal(Fake(2)), Fake(2), _)) + .WillOnce(testing::Return(Fake(3))); + + EXPECT_CALL(*env_, + GetMethodID(AsGlobal(Fake(2)), StrEq("methodNoCrossTalk"), + + StrEq("(I)I"))) + .WillOnce(testing::Return(Fake(3))); + EXPECT_CALL(*env_, CallIntMethodV(Fake(2), Fake(3), _)) + .WillOnce(testing::Return(123)); + + // Code under test. + JvmRef jvm_ref{jvm_.get()}; + LocalClassLoader class_loader{ + AdoptLocal{}, Fake(1)}; + + auto custom_loader_object = class_loader.BuildLocalObject(jint{1}); + + auto second_custom_loader_object = + class_loader.BuildLocalObject(jint{2}); + + EXPECT_EQ(custom_loader_object("methodNoCrossTalk", jint{2}), 123); + + TearDown(); +} + +} // namespace diff --git a/implementation/legacy/class_loader_ref_test.cc b/implementation/legacy/class_loader_ref_test.cc new file mode 100644 index 00000000..9414665d --- /dev/null +++ b/implementation/legacy/class_loader_ref_test.cc @@ -0,0 +1,317 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptGlobal; +using ::jni::AdoptLocal; +using ::jni::Class; +using ::jni::ClassLoader; +using ::jni::Constructor; +using ::jni::Fake; +using ::jni::GlobalClassLoader; +using ::jni::Jvm; +using ::jni::JvmRef; +using ::jni::kDefaultJvm; +using ::jni::kNullClassLoader; +using ::jni::LocalClassLoader; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::Params; +using ::jni::PromoteToGlobal; +using ::jni::Return; +using ::jni::SupportedClassSet; +using ::jni::test::AsGlobal; +using ::jni::test::JniTest; +using ::jni::test::JniTestWithNoDefaultJvmRef; +using ::jni::test::kDefaultConfiguration; +using ::testing::_; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::StrEq; + +static constexpr Class kClass1{"Class1", Constructor{}, Constructor{}, + Method{"Foo", Return{Class{"Class2"}}}}; + +static constexpr Class kClass2{ + "Class2", Constructor{}, Constructor{kClass1}, + Method{"Foo", jni::Return{}, jni::Params{kClass1}}}; + +static constexpr Class kClass3{"Class3"}; +static constexpr Class kClass4{"Class4"}; + +static constexpr ClassLoader kClassLoader{kNullClassLoader, + SupportedClassSet{kClass1, kClass2}}; + +static constexpr Jvm kJvm{kClassLoader}; + +// Helper class for classloader tests that gives the standard default class +// object when using CallObjectMethodV. This is because objects built from +// class loaders are built by calling "loadClass" on the respective classloader +// instance. JniTest is strict about the number of DeleteGlobalRef calls, +// so this satisfies that requirement. +// +// Note, when using this, you must call |TearDown| to pre-empt and class +// teardown prior to expectations being set. +class JniTestForClassLoaders : public JniTestWithNoDefaultJvmRef { + void SetUp() override { + JniTestWithNoDefaultJvmRef::SetUp(); + + ON_CALL(*env_, CallObjectMethodV) + .WillByDefault(::testing::Return(Fake())); + } +}; + +TEST_F(JniTest, LocalsAreMoveable) { + LocalClassLoader obj_1{Fake()}; + LocalClassLoader obj_2{std::move(obj_1)}; +} + +TEST_F(JniTest, GlobalClassLoadersSupportAdoptionMechanics) { + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + GlobalClassLoader obj_1{AdoptGlobal{}, Fake()}; +} + +TEST_F(JniTest, GlobalClassLoadersSupportPromoteMechanics) { + EXPECT_CALL(*env_, DeleteLocalRef).Times(1); + GlobalClassLoader obj_1{PromoteToGlobal{}, + Fake()}; +} + +TEST_F(JniTest, GlobalsAreMoveable) { + GlobalClassLoader obj_1{AdoptGlobal{}, Fake()}; + GlobalClassLoader obj_2{std::move(obj_1)}; +} + +//////////////////////////////////////////////////////////////////////////////// +// Default JVM, non-default classloader (No ID teardown on JVM destruction). +// +// Because these tests use |jni::kDefaultJvm|, the global refs bound to class +// and method IDs won't be freed (they are static across the process). As a +// hack, skip testing for them by calling clearing expected globals. +// +// An alternate (more effective) emulation would be to have these tests run +// as independent processes which would reflect the static nature of the memory. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, LocalObject_SupportsPassingAnObjectWithAClassLoader) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + // LocalObject a{}; // doesn't compile (good). + LocalObject a{Fake()}; + LocalObject b{}; + b("Foo", a); + + default_globals_made_that_should_be_released_.clear(); +} + +TEST_F(JniTestForClassLoaders, + ClassLoaderRefTest_ConstructorsAcceptClassLoadedObjects) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + auto a = local_class_loader.BuildLocalObject(); + LocalObject b{a}; + b("Foo", a); + + default_globals_made_that_should_be_released_.clear(); + TearDown(); +} + +TEST_F(JniTestForClassLoaders, + lassLoaderRefTest_ConstructorsAcceptGlobalClassLoadedObjects) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + auto a = local_class_loader.BuildGlobalObject(); + LocalObject b{a}; + b("Foo", a); + + default_globals_made_that_should_be_released_.clear(); + TearDown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Non standard JVM, non-default classloader (ID teardown on JVM destruction). +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTestForClassLoaders, + ClassLoaderRefTest_DefaultLoadedObjectBuildsWithClassLoadedObject) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + LocalClassLoader local_class_loader{AdoptLocal{}, + Fake()}; + LocalObject a = + local_class_loader.BuildLocalObject(); + LocalObject b{a}; + b("Foo", a); + + TearDown(); +} + +TEST_F(JniTestForClassLoaders, ClassLoaderRefTest_ConstructsFromRValue) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + LocalObject b{ + local_class_loader.BuildLocalObject()}; + + LocalObject c{b("Foo")}; + + TearDown(); +} + +TEST_F(JniTestForClassLoaders, + ClassLoaderRefTest_ClassLoadedObjectBuildsWithDefaultLoadedObject) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + + LocalObject a{}; + LocalObject b = + local_class_loader.BuildLocalObject(a); + b("Foo", a); + + TearDown(); +} + +TEST_F( + JniTestForClassLoaders, + ClassLoaderRefTest_LocalClassLoaderWithSingleClassAndConstructorCompiles) { + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + auto a = local_class_loader.BuildLocalObject(12345); + // local_class_loader.BuildLocalObject(); doesn't compile (good) + + TearDown(); +} + +TEST_F(JniTestWithNoDefaultJvmRef, + ClassLoaderRefTest_LocalClassLoaderWithMultipleClassesCompiles) { + ON_CALL(*env_, CallObjectMethodV) + .WillByDefault(::testing::Return(Fake())); + + static constexpr ClassLoader kClassLoader{ + kNullClassLoader, SupportedClassSet{kClass1, kClass2, kClass3, kClass4}}; + static constexpr Jvm kJvm{kClassLoader}; + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + + LocalObject a{local_class_loader.BuildLocalObject()}; + auto b = local_class_loader.BuildLocalObject(); + auto c = local_class_loader.BuildLocalObject(); + auto d = local_class_loader.BuildLocalObject(); + + TearDown(); +} + +TEST_F(JniTestWithNoDefaultJvmRef, + DefaultLoadedObjectAcceptsClassLoadedObject) { + ON_CALL(*env_, CallObjectMethodV(testing::_, testing::_, testing::_)) + .WillByDefault(::testing::Return(Fake())); + + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + + LocalObject a = + local_class_loader.BuildLocalObject(); + LocalObject b{}; + b("Foo", a); + + TearDown(); +} + +TEST_F(JniTestWithNoDefaultJvmRef, + ClassLoaderRefTest_DefaultLoadedClassCompiles) { + ON_CALL(*env_, CallObjectMethodV(testing::_, testing::_, testing::_)) + .WillByDefault(::testing::Return(Fake())); + JvmRef jvm_ref{jvm_.get(), kDefaultConfiguration}; + + LocalClassLoader local_class_loader{Fake()}; + + LocalObject a{local_class_loader.BuildLocalObject()}; + TearDown(); +} + +TEST_F(JniTestWithNoDefaultJvmRef, + ClassLoaderRefTest_ClassesOfDifferentClassLoadersAreUnique) { + static constexpr Class class_under_test{ + "com/google/ARCore", + Method{"Foo", jni::Return{}}, + }; + static constexpr ClassLoader class_loader{ + kNullClassLoader, SupportedClassSet{class_under_test}}; + + static constexpr jni::Jvm atypical_jvm_definition{class_loader}; + + InSequence seq; + + // The java/lang/Class and java/lang/ClassLoader will always be from the + // default loader, and they only need to be cached once. + EXPECT_CALL(*env_, FindClass(StrEq("java/lang/Class"))) + .WillOnce(testing::Return(Fake(2))); + + EXPECT_CALL(*env_, FindClass(StrEq("java/lang/ClassLoader"))) + .WillOnce(testing::Return(Fake(3))); + + EXPECT_CALL(*env_, GetObjectClass(Fake(1))) + .WillOnce(testing::Return(Fake(1))); + + EXPECT_CALL(*env_, + GetMethodID(AsGlobal(Fake(2)), StrEq("getClassLoader"), + StrEq("()Ljava/lang/ClassLoader;"))) + .WillOnce(testing::Return(Fake(2))); + + EXPECT_CALL(*env_, CallObjectMethodV(Fake(1), Fake(2), _)) + .WillOnce(testing::Return(Fake(3))); + + EXPECT_CALL(*env_, + GetMethodID(Eq(AsGlobal(Fake(3))), StrEq("loadClass"), + StrEq("(Ljava/lang/String;)Ljava/lang/Class;"))) + .WillOnce(testing::Return(Fake(1))); + + // Note: While "/" is the mandatory delimiter for describing the class in its + // definition, load class uses "." delineation. Strangely, when calling + // Classloader.loadClass on Android both '.' and '/'work, but on x86 Java (and + // presumably other JVM implementations), only the "." is accepted. + EXPECT_CALL(*env_, NewStringUTF(StrEq("com.google.ARCore"))) + .WillOnce(testing::Return(Fake())); + + EXPECT_CALL(*env_, CallObjectMethodV(Fake(3), Fake(1), _)) + .WillOnce(testing::Return(Fake(2))); + + // Make sure we try to get the method with the loaded class, not the direct + // object class. + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("()V"))) + .WillOnce(testing::Return(Fake(1))); + + // Code under test. + jni::JvmRef jvm_ref{jvm_.get(), + kDefaultConfiguration}; + jni::LocalObject + obj1{AdoptLocal{}, Fake(1)}; + obj1("Foo"); + + this->TearDown(); +} + +} // namespace diff --git a/implementation/legacy/field_ref_test.cc b/implementation/legacy/field_ref_test.cc new file mode 100644 index 00000000..ac334809 --- /dev/null +++ b/implementation/legacy/field_ref_test.cc @@ -0,0 +1,170 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::GlobalObject; +using ::jni::LocalObject; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; + +// clang-format off +static constexpr Class java_class_under_test{ + "com/google/TestClass", + Field{"booleanField", jboolean{}}, + Field{"byteField", jbyte{}}, + Field{"charField", jchar{}}, + Field{"shortField", jshort{}}, + Field{"intField", jint{}}, + Field{"longField", jlong{}}, + Field{"floatField", jfloat{}}, + Field{"doubleField", jdouble{}} +}; +// clang-format on + +TEST_F(JniTest, Field_SimpleGetAndSet) { + EXPECT_CALL(*env_, GetFieldID(_, StrEq("SomeField"), StrEq("I"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetIntField(_, Fake())).WillOnce(Return(999)); + EXPECT_CALL(*env_, SetIntField(_, Fake(), 123)); + + static constexpr Class java_class_under_test{"com/google/TestClass", + Field("SomeField", jint{})}; + + GlobalObject obj{}; + EXPECT_EQ(999, obj["SomeField"].Get()); + obj["SomeField"].Set(123); +} + +TEST_F(JniTest, Field_BooleanField) { + EXPECT_CALL(*env_, GetBooleanField); + EXPECT_CALL(*env_, SetBooleanField); + + LocalObject obj{}; + obj["booleanField"].Get(); + obj["booleanField"].Set(true); +} + +TEST_F(JniTest, Field_ByteField) { + EXPECT_CALL(*env_, GetByteField); + EXPECT_CALL(*env_, SetByteField); + + LocalObject obj{}; + obj["byteField"].Get(); + obj["byteField"].Set(jbyte{123}); +} + +TEST_F(JniTest, Field_CharField) { + EXPECT_CALL(*env_, GetCharField); + EXPECT_CALL(*env_, SetCharField); + + LocalObject obj{}; + obj["charField"].Get(); + obj["charField"].Set(jchar{'a'}); +} + +TEST_F(JniTest, Field_ShortField) { + EXPECT_CALL(*env_, GetShortField); + EXPECT_CALL(*env_, SetShortField); + + LocalObject obj{}; + obj["shortField"].Get(); + obj["shortField"].Set(jshort{123}); +} + +TEST_F(JniTest, Field_intField) { + EXPECT_CALL(*env_, GetIntField); + EXPECT_CALL(*env_, SetIntField); + + LocalObject obj{}; + obj["intField"].Get(); + obj["intField"].Set(123); +} + +TEST_F(JniTest, Field_longField) { + EXPECT_CALL(*env_, GetLongField); + EXPECT_CALL(*env_, SetLongField); + + LocalObject obj{}; + obj["longField"].Get(); + obj["longField"].Set(123); +} + +TEST_F(JniTest, Field_floatField) { + LocalObject obj{}; + EXPECT_CALL(*env_, GetFloatField); + EXPECT_CALL(*env_, SetFloatField); + obj["floatField"].Get(); + obj["floatField"].Set(123.f); +} + +TEST_F(JniTest, Field_doubleField) { + LocalObject obj{}; + EXPECT_CALL(*env_, GetDoubleField); + EXPECT_CALL(*env_, SetDoubleField); + obj["doubleField"].Get(); + obj["doubleField"].Set(123.); +} + +TEST_F(JniTest, Field_ObjectGet) { + EXPECT_CALL(*env_, GetFieldID(_, StrEq("classField"), StrEq("LkClass2;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(_, Fake())) + .WillOnce(Return(Fake())); + + static constexpr Class kClass{"com/google/TestClass", + Field{"classField", Class{"kClass2"}}}; + static constexpr Class kClass2{"kClass2"}; + + LocalObject obj{}; + LocalObject obj2 = obj["classField"].Get(); +} + +TEST_F(JniTest, Field_ObjectSet) { + EXPECT_CALL(*env_, GetFieldID(_, StrEq("classField"), StrEq("LkClass2;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(_, Fake(), Fake())) + .Times(4); + + static constexpr Class kClass2{"kClass2"}; + static constexpr Class kClass{ + "com/google/TestClass", + // Field{"classField", Class{"kClass2"}}}; // also works + Field{"classField", kClass2}}; + + LocalObject obj{}; + LocalObject some_obj{AdoptLocal{}, Fake()}; + obj["classField"].Set(Fake()); + obj["classField"].Set(LocalObject{AdoptLocal{}, Fake()}); + obj["classField"].Set(some_obj); + obj["classField"].Set(std::move(some_obj)); +} + +} // namespace diff --git a/implementation/legacy/global_object_test.cc b/implementation/legacy/global_object_test.cc new file mode 100644 index 00000000..9589caa3 --- /dev/null +++ b/implementation/legacy/global_object_test.cc @@ -0,0 +1,285 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptGlobal; +using ::jni::AdoptLocal; +using ::jni::Class; +using ::jni::Constructor; +using ::jni::Fake; +using ::jni::Field; +using ::jni::GlobalObject; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::NewRef; +using ::jni::Params; +using ::jni::PromoteToGlobal; +using ::jni::test::AsGlobal; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::StrEq; + +TEST_F(JniTest, GlobalObject_AllowsNullPtrT) { + static constexpr Class kClass{"kClass"}; + EXPECT_CALL(*env_, NewLocalRef).Times(0); + EXPECT_CALL(*env_, NewGlobalRef).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + EXPECT_CALL(*env_, DeleteGlobalRef).Times(0); + + GlobalObject obj{nullptr}; + EXPECT_EQ(jobject{obj}, nullptr); +} + +TEST_F(JniTest, GlobalObject_CallsNewAndDeleteOnNewObject) { + static constexpr Class kClass{"kClass"}; + + EXPECT_CALL(*env_, NewObjectV).WillOnce(Return(Fake())); + EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); + + GlobalObject global_object{}; + + EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); +} + +TEST_F(JniTest, GlobalObject_ConstructsFromNonStandardConstructor) { + static constexpr Class kClass{ + "kClass", + Constructor{jfloat{}, jfloat{}}, + }; + + EXPECT_CALL(*env_, NewObjectV).WillOnce(Return(Fake())); + EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); + + GlobalObject global_object{1.f, 2.f}; + + EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); +} + +TEST_F(JniTest, GlobalObject_ComparesAgainstOtherGlobalObjects) { + static constexpr Class kClass{ + "kClass", + Constructor{jfloat{}, jfloat{}}, + }; + static constexpr Class kClass2{ + "kClass2", + }; + GlobalObject val_1{AdoptGlobal{}, Fake(1)}; + GlobalObject val_2{AdoptGlobal{}, Fake(2)}; + + EXPECT_TRUE(val_1 == val_1); + EXPECT_FALSE(val_1 == val_2); + EXPECT_TRUE(val_1 != val_2); + EXPECT_TRUE(val_2 == val_2); + EXPECT_TRUE(val_2 != val_1); + EXPECT_FALSE(val_1 == val_2); +} + +TEST_F(JniTest, GlobalObject_DoesNotDeleteAnyLocalsForAdoptedGlobalJobject) { + static constexpr Class kClass{"kClass"}; + + EXPECT_CALL(*env_, NewObjectV).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + EXPECT_CALL(*env_, DeleteGlobalRef(Fake())); + + GlobalObject global_object{AdoptGlobal{}, Fake()}; + + EXPECT_EQ(jobject{global_object}, Fake()); +} + +TEST_F(JniTest, GlobalObject_PromotesJobjectsOnConstruction) { + EXPECT_CALL(*env_, NewObjectV).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); + + static constexpr Class kClass{"kClass"}; + GlobalObject global_object{PromoteToGlobal{}, Fake()}; + EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); +} + +TEST_F(JniTest, GlobalObject_PromotesDecoratedLocals) { + EXPECT_CALL(*env_, NewObjectV).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); + + static constexpr Class kClass{"kClass"}; + LocalObject local_obj{AdoptLocal{}, Fake()}; + // GlobalObject global_object{local_obj}; // doesn't compile (good). + GlobalObject global_object{std::move(local_obj)}; + + EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); +} + +// Identical to above but Local constructed in place. +TEST_F(JniTest, GlobalObject_PromotesDecoratedLocalsFromXValue) { + EXPECT_CALL(*env_, NewObjectV).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteGlobalRef(AsGlobal(Fake()))); + + static constexpr Class kClass{"kClass"}; + GlobalObject global_object{ + LocalObject{AdoptLocal{}, Fake()}}; + + EXPECT_EQ(jobject{global_object}, AsGlobal(Fake())); +} + +TEST_F(JniTest, GlobalObject_CallsOnlyDeleteOnWrapCtor) { + EXPECT_CALL(*env_, DeleteGlobalRef(Fake())); + + static constexpr Class kClass{"com/google/CallsOnlyDeleteOnWrapCtor"}; + GlobalObject global_object{AdoptGlobal{}, Fake()}; + + EXPECT_NE(jobject{global_object}, nullptr); +} + +TEST_F(JniTest, GlobalObject_CallsNewGlobalRefOnCopy) { + static constexpr Class kClass{"kClass"}; + + EXPECT_CALL(*env_, NewGlobalRef(Fake(1))) + .WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, DeleteGlobalRef(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))).Times(0); + + GlobalObject global_object{NewRef{}, Fake(1)}; + EXPECT_EQ(jobject{global_object}, Fake(2)); +} + +TEST_F(JniTest, GlobalObject_CallsDeleteOnceAfterAMoveConstruction) { + EXPECT_CALL(*env_, DeleteGlobalRef(Fake())); + + static constexpr Class kClass{ + "com/google/CallsDeleteOnceAfterAMoveConstruction"}; + GlobalObject global_object_1{AdoptGlobal{}, Fake()}; + EXPECT_EQ(jobject{global_object_1}, Fake()); + GlobalObject global_object_2{std::move(global_object_1)}; + + EXPECT_EQ(jobject{global_object_1}, nullptr); // NOLINT + EXPECT_EQ(jobject{global_object_2}, Fake()); +} + +TEST_F(JniTest, GlobalObject_FunctionsProperlyInSTLContainer) { + EXPECT_CALL(*env_, DeleteGlobalRef(Fake(1))); + EXPECT_CALL(*env_, DeleteGlobalRef(Fake(2))); + + static constexpr Class kClass{ + "com/google/CallsDeleteOnceAfterAMoveConstruction"}; + GlobalObject global_object_1{AdoptGlobal{}, Fake(1)}; + GlobalObject global_object_2{AdoptGlobal{}, Fake(2)}; + std::tuple t{std::move(global_object_1), std::move(global_object_2)}; +} + +TEST_F(JniTest, GlobalObject_ValuesWorkAfterMoveConstructor) { + EXPECT_CALL(*env_, CallIntMethodV).Times(3); + EXPECT_CALL(*env_, SetIntField).Times(4); + + static constexpr Class kClass{ + "com/google/ValuesWorkAfterMoveConstructor", + Method{"Foo", jni::Return{}, Params{}}, + Field{"BarField", jint{}}}; + GlobalObject global_object_1{AdoptGlobal{}, Fake(1)}; + global_object_1("Foo", 1); + global_object_1("Foo", 2); + global_object_1["BarField"].Set(1); + + GlobalObject global_object_2{std::move(global_object_1)}; + global_object_2("Foo", 3); + global_object_2["BarField"].Set(2); + global_object_2["BarField"].Set(3); + global_object_2["BarField"].Set(4); + + GlobalObject global_object_3{AdoptGlobal{}, Fake(1)}; +} + +TEST_F(JniTest, GlobalObject_ObjectReturnsInstanceMethods) { + InSequence seq; + EXPECT_CALL(*env_, GetMethodID(_, StrEq(""), StrEq("()V"))) + .WillOnce(Return(Fake(1))); + EXPECT_CALL(*env_, NewObjectV(_, Fake(1), _)) + .WillOnce(Return(Fake())); + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(I)I"))) + .WillOnce(Return(Fake(2))); + EXPECT_CALL(*env_, CallIntMethodV(_, _, _)); + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Baz"), StrEq("(F)V"))) + .WillOnce(Return(Fake(2))); + EXPECT_CALL(*env_, CallVoidMethodV(_, _, _)); + + EXPECT_CALL(*env_, + GetMethodID(_, + StrEq("AMethodWithAReallyLongNameThatWouldPossiblyBeH" + "ardForTemplatesToHandle"), + StrEq("(IFIFD)D"))); + EXPECT_CALL(*env_, CallDoubleMethodV(_, _, _)); + + static constexpr Class java_class_under_test{ + "com/google/ObjectReturnsInstanceMethods", + Method{"Foo", jni::Return{}, Params{}}, + Method{"Baz", jni::Return{}, Params{}}, + Method{"AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplates" + "ToHandle", + jni::Return{}, + Params{}}}; + + GlobalObject global_object{}; + global_object("Foo", 1); + global_object("Baz", 2.f); + global_object( + "AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplatesToHandle", + int{}, float{}, int{}, float{}, double{}); +} + +TEST_F(JniTest, GlobalObject_ReleasesGlobalsForAlternateConstructors) { + static constexpr Class java_class_under_test{ + "ReleasesGlobalsForAlternateConstructors", jni::Constructor{}}; + GlobalObject g1{1}; + GlobalObject g2{2}; + GlobalObject g3{3}; + EXPECT_CALL(*env_, DeleteGlobalRef(_)).Times(3); +} + +TEST_F(JniTest, GlobalObject_SupportsPassingAPrvalue) { + static constexpr Class kTestClass1{"TestClass1"}; + static constexpr Class kTestClass2{ + "TestClass2", Method{"Foo", jni::Return{}, jni::Params{kTestClass1}}}; + + GlobalObject a{}; + GlobalObject b{}; + b("Foo", std::move(a)); +} + +TEST_F(JniTest, GlobalObjects_PromoteRValuesFromEmittedLValues) { + static constexpr Class kClass1{"TestClass1"}; + static constexpr Class kClass2{ + "TestClass2", Method{"Foo", jni::Return{kClass1}, jni::Params{}}}; + + LocalObject b{}; + GlobalObject a{b("Foo")}; + + a = b("Foo"); +} + +} // namespace diff --git a/implementation/legacy/local_array_field_multidimensional_test.cc b/implementation/legacy/local_array_field_multidimensional_test.cc new file mode 100644 index 00000000..19e327f9 --- /dev/null +++ b/implementation/legacy/local_array_field_multidimensional_test.cc @@ -0,0 +1,152 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::Array; +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::LocalObject; +using ::jni::Rank; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::StrEq; + +static constexpr Class kClass{"kClass"}; + +static constexpr Class kRank2{ + "kFieldClass", + Field{"BooleanArray", Array{jboolean{}, Rank<2>{}}}, + Field{"ByteArray", Array{jbyte{}, Rank<2>{}}}, + Field{"CharArray", Array{jchar{}, Rank<2>{}}}, + Field{"ShortArray", Array{jshort{}, Rank<2>{}}}, + Field{"IntArray", Array{jint{}, Rank<2>{}}}, + Field{"FloatArray", Array{jfloat{}, Rank<2>{}}}, + Field{"DoubleArray", Array{jdouble{}, Rank<2>{}}}, + Field{"LongArray", Array{jlong{}, Rank<2>{}}}, + Field{"ObjectArray", Array{kClass, Rank<2>{}}}, +}; + +TEST_F(JniTest, LocalArrayField_Rank2) { + EXPECT_CALL(*env_, GetFieldID(_, StrEq("BooleanArray"), StrEq("[[Z"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ByteArray"), StrEq("[[B"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("CharArray"), StrEq("[[C"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ShortArray"), StrEq("[[S"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("IntArray"), StrEq("[[I"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("FloatArray"), StrEq("[[F"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("DoubleArray"), StrEq("[[D"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("LongArray"), StrEq("[[J"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ObjectArray"), StrEq("[[LkClass;"))); + + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillOnce(::testing::Return(Fake(1))) + .WillOnce(::testing::Return(Fake(2))) + .WillOnce(::testing::Return(Fake(3))) + .WillOnce(::testing::Return(Fake(4))) + .WillOnce(::testing::Return(Fake(5))) + .WillOnce(::testing::Return(Fake(6))) + .WillOnce(::testing::Return(Fake(7))) + .WillOnce(::testing::Return(Fake(8))) + .WillOnce(::testing::Return(Fake(9))); + + LocalObject obj{AdoptLocal{}, Fake()}; + + EXPECT_EQ(static_cast(obj["BooleanArray"].Get()), + Fake(1)); + EXPECT_EQ(static_cast(obj["ByteArray"].Get()), + Fake(2)); + EXPECT_EQ(static_cast(obj["CharArray"].Get()), + Fake(3)); + EXPECT_EQ(static_cast(obj["ShortArray"].Get()), + Fake(4)); + EXPECT_EQ(static_cast(obj["IntArray"].Get()), + Fake(5)); + EXPECT_EQ(static_cast(obj["FloatArray"].Get()), + Fake(6)); + EXPECT_EQ(static_cast(obj["DoubleArray"].Get()), + Fake(7)); + EXPECT_EQ(static_cast(obj["LongArray"].Get()), + Fake(8)); + EXPECT_EQ(static_cast(obj["ObjectArray"].Get()), + Fake(9)); +} + +static constexpr Class kRank3{ + "kFieldClass", + Field{"BooleanArray", Array{jboolean{}, Rank<3>{}}}, + Field{"ByteArray", Array{jbyte{}, Rank<3>{}}}, + Field{"CharArray", Array{jchar{}, Rank<3>{}}}, + Field{"ShortArray", Array{jshort{}, Rank<3>{}}}, + Field{"IntArray", Array{jint{}, Rank<3>{}}}, + Field{"FloatArray", Array{jfloat{}, Rank<3>{}}}, + Field{"DoubleArray", Array{jdouble{}, Rank<3>{}}}, + Field{"LongArray", Array{jlong{}, Rank<3>{}}}, + Field{"ObjectArray", Array{kClass, Rank<3>{}}}, +}; + +TEST_F(JniTest, LocalArrayField_Rank3) { + EXPECT_CALL(*env_, GetFieldID(_, StrEq("BooleanArray"), StrEq("[[[Z"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ByteArray"), StrEq("[[[B"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("CharArray"), StrEq("[[[C"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ShortArray"), StrEq("[[[S"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("IntArray"), StrEq("[[[I"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("FloatArray"), StrEq("[[[F"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("DoubleArray"), StrEq("[[[D"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("LongArray"), StrEq("[[[J"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ObjectArray"), StrEq("[[[LkClass;"))); + + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillOnce(::testing::Return(Fake(1))) + .WillOnce(::testing::Return(Fake(2))) + .WillOnce(::testing::Return(Fake(3))) + .WillOnce(::testing::Return(Fake(4))) + .WillOnce(::testing::Return(Fake(5))) + .WillOnce(::testing::Return(Fake(6))) + .WillOnce(::testing::Return(Fake(7))) + .WillOnce(::testing::Return(Fake(8))) + .WillOnce(::testing::Return(Fake(9))); + + LocalObject obj{AdoptLocal{}, Fake()}; + + EXPECT_EQ(static_cast(obj["BooleanArray"].Get()), + Fake(1)); + EXPECT_EQ(static_cast(obj["ByteArray"].Get()), + Fake(2)); + EXPECT_EQ(static_cast(obj["CharArray"].Get()), + Fake(3)); + EXPECT_EQ(static_cast(obj["ShortArray"].Get()), + Fake(4)); + EXPECT_EQ(static_cast(obj["IntArray"].Get()), + Fake(5)); + EXPECT_EQ(static_cast(obj["FloatArray"].Get()), + Fake(6)); + EXPECT_EQ(static_cast(obj["DoubleArray"].Get()), + Fake(7)); + EXPECT_EQ(static_cast(obj["LongArray"].Get()), + Fake(8)); + EXPECT_EQ(static_cast(obj["ObjectArray"].Get()), + Fake(9)); +} + +} // namespace diff --git a/implementation/legacy/local_array_field_test.cc b/implementation/legacy/local_array_field_test.cc new file mode 100644 index 00000000..06839452 --- /dev/null +++ b/implementation/legacy/local_array_field_test.cc @@ -0,0 +1,480 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::Array; +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::Rank; +using ::jni::test::AsNewLocalReference; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::InSequence; +using ::testing::StrEq; + +static constexpr Class kClass2{"kClass2"}; + +//////////////////////////////////////////////////////////////////////////////// +// Fields: Smoke Tests. +//////////////////////////////////////////////////////////////////////////////// +static constexpr Class kFieldClass{ + "kFieldClass", + Field{"BooleanArray", Array{jboolean{}}}, + Field{"ByteArray", Array{jbyte{}}}, + Field{"CharArray", Array{jchar{}}}, + Field{"ShortArray", Array{jshort{}}}, + Field{"IntArray", Array{jint{}}}, + Field{"FloatArray", Array{jfloat{}}}, + Field{"DoubleArray", Array{jdouble{}}}, + Field{"LongArray", Array{jlong{}}}, + Field{"ObjectArrayRank1", Array{kClass2}}, + Field{"ObjectArrayRank2", Array{kClass2, Rank<2>{}}}, + Field{"ObjectArrayRank3", Array{kClass2, Rank<3>{}}}, +}; + +TEST_F(JniTest, ArrayField_Object_Iteration_Rank_1) { + EXPECT_CALL(*env_, GetObjectField) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength).WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(100))) + .WillOnce(::testing::Return(Fake(101))) + .WillOnce(::testing::Return(Fake(102))); + + int i = 100; + LocalObject obj{Fake()}; + for (LocalObject a : obj["ObjectArrayRank1"].Get().Pin()) { + EXPECT_EQ(static_cast(a), Fake(i)); + i++; + } +} + +TEST_F(JniTest, ArrayField_Object_Iteration_Rank_2) { + EXPECT_CALL(*env_, GetObjectField) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength).WillOnce(::testing::Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(100))) + .WillOnce(::testing::Return(Fake(101))) + .WillOnce(::testing::Return(Fake(102))); + + int i = 100; + LocalObject obj{Fake(1)}; + for (LocalArray a : + obj["ObjectArrayRank2"].Get().Pin()) { + EXPECT_EQ(static_cast(a), Fake(i)); + i++; + } +} + +TEST_F(JniTest, Array_FieldTests) { + EXPECT_CALL(*env_, GetFieldID(_, StrEq("BooleanArray"), StrEq("[Z"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ByteArray"), StrEq("[B"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("CharArray"), StrEq("[C"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ShortArray"), StrEq("[S"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("IntArray"), StrEq("[I"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("FloatArray"), StrEq("[F"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("DoubleArray"), StrEq("[D"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("LongArray"), StrEq("[J"))); + EXPECT_CALL(*env_, + GetFieldID(_, StrEq("ObjectArrayRank1"), StrEq("[LkClass2;"))); + EXPECT_CALL(*env_, + GetFieldID(_, StrEq("ObjectArrayRank2"), StrEq("[[LkClass2;"))); + EXPECT_CALL(*env_, + GetFieldID(_, StrEq("ObjectArrayRank3"), StrEq("[[[LkClass2;"))); + + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake())) + .WillOnce(::testing::Return(Fake(1))) + .WillOnce(::testing::Return(Fake(2))) + .WillOnce(::testing::Return(Fake(3))); + + LocalObject obj{AdoptLocal{}, Fake()}; + + EXPECT_EQ(static_cast(obj["BooleanArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["ByteArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["CharArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["ShortArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["IntArray"].Get()), Fake()); + EXPECT_EQ(static_cast(obj["FloatArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["DoubleArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["LongArray"].Get()), + Fake()); + EXPECT_EQ(static_cast(obj["ObjectArrayRank1"].Get()), + Fake(1)); + EXPECT_EQ(static_cast(obj["ObjectArrayRank2"].Get()), + Fake(2)); + EXPECT_EQ(static_cast(obj["ObjectArrayRank3"].Get()), + Fake(3)); +} + +TEST_F(JniTest, Array_Field_Boolean_Test) { + std::unique_ptr fake_storage_ptr(new jboolean()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("BooleanArray"), StrEq("[Z"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetBooleanArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseBooleanArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseBooleanArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["BooleanArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["BooleanArray"].Set(Fake()); + obj["BooleanArray"].Set( + LocalArray{AdoptLocal{}, Fake()}); + obj["BooleanArray"].Set(arr2); + obj["BooleanArray"].Set(obj["BooleanArray"].Get()); + + EXPECT_EQ(obj["BooleanArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["BooleanArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Byte_Test) { + std::unique_ptr fake_storage_ptr(new jbyte()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ByteArray"), StrEq("[B"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetByteArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseByteArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseByteArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["ByteArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["ByteArray"].Set(Fake()); + obj["ByteArray"].Set(LocalArray{AdoptLocal{}, Fake()}); + obj["ByteArray"].Set(arr2); + obj["ByteArray"].Set(obj["ByteArray"].Get()); + EXPECT_EQ(obj["ByteArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["ByteArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Char_Test) { + std::unique_ptr fake_storage_ptr(new jchar()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("CharArray"), StrEq("[C"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetCharArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseCharArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseCharArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["CharArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["CharArray"].Set(Fake()); + obj["CharArray"].Set(LocalArray{AdoptLocal{}, Fake()}); + obj["CharArray"].Set(arr2); + obj["CharArray"].Set(obj["CharArray"].Get()); + EXPECT_EQ(obj["CharArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["CharArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Short_Test) { + std::unique_ptr fake_storage_ptr(new jshort()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("ShortArray"), StrEq("[S"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetShortArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseShortArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseShortArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["ShortArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["ShortArray"].Set(Fake()); + obj["ShortArray"].Set(LocalArray{AdoptLocal{}, Fake()}); + obj["ShortArray"].Set(arr2); + obj["ShortArray"].Set(obj["ShortArray"].Get()); + EXPECT_EQ(obj["ShortArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["ShortArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Int_Test) { + std::unique_ptr fake_storage_ptr(new jint()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("IntArray"), StrEq("[I"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetIntArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseIntArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, ReleaseIntArrayElements( + Fake(), fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["IntArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["IntArray"].Set(Fake()); + obj["IntArray"].Set(LocalArray{AdoptLocal{}, Fake()}); + obj["IntArray"].Set(arr2); + obj["IntArray"].Set(obj["IntArray"].Get()); + EXPECT_EQ(obj["IntArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["IntArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Float_Test) { + std::unique_ptr fake_storage_ptr(new jfloat()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("FloatArray"), StrEq("[F"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetFloatArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseFloatArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseFloatArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["FloatArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["FloatArray"].Set(Fake()); + obj["FloatArray"].Set(LocalArray{AdoptLocal{}, Fake()}); + obj["FloatArray"].Set(arr2); + obj["FloatArray"].Set(obj["FloatArray"].Get()); + EXPECT_EQ(obj["FloatArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["FloatArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Double_Test) { + std::unique_ptr fake_storage_ptr(new jdouble()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("DoubleArray"), StrEq("[D"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetDoubleArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseDoubleArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseDoubleArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["DoubleArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["DoubleArray"].Set(Fake()); + obj["DoubleArray"].Set( + LocalArray{AdoptLocal{}, Fake()}); + obj["DoubleArray"].Set(arr2); + obj["DoubleArray"].Set(obj["DoubleArray"].Get()); + EXPECT_EQ(obj["DoubleArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["DoubleArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Long_Test) { + std::unique_ptr fake_storage_ptr(new jlong()); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("LongArray"), StrEq("[J"))) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetLongArrayElements) + .WillRepeatedly(::testing::Return(fake_storage_ptr.get())); + EXPECT_CALL(*env_, ReleaseLongArrayElements(Fake(), + fake_storage_ptr.get(), 0)); + EXPECT_CALL(*env_, + ReleaseLongArrayElements(Fake(), + fake_storage_ptr.get(), JNI_ABORT)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr{obj["LongArray"].Get()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + obj["LongArray"].Set(Fake()); + obj["LongArray"].Set(LocalArray{AdoptLocal{}, Fake()}); + obj["LongArray"].Set(arr2); + obj["LongArray"].Set(obj["LongArray"].Get()); + EXPECT_EQ(obj["LongArray"].Get().Pin().ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj["LongArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); +} + +TEST_F(JniTest, Array_Field_Object_Test) { + EXPECT_CALL(*env_, + GetFieldID(_, StrEq("ObjectArrayRank1"), StrEq("[LkClass2;"))) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetObjectField(Fake(), Fake())) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, SetObjectField(Fake(), Fake(), + Fake())) + .Times(4); + EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 2)); + + LocalObject obj{AdoptLocal{}, Fake()}; + LocalArray arr2{AdoptLocal{}, Fake()}; + LocalArray arr{obj["ObjectArrayRank1"].Get()}; + obj["ObjectArrayRank1"].Set(Fake()); + obj["ObjectArrayRank1"].Set( + LocalArray{AdoptLocal{}, Fake()}); + obj["ObjectArrayRank1"].Set(arr2); + obj["ObjectArrayRank1"].Set(obj["ObjectArrayRank1"].Get()); + obj["ObjectArrayRank1"].Get().Get(2); +} + +TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_1) { + static constexpr Class kClass2{"kClass2"}; + + static constexpr Class kClass{ + "ArrayMultiTest", + Field{"Foo", Array{kClass2}}, + }; + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("Foo"), StrEq("[LkClass2;"))); + + LocalObject obj{Fake()}; + LocalArray{obj["Foo"].Get()}; +} + +TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_2) { + static constexpr Class kClass2{"kClass2"}; + + static constexpr Class kClass{ + "kClass", + Field{"Foo", Array{kClass2, Rank<2>{}}}, + }; + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("Foo"), StrEq("[[LkClass2;"))); + + LocalObject obj{Fake()}; + LocalArray arr_from_field{obj["Foo"].Get()}; +} + +TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_2_Iteration) { + static constexpr Class kClass2{"kClass2"}; + + static constexpr Class kClass{ + "kClass", + Field{"Foo", Array{kClass2, Rank<2>{}}}, + }; + + InSequence seq; + + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + + EXPECT_CALL(*env_, GetFieldID(_, StrEq("Foo"), StrEq("[[LkClass2;"))); + EXPECT_CALL(*env_, GetObjectField) + .WillRepeatedly(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetArrayLength).WillOnce(::testing::Return(3)); + + // A temporary local array is materialised in the field access. + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + + // Internal elements looped over. + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(100))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(100))); + + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(101))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(101))); + + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(::testing::Return(Fake(102))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(102))); + + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + + // Object with queried array field. + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); + + LocalObject obj{AdoptLocal{}, Fake(1)}; + int i = 100; + for (LocalArray obj : obj["Foo"].Get().Pin()) { + EXPECT_EQ(static_cast(obj), Fake(i)); + i++; + } +} + +} // namespace diff --git a/implementation/legacy/local_array_iteration_test.cc b/implementation/legacy/local_array_iteration_test.cc new file mode 100644 index 00000000..3432e8ed --- /dev/null +++ b/implementation/legacy/local_array_iteration_test.cc @@ -0,0 +1,262 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::ArrayView; +using ::jni::Class; +using ::jni::Fake; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::test::AsNewLocalReference; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; + +static constexpr Class kClass{"kClass"}; + +//////////////////////////////////////////////////////////////////////////////// +// Rank 0. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, IteratesOver1DRange) { + std::array expected{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; + + EXPECT_CALL(*env_, GetArrayLength).WillOnce(Return(10)); + EXPECT_CALL(*env_, GetIntArrayElements) + .WillRepeatedly(Return(expected.data())); + + LocalArray new_array{AdoptLocal{}, Fake()}; + + int i{0}; + for (jint val : new_array.Pin()) { + EXPECT_EQ(val, expected[i]); + ++i; + } + EXPECT_EQ(i, 10); +} + +TEST_F(JniTest, WorksWithSTLComparison) { + std::array expected{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; + + EXPECT_CALL(*env_, GetArrayLength).WillOnce(Return(10)); + EXPECT_CALL(*env_, GetIntArrayElements) + .WillRepeatedly(Return(expected.data())); + + LocalArray new_array{AdoptLocal{}, Fake()}; + ArrayView array_view = new_array.Pin(); + EXPECT_TRUE( + std::equal(array_view.begin(), array_view.end(), expected.begin())); +} + +TEST_F(JniTest, WorksWithSTLComparisonOfObjects) { + std::array expected{Fake(1), Fake(2), Fake(3)}; + + EXPECT_CALL(*env_, GetArrayLength).WillOnce(Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(Return(Fake(1))) + .WillOnce(Return(Fake(2))) + .WillOnce(Return(Fake(3))); + + LocalArray new_array{Fake()}; + ArrayView array_view = new_array.Pin(); + EXPECT_TRUE( + std::equal(array_view.begin(), array_view.end(), expected.begin())); +} + +TEST_F(JniTest, WorksWithSTLComparisonOfRichlyDecoratedObjects) { + std::array expected{LocalObject{AdoptLocal{}, Fake(1)}, + LocalObject{AdoptLocal{}, Fake(2)}, + LocalObject{AdoptLocal{}, Fake(3)}}; + + EXPECT_CALL(*env_, GetArrayLength).WillOnce(Return(3)); + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(Return(Fake(1))) + .WillOnce(Return(Fake(2))) + .WillOnce(Return(Fake(3))); + + LocalArray new_array{AdoptLocal{}, Fake()}; + ArrayView array_view = new_array.Pin(); + EXPECT_TRUE( + std::equal(array_view.begin(), array_view.end(), expected.begin())); +} + +TEST_F(JniTest, 2D_Iterates) { + int a[5] = {1, 2, 3, 4, 5}; + std::array expected{1, 2, 3, 4, 5}; + + EXPECT_CALL(*env_, FindClass(StrEq("[I"))); + EXPECT_CALL(*env_, NewObjectArray(5, _, Fake())); + EXPECT_CALL(*env_, GetArrayLength) + .WillOnce(Return(5)) // outer + .WillOnce(Return(1)) // inner: 1, running sum: 1 + .WillOnce(Return(2)) // inner: 1, running sum: 3 + .WillOnce(Return(3)) // inner: 1, running sum: 6 + .WillOnce(Return(4)) // inner: 1, running sum: 10 + .WillOnce(Return(5)); // inner: 1, running sum: 15 + + // 5: Once per outer array element. + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(Return(Fake(1))) + .WillOnce(Return(Fake(2))) + .WillOnce(Return(Fake(3))) + .WillOnce(Return(Fake(4))) + .WillOnce(Return(Fake(5))); + + // All the returned intArrays are deleted. + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(3))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(4))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(5))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + + // 5 (outer array length) * 2 (pins per) = 10 + EXPECT_CALL(*env_, GetIntArrayElements).Times(10).WillRepeatedly(Return(a)); + EXPECT_CALL(*env_, ReleaseIntArrayElements).Times(10); + + LocalArray new_array{5, Fake()}; + + int sum = 0; + for (LocalArray arr_1d : new_array.Pin()) { + // Note: Each `Pin` below triggers a separate `GetIntArrayElements`. + { + auto pinn = arr_1d.Pin(); + EXPECT_TRUE(std::equal(pinn.begin(), pinn.end(), expected.begin())); + } + + // Note: GetArrayLength is not called again (it's cached). + { + for (jint val : arr_1d.Pin()) { + sum += val; + } + } + } + + // 1 + 3 + 6 + 10 + 15 = 35 + EXPECT_EQ(sum, 35); +} + +// Identical to above except with raw loops. +TEST_F(JniTest, 2D_Iterates_Raw_loops) { + int a[5] = {1, 2, 3, 4, 5}; + std::array expected{1, 2, 3, 4, 5}; + + EXPECT_CALL(*env_, FindClass(StrEq("[I"))); + EXPECT_CALL(*env_, NewObjectArray(5, _, Fake())); + EXPECT_CALL(*env_, GetArrayLength) + .WillOnce(Return(5)) // outer + .WillOnce(Return(1)) // inner: 1, running sum: 1 + .WillOnce(Return(2)) // inner: 1, running sum: 3 + .WillOnce(Return(3)) // inner: 1, running sum: 6 + .WillOnce(Return(4)) // inner: 1, running sum: 10 + .WillOnce(Return(5)); // inner: 1, running sum: 15 + + // 5: Once per outer array element. + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(Return(Fake(1))) + .WillOnce(Return(Fake(2))) + .WillOnce(Return(Fake(3))) + .WillOnce(Return(Fake(4))) + .WillOnce(Return(Fake(5))); + + // All the returned intArrays are deleted. + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(3))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(4))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(5))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + + // 5 (outer array length) * 2 (pins per) = 10 + EXPECT_CALL(*env_, GetIntArrayElements).Times(10).WillRepeatedly(Return(a)); + EXPECT_CALL(*env_, ReleaseIntArrayElements).Times(10); + + LocalArray new_array{5, Fake()}; + + int sum = 0; + int loop_size = new_array.Length(); + for (int i = 0; i < loop_size; ++i) { + LocalArray arr_1d{new_array.Get(i)}; + + // Note: Each `Pin` below triggers a separate `GetIntArrayElements`. + { + auto pinn = arr_1d.Pin(); + EXPECT_TRUE(std::equal(pinn.begin(), pinn.end(), expected.begin())); + } + + // Note: GetArrayLength is not called again (it's cached). + { + for (jint val : arr_1d.Pin()) { + sum += val; + } + } + } + + // 1 + 3 + 6 + 10 + 15 = 35 + EXPECT_EQ(sum, 35); +} + +// Identical to above except with object loops. +TEST_F(JniTest, 2D_Iterates_Raw_loops_of_Objects) { + EXPECT_CALL(*env_, FindClass(StrEq("[LkClass;"))); + EXPECT_CALL(*env_, NewObjectArray(5, _, Fake(100))); + EXPECT_CALL(*env_, GetArrayLength).WillOnce(Return(5)); // outer + + EXPECT_CALL(*env_, GetObjectArrayElement) + .WillOnce(Return(Fake(1))) + .WillOnce(Return(Fake(2))) + .WillOnce(Return(Fake(3))) + .WillOnce(Return(Fake(4))) + .WillOnce(Return(Fake(5))); + + // All the returned objectArrays are deleted. + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(3))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(4))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(5))); + + // Note: This is just 0 (default), not 100. 100 is the sample (i.e. template) + // object, no arg is the default that is created. + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + + LocalArray new_array{5, Fake(100)}; + EXPECT_EQ(jobject{new_array}, Fake()); + + int i = 1; + for (LocalArray arr : new_array.Pin()) { + EXPECT_EQ(jobject(arr), Fake(i)); + ++i; + } +} + +} // namespace diff --git a/implementation/legacy/local_array_method_multidimensional_test.cc b/implementation/legacy/local_array_method_multidimensional_test.cc new file mode 100644 index 00000000..1e1a9719 --- /dev/null +++ b/implementation/legacy/local_array_method_multidimensional_test.cc @@ -0,0 +1,119 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::Array; +using ::jni::Class; +using ::jni::Fake; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::Params; +using ::jni::Rank; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::StrEq; + +static constexpr Class kClass2{"kClass2"}; + +//////////////////////////////////////////////////////////////////////////////// +// As Return. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, V_2I) { + static constexpr Class kClass{"kClass", + Method{"I", jni::Return{Array{}}}}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("I"), StrEq("()[[I"))); + + LocalObject obj{Fake()}; + obj("I"); +} + +TEST_F(JniTest, V_2LkClass) { + static constexpr Class kClass{ + "kClass", Method{"Foo", jni::Return{Array{kClass2, Rank<2>{}}}}}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("()[[LkClass2;"))); + + LocalObject obj{Fake()}; + obj("Foo"); +} + +//////////////////////////////////////////////////////////////////////////////// +// Complex: Arrays in Params & Return. +//////////////////////////////////////////////////////////////////////////////// +static constexpr Class kArrClass{ + "ArrClass", + Method{"Foo", jni::Return{Array{jint{}}}, Params{Array{}}}, + Method{"Baz", jni::Return{Array{kClass2}}, Params{Array{}}}, + Method{"Bar", jni::Return{Array{Class{"kClass3"}, Rank<2>{}}}, + Params{Array{}, Array{double{}}}}, +}; + +TEST_F(JniTest, 3I1D_2LkClass) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Bar"), StrEq("([[[I[D)[[LkClass3;"))); + + LocalObject obj{Fake()}; + obj("Bar", Fake(), Fake()); +} + +TEST_F(JniTest, 2I_I) { + static constexpr Class kClass{ + "kClass", Method{"I", jni::Return{}, Params{Array{}}}}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("I"), StrEq("([[I)I"))); + + LocalObject obj{Fake()}; + // obj("I", jintArray{nullptr}); // doesn't compile (good). + obj("I", Fake()); + obj("I", LocalArray{Fake()}); +} + +TEST_F(JniTest, 3I_1LkClass) { + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Baz"), StrEq("([[[I)[LkClass2;"))); + + LocalObject obj{Fake()}; + + // The compiler complains of unused variable here. This would be worth digging + // into to understand the underlying cause (i.e. only the following fails). + // LocalArray ret = obj("Baz", jobjectArray{nullptr}); + + // TODO(b/143908983): CTAD is failing. + // LocalArray ret = obj("Baz", jobjectArray{nullptr}); + + LocalArray ret = obj("Baz", Fake()); + static_assert(std::is_same_v>); +} + +TEST_F(JniTest, 3I1D_1LkClass) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Bar"), StrEq("([[[I[D)[[LkClass3;"))); + + LocalObject obj{Fake()}; + obj("Bar", Fake(), Fake()); +} + +} // namespace diff --git a/implementation/legacy/local_array_method_test.cc b/implementation/legacy/local_array_method_test.cc new file mode 100644 index 00000000..afb7f4c7 --- /dev/null +++ b/implementation/legacy/local_array_method_test.cc @@ -0,0 +1,228 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::Array; +using ::jni::Class; +using ::jni::Fake; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::Params; +using ::jni::Return; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::StrEq; + +static constexpr Class kClass2{"kClass2"}; + +//////////////////////////////////////////////////////////////////////////////// +// As Return. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ReturnSmokeTest) { + static constexpr Class kClass{ + "kClass", + Method{"BooleanArray", Return{Array{jboolean{}}}}, + Method{"ByteArray", Return{Array{jbyte{}}}}, + Method{"CharArray", Return{Array{jchar{}}}}, + Method{"ShortArray", Return{Array{jshort{}}}}, + Method{"IntArray", Return{Array{jint{}}}}, + Method{"FloatArray", Return{Array{jfloat{}}}}, + Method{"DoubleArray", Return{Array{jdouble{}}}}, + Method{"LongArray", Return{Array{jlong{}}}}, + Method{"ObjectArray", Return{Array{kClass2}}}, + }; + + EXPECT_CALL(*env_, CallObjectMethodV) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())); + + LocalObject obj{Fake()}; + EXPECT_CALL(*env_, GetMethodID(_, StrEq("BooleanArray"), StrEq("()[Z"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ByteArray"), StrEq("()[B"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("CharArray"), StrEq("()[C"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ShortArray"), StrEq("()[S"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("IntArray"), StrEq("()[I"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("LongArray"), StrEq("()[J"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("FloatArray"), StrEq("()[F"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("DoubleArray"), StrEq("()[D"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("ObjectArray"), StrEq("()[LkClass2;"))); + + LocalArray bool_array{obj("BooleanArray")}; + EXPECT_EQ((static_cast(bool_array)), (Fake())); + + LocalArray byte_array{obj("ByteArray")}; + EXPECT_EQ((static_cast(byte_array)), (Fake())); + + LocalArray char_array{obj("CharArray")}; + EXPECT_EQ((static_cast(char_array)), (Fake())); + + LocalArray short_array{obj("ShortArray")}; + EXPECT_EQ((static_cast(short_array)), (Fake())); + + LocalArray int_array{obj("IntArray")}; + EXPECT_EQ((static_cast(int_array)), (Fake())); + + LocalArray long_array{obj("LongArray")}; + EXPECT_EQ((static_cast(long_array)), (Fake())); + + LocalArray float_array{obj("FloatArray")}; + EXPECT_EQ((static_cast(float_array)), (Fake())); + + LocalArray double_array{obj("DoubleArray")}; + EXPECT_EQ((static_cast(double_array)), (Fake())); + + LocalArray object_array{obj("ObjectArray")}; + EXPECT_EQ((static_cast(object_array)), (Fake())); +} + +//////////////////////////////////////////////////////////////////////////////// +// As Params. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ParamsSmokeTest) { + static constexpr Class kClass{ + "kClass", + Method{"BooleanArray", Return{}, Params{Array{jboolean{}}}}, + Method{"ByteArray", Return{}, Params{Array{jbyte{}}}}, + Method{"CharArray", Return{}, Params{Array{jchar{}}}}, + Method{"ShortArray", Return{}, Params{Array{jshort{}}}}, + Method{"IntArray", Return{}, Params{Array{jint{}}}}, + Method{"FloatArray", Return{}, Params{Array{jfloat{}}}}, + Method{"DoubleArray", Return{}, Params{Array{jdouble{}}}}, + Method{"LongArray", Return{}, Params{Array{jlong{}}}}, + Method{"ObjectArray", Return{}, Params{Array{kClass2}}}, + }; + + LocalObject obj{Fake()}; + EXPECT_CALL(*env_, GetMethodID(_, StrEq("BooleanArray"), StrEq("([Z)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ByteArray"), StrEq("([B)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("CharArray"), StrEq("([C)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ShortArray"), StrEq("([S)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("IntArray"), StrEq("([I)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("FloatArray"), StrEq("([F)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("DoubleArray"), StrEq("([D)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("LongArray"), StrEq("([J)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("ObjectArray"), StrEq("([LkClass2;)V"))); + + obj("BooleanArray", LocalArray{Fake()}); + obj("ByteArray", LocalArray{Fake()}); + obj("CharArray", LocalArray{Fake()}); + obj("ShortArray", LocalArray{Fake()}); + obj("IntArray", LocalArray{Fake()}); + obj("FloatArray", LocalArray{Fake()}); + obj("DoubleArray", LocalArray{Fake()}); + obj("LongArray", LocalArray{Fake()}); + obj("ObjectArray", LocalArray{Fake()}); +} + +//////////////////////////////////////////////////////////////////////////////// +// As Complex. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, ComplexSmokeTest) { + static constexpr Class kClass{ + "kClass", + Method{"BooleanArray", Return{Array{jboolean{}}}, + Params{Array{jboolean{}}}}, + Method{"ByteArray", Return{Array{jbyte{}}}, Params{Array{jbyte{}}}}, + Method{"CharArray", Return{Array{jchar{}}}, Params{Array{jchar{}}}}, + Method{"ShortArray", Return{Array{jshort{}}}, Params{Array{jshort{}}}}, + Method{"IntArray", Return{Array{jint{}}}, Params{Array{jint{}}}}, + Method{"FloatArray", Return{Array{jfloat{}}}, Params{Array{jfloat{}}}}, + Method{"DoubleArray", Return{Array{jdouble{}}}, Params{Array{jdouble{}}}}, + Method{"LongArray", Return{Array{jlong{}}}, Params{Array{jlong{}}}}, + Method{"ObjectArray", Return{Array{kClass2}}, Params{Array{kClass2}}}, + }; + + EXPECT_CALL(*env_, CallObjectMethodV) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())) + .WillOnce(testing::Return(Fake())); + + LocalObject obj{Fake()}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("BooleanArray"), StrEq("([Z)[Z"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ByteArray"), StrEq("([B)[B"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("CharArray"), StrEq("([C)[C"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ShortArray"), StrEq("([S)[S"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("IntArray"), StrEq("([I)[I"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("FloatArray"), StrEq("([F)[F"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("DoubleArray"), StrEq("([D)[D"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("LongArray"), StrEq("([J)[J"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("ObjectArray"), + StrEq("([LkClass2;)[LkClass2;"))); + + LocalArray bool_array{ + obj("BooleanArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(bool_array)), (Fake())); + + LocalArray byte_array{ + obj("ByteArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(byte_array)), (Fake())); + + LocalArray char_array{ + obj("CharArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(char_array)), (Fake())); + + LocalArray short_array{ + obj("ShortArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(short_array)), (Fake())); + + LocalArray int_array{ + obj("IntArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(int_array)), (Fake())); + + LocalArray float_array{ + obj("FloatArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(float_array)), (Fake())); + + LocalArray double_array{ + obj("DoubleArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(double_array)), (Fake())); + + LocalArray long_array{ + obj("LongArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(long_array)), (Fake())); + + LocalArray object_array{obj( + "ObjectArray", LocalArray{Fake()})}; + EXPECT_EQ((static_cast(object_array)), (Fake())); +} + +} // namespace diff --git a/implementation/legacy/local_array_multidimensional_test.cc b/implementation/legacy/local_array_multidimensional_test.cc new file mode 100644 index 00000000..3336a7d6 --- /dev/null +++ b/implementation/legacy/local_array_multidimensional_test.cc @@ -0,0 +1,112 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::Class; +using ::jni::Fake; +using ::jni::LocalArray; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::StrEq; + +static constexpr Class kClass{"kClass"}; + +//////////////////////////////////////////////////////////////////////////////// +// Construction. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, BuildsFromSizeForMultiDimensional_no_value) { + EXPECT_CALL(*env_, NewObjectArray(10, _, Fake())); + + LocalArray{std::size_t{10}, Fake()}; +} + +TEST_F(JniTest, BuildsFromSizeForMultiDimensional_primitive_xref) { + EXPECT_CALL(*env_, FindClass(StrEq("[I"))); + EXPECT_CALL(*env_, NewObjectArray(10, _, Fake())); + + LocalArray{std::size_t{10}, + LocalArray{AdoptLocal{}, Fake()}}; +} + +TEST_F(JniTest, BuildsFromSizeForMultiDimensional_primitive_lvalue) { + EXPECT_CALL(*env_, NewObjectArray(10, _, Fake())); + + LocalArray arr{AdoptLocal{}, Fake()}; + LocalArray{std::size_t{10}, arr}; +} + +//////////////////////////////////////////////////////////////////////////////// +// Getters. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, GetsIntValues) { + EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 0)) + .WillOnce(::testing::Return(Fake(0))); + EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 1)) + .WillOnce(::testing::Return(Fake(1))); + EXPECT_CALL(*env_, GetObjectArrayElement(Fake(), 2)) + .WillOnce(::testing::Return(Fake(2))); + LocalArray arr{std::size_t{10}, Fake()}; + + EXPECT_EQ((static_cast(arr.Get(0))), (Fake(0))); + EXPECT_EQ((static_cast(arr.Get(1))), (Fake(1))); + EXPECT_EQ((static_cast(arr.Get(2))), (Fake(2))); +} + +//////////////////////////////////////////////////////////////////////////////// +// Setters. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, SetsIntValues) { + EXPECT_CALL( + *env_, SetObjectArrayElement(Fake(), 0, Fake())); + EXPECT_CALL( + *env_, SetObjectArrayElement(Fake(), 1, Fake())); + EXPECT_CALL( + *env_, SetObjectArrayElement(Fake(), 2, Fake())); + + LocalArray array_arg{AdoptLocal{}, Fake()}; + LocalArray arr{std::size_t{10}, Fake()}; + arr.Set(0, array_arg); + arr.Set(1, array_arg); + arr.Set(2, std::move(array_arg)); +} + +TEST_F(JniTest, SetsObjectValues) { + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(1), 0, + Fake(2))); + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(1), 1, + Fake(2))); + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(1), 2, + Fake(2))); + + LocalArray array_arg{AdoptLocal{}, Fake(2)}; + LocalArray arr{AdoptLocal{}, Fake(1)}; + arr.Set(0, array_arg); + arr.Set(1, array_arg); + arr.Set(2, std::move(array_arg)); +} + +} // namespace diff --git a/implementation/legacy/local_array_string_test.cc b/implementation/legacy/local_array_string_test.cc new file mode 100644 index 00000000..a78a221b --- /dev/null +++ b/implementation/legacy/local_array_string_test.cc @@ -0,0 +1,204 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +using ::jni::Array; +using ::jni::ArrayStrip_t; +using ::jni::CDecl_t; +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::kJavaLangString; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::LocalString; +using ::jni::Method; +using ::jni::Params; +using ::jni::Rank; +using ::jni::RegularToArrayTypeMap_t; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; + +namespace { + +static constexpr Class kClass{"kClass"}; + +//////////////////////////////////////////////////////////////////////////////// +// Strings are unusual in that they have their own type (jstring) but are +// almost completely objects otherwise. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Construction / Materialisation. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, Array_ConstructsFromAnotherStringArray) { + LocalArray arr_1{Fake()}; + LocalArray arr_2{std::move(arr_1)}; +} + +TEST_F(JniTest, Array_CorrectSignatureForStringParams) { + EXPECT_CALL(*env_, FindClass(StrEq("ClassThatReturnsArrays"))); + EXPECT_CALL(*env_, FindClass(StrEq("java/lang/String"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("StringArray"), + StrEq("([Ljava/lang/String;)V"))); + + static constexpr Class kClass{ + "ClassThatReturnsArrays", + Method{"StringArray", jni::Return{}, jni::Params{Array{jstring{}}}}, + }; + + LocalObject obj{jobject{nullptr}}; + LocalArray arr{3}; + obj("StringArray", arr); +} + +//////////////////////////////////////////////////////////////////////////////// +// Caches Length. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, Array_CachesLengthExactlyOnceOnFirstRequest) { + EXPECT_CALL(*env_, GetArrayLength).Times(0); + + LocalArray obj{Fake()}; + + EXPECT_CALL(*env_, GetArrayLength).Times(1).WillOnce(Return(5)); + EXPECT_EQ(obj.Length(), 5); + EXPECT_EQ(obj.Length(), 5); + EXPECT_EQ(obj.Length(), 5); +} + +//////////////////////////////////////////////////////////////////////////////// +// Setters. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, Array_LocalStringRValuesCanBeSet) { + EXPECT_CALL(*env_, DeleteLocalRef(_)) + .Times(1); // array (object is moved from) + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); // FindClass + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(0); + + LocalArray arr{3}; + arr.Set(0, LocalString{Fake()}); +} + +TEST_F(JniTest, Array_LocalVanillaObjectRValuesCanBeSet) { + // Unfortunately this is getting cached separately by `LocalArray`. + // In the future, this should drop to 1. + EXPECT_CALL(*env_, FindClass(StrEq("java/lang/String"))).Times(2); + + EXPECT_CALL(*env_, DeleteLocalRef(_)).Times(2); // array, in place obj + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(2); // FindClass + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(0); + + LocalArray arr{ + 3, LocalObject{"Foo"}}; + arr.Set(0, LocalObject{Fake()}); +} + +//////////////////////////////////////////////////////////////////////////////// +// As Return. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, Array_CorrectReturnSignatureForStrings) { + static constexpr Class kClass{ + "kClass", + Method{"StringArray", jni::Return{Array{jstring{}}}}, + }; + + LocalObject obj{Fake()}; + EXPECT_CALL(*env_, GetMethodID(_, StrEq("StringArray"), + StrEq("()[Ljava/lang/String;"))); + LocalArray arr = obj("StringArray"); +} + +//////////////////////////////////////////////////////////////////////////////// +// As Param. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, Array_CorrectParamSignatureForStrings) { + static constexpr Class kClass{ + "kClass", + Method{"StringArray", jni::Return{}, Params{Array{jstring{}}}}, + }; + + LocalObject obj{Fake()}; + EXPECT_CALL(*env_, GetMethodID(_, StrEq("StringArray"), + StrEq("([Ljava/lang/String;)V"))); + LocalArray arr{2}; + obj("StringArray", arr); +} + +TEST_F(JniTest, LocalStringArray_ConstructsObjectsForLValues) { + // Unlike POD, objects are constructed with a size, a jclass, and an init + // object. This makes for a slightly different API then other objects. + EXPECT_CALL(*env_, NewObjectArray); + + LocalObject default_object{}; + LocalArray local_object_array{5, default_object}; +} + +/* +// TODO(b/143908983): Restore after fixing ubsan failures. +TEST_F(JniTest, Array_StringsCanBeSetOnLocalString) { + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(), 0, _)); + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(), 1, _)); + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(), 2, _)); + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(), 3, _)); + EXPECT_CALL(*env_, SetObjectArrayElement(Fake(), 4, _)); + + const char* kFoo = "kFoo"; + const std::string kNar = "kNar"; + + LocalArray arr{5, LocalObject{"Foo"}}; + arr.Set(0, "Bar"); + arr.Set(1, std::string{"Baz"}); + arr.Set(2, std::string_view{"Bar"}); + arr.Set(3, std::string_view{kFoo}); + arr.Set(4, std::string_view{kNar}); +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +// String Fields. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, Array_CorrectFieldSignatureForStrings) { + static constexpr Class kClass{ + "kClass", + Field{"StringArrayRank1", Array{jstring{}}}, + Field{"StringArrayRank2", Array{jstring{}, Rank<2>{}}}, + Field{"StringArrayRank3", Array{jstring{}, Rank<3>{}}}, + }; + + LocalObject obj{jobject{nullptr}}; + EXPECT_CALL(*env_, GetFieldID(_, StrEq("StringArrayRank1"), + StrEq("[Ljava/lang/String;"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("StringArrayRank2"), + StrEq("[[Ljava/lang/String;"))); + EXPECT_CALL(*env_, GetFieldID(_, StrEq("StringArrayRank3"), + StrEq("[[[Ljava/lang/String;"))); + + LocalArray arr1 = obj["StringArrayRank1"].Get(); + LocalArray arr2 = obj["StringArrayRank2"].Get(); + LocalArray arr3 = obj["StringArrayRank3"].Get(); +} + +} // namespace diff --git a/implementation/legacy/local_object_test.cc b/implementation/legacy/local_object_test.cc new file mode 100644 index 00000000..0d71d462 --- /dev/null +++ b/implementation/legacy/local_object_test.cc @@ -0,0 +1,327 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::NewRef; +using ::jni::Params; +using ::jni::test::AsNewLocalReference; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::InSequence; +using ::testing::StrEq; + +static constexpr Class kClass{"kClass"}; +static constexpr Class kClass2{"kClass2"}; + +TEST_F(JniTest, LocalObject_AllowsNullPtrT) { + EXPECT_CALL(*env_, NewLocalRef).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + + LocalObject obj{nullptr}; + EXPECT_EQ(jobject{obj}, nullptr); +} + +TEST_F(JniTest, LocalObject_DoesntTryToDeleteNull) { + EXPECT_CALL(*env_, NewLocalRef).Times(0); + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + + LocalObject obj{jobject{nullptr}}; + EXPECT_EQ(jobject{obj}, nullptr); +} + +TEST_F(JniTest, LocalObject_CallsNewAndDeleteOnNewObject) { + EXPECT_CALL(*env_, NewObjectV).WillOnce(testing::Return(Fake())); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); + + LocalObject obj{}; + EXPECT_EQ(jobject{obj}, Fake()); +} + +TEST_F(JniTest, LocalObject_CallsOnlyDeleteOnWrapCtor) { + EXPECT_CALL(*env_, NewLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))) + .Times(1); + + LocalObject obj{Fake()}; + EXPECT_NE(jobject{obj}, nullptr); +} + +TEST_F(JniTest, LocalObject_CallsNewLocalRefByDefault) { + EXPECT_CALL(*env_, NewLocalRef).WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + + LocalObject obj{Fake(1)}; + EXPECT_EQ(jobject{obj}, Fake(2)); +} + +TEST_F(JniTest, LocalObject_CallsNewLocalRefOnCopy) { + EXPECT_CALL(*env_, NewLocalRef).WillOnce(::testing::Return(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + + LocalObject obj{NewRef{}, Fake(1)}; + EXPECT_EQ(jobject{obj}, Fake(2)); +} + +TEST_F(JniTest, LocalObject_ObjectReturnsInstanceMethods) { + // This test doesn't use the default JniTest helpers to be a little more + // explicit about exactly what calls must be made in what order. + static constexpr Class kClass{ + "com/google/AnotherClass", + Method{"Foo", jni::Return{}, Params{}}, + Method{"Baz", jni::Return{}, Params{}}, + Method{"AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplates" + "ToHandle", + jni::Return{}, + Params{}}}; + + InSequence seq; + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); + EXPECT_CALL(*env_, GetMethodID(_, StrEq(""), StrEq("()V"))) + .WillOnce(testing::Return(Fake(1))); + EXPECT_CALL(*env_, NewObjectV(_, Fake(1), _)) + .WillOnce(testing::Return(Fake())); + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(I)I"))) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Baz"), StrEq("(F)V"))) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, + GetMethodID(_, + StrEq("AMethodWithAReallyLongNameThatWouldPossiblyBeH" + "ardForTemplatesToHandle"), + StrEq("(IFIFD)D"))) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); + + LocalObject obj{}; + obj("Foo", 12345); + obj("Baz", 12345.f); + obj("AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplatesToHandle", + 12345, 12345.f, 12345, 12345.f, jdouble{12345}); +} + +TEST_F(JniTest, LocalObject_CallsDeleteOnceAfterAMoveConstruction) { + EXPECT_CALL(*env_, NewLocalRef).Times(1); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))) + .Times(1); + + LocalObject obj_1{Fake()}; + + EXPECT_NE(jobject{obj_1}, nullptr); + + LocalObject obj_2{std::move(obj_1)}; + + EXPECT_NE(jobject{obj_2}, nullptr); +} + +TEST_F(JniTest, LocalObject_FunctionsProperlyInSTLContainer) { + EXPECT_CALL(*env_, NewLocalRef).Times(2); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake(1)))); + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake(2)))); + + LocalObject obj_1{Fake(1)}; + LocalObject obj_2{Fake(2)}; + std::tuple t{std::move(obj_1), std::move(obj_2)}; +} + +TEST_F(JniTest, LocalObject_ValuesWorkAfterMoveConstructor) { + static constexpr Class kClass{ + "com/google/ValuesWorkAfterMoveConstructor", + Method{"Foo", jni::Return{}, Params{}}, + Field{"BarField", jint{}}}; + + EXPECT_CALL(*env_, CallIntMethodV).Times(3); + EXPECT_CALL(*env_, SetIntField).Times(4); + + LocalObject obj_1{Fake()}; + obj_1("Foo", 1); + obj_1("Foo", 2); + obj_1["BarField"].Set(1); + + LocalObject obj_2{std::move(obj_1)}; + obj_2("Foo", 3); + obj_2["BarField"].Set(2); + obj_2["BarField"].Set(3); + obj_2["BarField"].Set(4); +} + +TEST_F(JniTest, LocalObject_ReleasesLocalsForAlternateConstructors) { + static constexpr Class kClass{"ReleasesLocalsForAlternateConstructors", + jni::Constructor{}}; + LocalObject g1{1}; + LocalObject g2{2}; + LocalObject g3{3}; + EXPECT_CALL(*env_, DeleteLocalRef(_)).Times(3); +} + +TEST_F(JniTest, LocalObject_ComparesAgainstOtherLocalObjects) { + LocalObject val_1{Fake(1)}; + LocalObject val_2{Fake(2)}; + + EXPECT_TRUE(val_1 == val_1); + EXPECT_FALSE(val_1 == val_2); + EXPECT_TRUE(val_1 != val_2); + EXPECT_TRUE(val_2 == val_2); + EXPECT_TRUE(val_2 != val_1); + EXPECT_FALSE(val_1 == val_2); +} + +TEST_F(JniTest, LocalObject_ComparesAgainstjobjects) { + static constexpr Class kClass{"kClass1"}; + LocalObject val_1{Fake()}; + + EXPECT_TRUE(val_1 == AsNewLocalReference(Fake())); + EXPECT_TRUE(AsNewLocalReference(Fake()) == val_1); + + EXPECT_FALSE(val_1 != AsNewLocalReference(Fake())); + EXPECT_FALSE(AsNewLocalReference(Fake()) != val_1); +} + +struct A { + LocalObject val_1; + LocalObject val_2; + + A(LocalObject&& val_1, LocalObject&& val_2) + : val_1(std::move(val_1)), val_2(std::move(val_2)) {} + + A(A&& rhs) : val_1(std::move(rhs.val_1)), val_2(std::move(rhs.val_2)) {} +}; + +constexpr bool operator==(const A& lhs, const A& rhs) { + return lhs.val_1 == rhs.val_1 && lhs.val_2 == rhs.val_2; +} + +constexpr bool operator!=(const A& lhs, const A& rhs) { + return lhs.val_1 != rhs.val_1 || lhs.val_2 != rhs.val_2; +} + +TEST_F(JniTest, LocalObject_ComparesAgainstOtherLocalObjects_InContainers) { + A val_1{LocalObject{Fake(1)}, {Fake(2)}}; + A val_2{LocalObject{Fake(1)}, {Fake(3)}}; + + EXPECT_FALSE(val_1 == val_2); + EXPECT_TRUE(val_1 != val_2); + + EXPECT_EQ((std::array{A{LocalObject(Fake(1)), + LocalObject(Fake(2))}}), + (std::array{A{LocalObject(Fake(1)), + LocalObject(Fake(2))}})); + + EXPECT_TRUE((std::array{A{LocalObject(Fake(1)), + LocalObject(Fake(2))}} != + (std::array{A{LocalObject(Fake(1)), + LocalObject(Fake(3))}}))); +} + +TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnLvalue) { + static constexpr Class kClass2{ + "Class2", Method{"Foo", jni::Return{}, jni::Params{kClass}}}; + + LocalObject a{}; + LocalObject b{}; + b("Foo", a); +} + +TEST_F(JniTest, LocalObject_SupportsReturningAClass) { + static constexpr Class kClass{ + "Class1", Method{"Foo", jni::Return{kClass2}, jni::Params{}}}; + + LocalObject a{}; + a("Foo"); +} + +TEST_F(JniTest, LocalObject_SupportsReturningAString) { + static constexpr Class kClass{ + "Class1", Method{"Foo", jni::Return{}, jni::Params{}}}; + + LocalObject a{}; + a("Foo"); +} + +jobject ReturnOutputOfMethod() { + static constexpr Class kClass2{"Class2", Method{"Foo", jni::Return{kClass}}}; + + return LocalObject{}("Foo").Release(); +} + +TEST_F(JniTest, LocalObject_CompilesWhenReturnReleasing) { + ReturnOutputOfMethod(); +} + +TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnPrvalue) { + static constexpr Class kClass2{ + "Class2", Method{"Foo", jni::Return{}, jni::Params{kClass}}}; + + LocalObject a{}; + LocalObject b{}; + b("Foo", std::move(a)); +} + +TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnXvalue) { + static constexpr Class kClass2{ + "Class2", Method{"Foo", jni::Return{}, jni::Params{kClass}}}; + + LocalObject b{}; + b("Foo", LocalObject{}); +} + +TEST_F(JniTest, LocalObject_MovesInContainerStruct) { + struct A { + const LocalObject val; + + A(LocalObject&& in) : val(std::move(in)) {} + }; + + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + + A a{LocalObject{Fake()}}; +} + +TEST_F(JniTest, LocalObject_DoesntCrossTalkOverClassMethodIds) { + static constexpr Class kClass{ + "kClass", Method{"Foo", jni::Return{}, jni::Params{}}}; + + static constexpr Class kClass2{ + "kClass2", Method{"Foo", jni::Return{}, jni::Params{}}}; + + EXPECT_CALL(*env_, GetMethodID(_, _, StrEq("(I)V"))).Times(2); + + LocalObject obj_1{Fake(1)}; + LocalObject obj_2{Fake(2)}; + + // These are different method IDs (they are different classes). + obj_1("Foo", 1); + obj_2("Foo", 1); +} + +} // namespace diff --git a/implementation/legacy/method_ref_test.cc b/implementation/legacy/method_ref_test.cc new file mode 100644 index 00000000..fc9248bc --- /dev/null +++ b/implementation/legacy/method_ref_test.cc @@ -0,0 +1,301 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptGlobal; +using ::jni::Array; +using ::jni::Class; +using ::jni::Fake; +using ::jni::GlobalObject; +using ::jni::Id; +using ::jni::IdType; +using ::jni::JniT; +using ::jni::kDefaultClassLoader; +using ::jni::kNoIdx; +using ::jni::LocalArray; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::OverloadRef; +using ::jni::Params; +using ::jni::Rank; +using ::jni::Return; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::StrEq; + +template +using MethodRefT_t = OverloadRef, + IdType::OVERLOAD, I, 0, kNoIdx, 0>, + IdType::OVERLOAD_PARAM>; + +TEST_F(JniTest, MethodRef_DoesntStaticCrossTalkWithTagUse) { + static constexpr Method m{"FooV", Return{}, Params{jint{}}}; + static constexpr Class kSomeClass{"someClass", m}; + + MethodRefT_t::Invoke( + Fake(), Fake(), 123); +} + +TEST_F(JniTest, MethodRef_CallsGetMethodCorrectlyForSingleMethod) { + static constexpr Method m1{"FooV", Return{}}; + static constexpr Class c{"SimpleClass", m1}; + + InSequence seq; + EXPECT_CALL(*env_, + GetMethodID(Eq(Fake()), StrEq("FooV"), StrEq("()V"))) + .WillOnce(testing::Return(Fake())); + EXPECT_CALL(*env_, CallVoidMethodV(Fake(), Fake(), _)); + + MethodRefT_t::Invoke(Fake(), + Fake()); +} + +TEST_F(JniTest, MethodRef_ReturnWithObject) { + static constexpr Class c2{"someClass2"}; + static constexpr Method m1{"FooV", Return{c2}}; + static constexpr Class c{"someClass", m1}; + + InSequence seq; + EXPECT_CALL(*env_, GetMethodID(Eq(Fake()), StrEq("FooV"), + StrEq("()LsomeClass2;"))) + .WillOnce(testing::Return(Fake())); + EXPECT_CALL(*env_, CallObjectMethodV(Fake(), Fake(), _)); + + MethodRefT_t::Invoke(Fake(), + Fake()); +} + +TEST_F(JniTest, MethodRef_ReturnWithRank1Object) { + static constexpr Class c2{"someClass2"}; + static constexpr Method m1{"FooV", Return{Array{c2}}}; + static constexpr Class c{"someClass", m1}; + + InSequence seq; + EXPECT_CALL(*env_, GetMethodID(Eq(Fake()), StrEq("FooV"), + StrEq("()[LsomeClass2;"))) + .WillOnce(testing::Return(Fake())); + EXPECT_CALL(*env_, CallObjectMethodV(Fake(), Fake(), _)); + + MethodRefT_t::Invoke(Fake(), + Fake()); +} + +TEST_F(JniTest, MethodRef_ReturnWithRank2Object) { + static constexpr Class c2{"someClass2"}; + static constexpr Method m1{"FooV", Return{Array{c2, Rank<2>{}}}, Params<>{}}; + static constexpr Class c{"someClass", m1}; + + InSequence seq; + EXPECT_CALL(*env_, GetMethodID(Eq(Fake()), StrEq("FooV"), + StrEq("()[[LsomeClass2;"))) + .WillOnce(testing::Return(Fake())); + EXPECT_CALL(*env_, CallObjectMethodV(Fake(), Fake(), _)); + + MethodRefT_t::Invoke(Fake(), + Fake()); +} + +TEST_F(JniTest, MethodRef_ReturnWithNoParams) { + static constexpr Method m1{"FooV", Return{}}; + static constexpr Method m2{"BarI", Return{}}; + static constexpr Method m3{"BazF", Return{}}; + static constexpr Class c{"someClass", m1, m2, m3}; + + InSequence seq; + EXPECT_CALL(*env_, + GetMethodID(Eq(Fake()), StrEq("FooV"), StrEq("()V"))) + .WillOnce(testing::Return(Fake(1))); + EXPECT_CALL(*env_, CallVoidMethodV(Fake(), Fake(1), _)); + + EXPECT_CALL(*env_, + GetMethodID(Eq(Fake()), StrEq("BarI"), StrEq("()I"))) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, CallIntMethodV(Fake(), Fake(2), _)); + + EXPECT_CALL(*env_, + GetMethodID(Eq(Fake()), StrEq("BazF"), StrEq("()F"))) + .WillOnce(testing::Return(Fake(3))); + EXPECT_CALL(*env_, CallFloatMethodV(Fake(), Fake(3), _)); + + MethodRefT_t::Invoke(Fake(), + Fake()); + MethodRefT_t::Invoke(Fake(), + Fake()); + MethodRefT_t::Invoke(Fake(), + Fake()); +} + +TEST_F(JniTest, MethodRef_SingleParam) { + constexpr Method m1{"SomeFunc1", Return{}, Params{}}; + constexpr Method m2{"SomeFunc2", Return{}, Params{}}; + constexpr Method m3{"SomeFunc3", Return{}, Params{}}; + static constexpr Class c{"someClass", m1, m2, m3}; + + InSequence seq; + EXPECT_CALL( + *env_, GetMethodID(Eq(Fake()), StrEq("SomeFunc1"), StrEq("(I)V"))) + .WillOnce(testing::Return(Fake(1))); + // There is no clear way to test variable vaargs type arguments using Gmock, + // but at least we can test the correct method is called. + EXPECT_CALL(*env_, CallVoidMethodV(Fake(), Fake(1), _)); + + EXPECT_CALL( + *env_, GetMethodID(Eq(Fake()), StrEq("SomeFunc2"), StrEq("(F)I"))) + .WillOnce(testing::Return(Fake(2))); + EXPECT_CALL(*env_, CallIntMethodV(Fake(), Fake(2), _)); + + EXPECT_CALL( + *env_, GetMethodID(Eq(Fake()), StrEq("SomeFunc3"), StrEq("(F)F"))) + .WillOnce(testing::Return(Fake(3))); + EXPECT_CALL(*env_, CallFloatMethodV(Fake(), Fake(3), _)); + + MethodRefT_t::Invoke(Fake(), + Fake(), 1); + MethodRefT_t::Invoke(Fake(), + Fake(), 1.234f); + MethodRefT_t::Invoke(Fake(), + Fake(), 5.6789f); +} + +TEST_F(JniTest, MethodRef_ReturnsObjects) { + static constexpr Class c1{"Bazz"}; + static constexpr Class kClass{ + "com/google/ReturnsObjects", + Method{"Foo", Return{c1}, Params{}}, + }; + + // Note, class refs are not released, so Times() != 2. + EXPECT_CALL(*env_, NewObjectV).WillOnce(testing::Return(Fake())); + + GlobalObject global_object{}; + LocalObject new_obj{global_object("Foo", 5)}; +} + +TEST_F(JniTest, MethodRef_PassesObjects) { + static constexpr Class c1{"com/google/Bazz"}; + static constexpr Class kClass{ + "com/google/PassesObjects", + Method{"Foo", Return{}, Params{c1}}, + }; + + LocalObject local_object{Fake()}; + GlobalObject global_object{AdoptGlobal{}, Fake(100)}; + + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), StrEq("(Lcom/google/Bazz;)I"))); + + global_object("Foo", local_object); +} + +TEST_F(JniTest, MethodRef_PassesAndReturnsMultipleObjects) { + static constexpr Class c1{"Class1"}; + static constexpr Class c2{"Class2"}; + static constexpr Class c3{"Class3"}; + static constexpr Class c4{"Class4"}; + + static constexpr Class class_under_test{ + "com/google/PassesAndReturnsMultipleObjects", + Method{"Foo", Return{c1}, Params{c1, c2, c3, c4}}, + }; + + LocalObject obj1{Fake(1)}; + LocalObject obj2{Fake(2)}; + LocalObject obj3{Fake(3)}; + LocalObject obj4{Fake(4)}; + LocalObject object_under_test{Fake(5)}; + + LocalObject obj5{object_under_test("Foo", obj1, obj2, obj3, obj4)}; +} + +TEST_F(JniTest, MethodRef_SupportsForwardDefines) { + static constexpr Class kClass1{ + "kClass1", + Method{"m1", Return{}, Params{Class{"kClass1"}}}, + Method{"m2", Return{}, Params{Class{"kClass2"}}}, + Method{"m3", Return{Class{"kClass1"}}}, + Method{"m4", Return{Class{"kClass2"}}}, + }; + + static constexpr Class kClass2{ + "kClass2", + Method{"m1", Return{}, Params{Class{"kClass1"}}}, + Method{"m2", Return{}, Params{Class{"kClass2"}}}, + Method{"m3", Return{Class{"kClass1"}}}, + Method{"m4", Return{Class{"kClass2"}}}, + }; + + LocalObject c1_obj1{Fake(1)}; + LocalObject c1_obj2{Fake(2)}; + + LocalObject c2_obj1{Fake(3)}; + LocalObject c2_obj2{Fake(4)}; + + c1_obj1("m1", c1_obj1); + c1_obj1("m2", c2_obj1); + c1_obj1("m1", c1_obj1("m3")); + c1_obj1("m2", c1_obj1("m4")); + + c2_obj1("m1", c1_obj1); + c2_obj1("m2", c2_obj2); + c2_obj1("m2", std::move(std::move(c2_obj2))); + + c1_obj1("m2", std::move(c2_obj1)); + + // c2_obj1("m1", c1_obj1); // illegal! triggers warnings (post move read). + // c2_obj1("m2", c2_obj2); // illegal! triggers warnings (post move read). + // c2_obj1("m2", std::move(c2_obj2)); // illegal! triggers warnings (post + // move read). +} + +TEST_F(JniTest, MethodRef_SupportsStrings) { + static constexpr Class class_under_test{ + "com/google/SupportsStrings", + Method{"Foo", Return{}, Params{}}, + Method{"Bar", Return{}, Params{}}, + Method{"Baz", Return{}}, + }; + + LocalObject obj1{Fake()}; + obj1("Foo", "This is a method."); + obj1("Bar", "This is a method.", "It takes strings"); + obj1("Baz"); +} + +TEST_F(JniTest, MethodRef_SupportsArrays) { + static constexpr Class kClass{"kClass"}; + static constexpr Class class_under_test{ + "com/google/SupportsArrays", + Method{"Foo", Return{}, Params{Array{kClass}}}, + Method{"Bar", Return{}, Params{}}}; + + LocalArray local_array{nullptr}; + LocalObject obj1{Fake()}; + obj1("Foo", local_array); +} + +} // namespace diff --git a/implementation/legacy/multi_type_test.cc b/implementation/legacy/multi_type_test.cc new file mode 100644 index 00000000..70c323de --- /dev/null +++ b/implementation/legacy/multi_type_test.cc @@ -0,0 +1,114 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::Method; +using ::jni::Params; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Eq; +using ::testing::Return; +using ::testing::StrEq; + +TEST_F(JniTest, MultiTypeTest_SimpleSmokeTestForSingleObject) { + static constexpr Class object{ + "ARCore", + Method{"Foo", jni::Return{}, Params{}}, + Method{"Bar", jni::Return{jint{}}}, + Method{"Baz", jni::Return{}, Params{}}, + Field{"SomeField", jint{}}, + }; + + EXPECT_CALL(*env_, FindClass(StrEq("ARCore"))) + .WillOnce(Return(Fake(1))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))); + EXPECT_CALL(*env_, NewGlobalRef(Eq(Fake(1)))) + .WillOnce(Return(Fake(2))); + EXPECT_CALL(*env_, + GetMethodID(Fake(2), StrEq(""), StrEq("()V"))) + .WillOnce(Return(Fake())); + + EXPECT_CALL(*env_, NewObjectV(Fake(2), Fake(), _)) + .WillOnce(Return(Fake(1))); + EXPECT_CALL(*env_, NewGlobalRef(Fake(1))) + .WillOnce(Return(Fake(2))); + EXPECT_CALL(*env_, DeleteLocalRef(Fake(1))).Times(1); + + EXPECT_CALL(*env_, GetMethodID(Fake(2), StrEq("Foo"), StrEq("(IF)I"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetMethodID(Fake(2), StrEq("Bar"), StrEq("()I"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetMethodID(Fake(2), StrEq("Baz"), StrEq("(F)V"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, + GetFieldID(Fake(2), StrEq("SomeField"), StrEq("I"))) + .WillOnce(Return(Fake())); + + EXPECT_CALL(*env_, DeleteGlobalRef(Fake(2))).Times(1); + EXPECT_CALL(*env_, DeleteGlobalRef(Fake(2))).Times(1); + + jni::GlobalObject obj{}; + obj("Foo", 1, 2.f); + obj("Baz", 1.f); + obj("Baz", 1.f); + obj("Baz", 2.f); + obj("Baz", 3.f); + obj("Bar"); + obj["SomeField"].Get(); +} + +TEST_F(JniTest, MultiTypeTest_MethodsOfSameNameButDifferentClassAreUnique) { + EXPECT_CALL(*env_, GetMethodID).Times(2); + + static constexpr Class c1{ + "com/google/ARCore", + Method{"Foo", jni::Return{}, Params{}}, + }; + static constexpr Class c2{ + "com/google/VRCore", + Method{"Foo", jni::Return{}, Params{}}, + }; + + jni::LocalObject obj1{Fake(1)}; + jni::LocalObject obj2{Fake(2)}; + obj1("Foo", 12345); + obj2("Foo", 12345); + + // All of these calls ought not query for a method ID again. + obj1("Foo", 12345); + obj1("Foo", 12345); + obj1("Foo", 12345); + obj1("Foo", 12345); + obj2("Foo", 12345); + obj2("Foo", 12345); + obj2("Foo", 12345); + + jni::LocalObject obj3{Fake(3)}; + obj3("Foo", 12345); + obj3("Foo", 12345); +} + +} // namespace diff --git a/implementation/legacy/overload_ref_test.cc b/implementation/legacy/overload_ref_test.cc new file mode 100644 index 00000000..afd62155 --- /dev/null +++ b/implementation/legacy/overload_ref_test.cc @@ -0,0 +1,126 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::Class; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::Overload; +using ::jni::Params; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::StrEq; + +TEST_F(JniTest, MethodRef_AsksForCorrectMethods1) { + static constexpr Class kClass{ + "com/google/SupportsStrings", + Method{"Foo", jni::Return{}, Params{}}, + }; + LocalObject obj{}; + + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;)V"))); + + obj("Foo", "test"); +} + +TEST_F(JniTest, MethodRef_AsksForCorrectMethods2) { + static constexpr Class kClass{ + "com/google/SupportsStrings", + Method{"Foo", Overload{jni::Return{}, Params{jint{}}}, + Overload{jni::Return{}, Params{jstring{}}}}}; + LocalObject obj{}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(I)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;)V"))); + + obj("Foo", 1); + // obj("Foo", 2.f); doesn't compile (good). + obj("Foo", "test"); +} + +TEST_F(JniTest, MethodRef_AsksForCorrectMethods3) { + static constexpr Class kClass{ + "com/google/SupportsStrings", + Method{ + "Foo", + Overload{jni::Return{}, Params{jint{}}}, + Overload{jni::Return{}, Params{jstring{}}}, + Overload{jni::Return{}, Params{jstring{}, jstring{}}}, + }}; + LocalObject obj{}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(I)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), + StrEq("(Ljava/lang/String;Ljava/lang/String;)V"))); + + obj("Foo", 1); + // obj("Foo", 2.f); // doesn't compile (good). + obj("Foo", "test"); + obj("Foo", "test1", "test2"); + obj("Foo", "this_doesnt", "trigger_method_lookup"); +} + +TEST_F(JniTest, MethodRef_AsksForCorrectMethodsWhenMultiplePresent4) { + static constexpr Class kClass{ + "com/google/SupportsStrings", + Method{ + "Foo", + Overload{jni::Return{}, Params{jint{}}}, + Overload{jni::Return{}, Params{jstring{}}}, + Overload{jni::Return{}, Params{jstring{}, jstring{}}}, + }, + Method{ + "Baz", + Overload{jni::Return{}, Params{jint{}}}, + Overload{jni::Return{}, Params{jstring{}, jstring{}}}, + Overload{jni::Return{}, Params{jfloat{}, jfloat{}}}, + }}; + LocalObject obj{}; + + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(I)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Foo"), + StrEq("(Ljava/lang/String;Ljava/lang/String;)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Baz"), StrEq("(I)V"))); + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("Baz"), + StrEq("(Ljava/lang/String;Ljava/lang/String;)V"))); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("Baz"), StrEq("(FF)V"))); + + obj("Foo", 1); + // obj("Foo", 2.f); // doesn't compile (good). + obj("Foo", "test"); + obj("Foo", "test1", "test2"); + obj("Foo", "this_doesnt", "trigger_method_lookup"); + obj("Baz", 1); + obj("Baz", "test3", "test4"); + obj("Baz", 1234.f, 5678.f); +} + +} // namespace diff --git a/implementation/legacy/self_test.cc b/implementation/legacy/self_test.cc new file mode 100644 index 00000000..7deee5d6 --- /dev/null +++ b/implementation/legacy/self_test.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::Class; +using ::jni::Fake; +using ::jni::LocalObject; +using ::jni::Method; +using ::jni::Params; +using ::jni::Return; +using ::jni::Self; +using ::jni::test::AsGlobal; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::StrEq; + +static constexpr Class kClass{"Builder", Method{"build", Return{Self{}}}, + Method{"takesBuilder", Return{}, Params{Self{}}}}; + +TEST_F(JniTest, SelfCanBeUsedAsAReturnValue) { + EXPECT_CALL(*env_, FindClass(StrEq("Builder"))) + .WillOnce(::testing::Return(Fake())); + EXPECT_CALL(*env_, GetMethodID(AsGlobal(Fake()), StrEq("build"), + StrEq("()LBuilder;"))); + + LocalObject obj{Fake()}; + obj("build"); +} + +TEST_F(JniTest, SelfCanBeUsedAsAReturnValueAndMaintainsRichDecoration) { + EXPECT_CALL(*env_, GetMethodID).Times(AnyNumber()); + EXPECT_CALL(*env_, GetMethodID(_, StrEq("build"), StrEq("()LBuilder;"))); + + LocalObject obj{Fake()}; + obj("build")("build")("build"); +} + +TEST_F(JniTest, SelfCanBeUsedAsParam) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq("takesBuilder"), StrEq("(LBuilder;)V"))); + + LocalObject obj_1{Fake(1)}; + LocalObject obj_2{Fake(2)}; + + obj_1("takesBuilder", obj_2); +} + +} // namespace diff --git a/implementation/legacy/static_ref_test.cc b/implementation/legacy/static_ref_test.cc new file mode 100644 index 00000000..b94c5a4a --- /dev/null +++ b/implementation/legacy/static_ref_test.cc @@ -0,0 +1,373 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptLocal; +using ::jni::Array; +using ::jni::Class; +using ::jni::Fake; +using ::jni::Field; +using ::jni::LocalObject; +using ::jni::LocalString; +using ::jni::Method; +using ::jni::Params; +using ::jni::Rank; +using ::jni::Self; +using ::jni::Static; +using ::jni::StaticRef; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; + +//////////////////////////////////////////////////////////////////////////////// +// Fields. +//////////////////////////////////////////////////////////////////////////////// + +static constexpr Class kClass2{"kClass2"}; + +// clang-format off +static constexpr Class kClass{ + "kClass", + Static { + Field{"booleanField", jboolean{}}, + Field{"byteField", jbyte{}}, + Field{"charField", jchar{}}, + Field{"shortField", jshort{}}, + Field{"intField", jint{}}, + Field{"longField", jlong{}}, + Field{"floatField", jfloat{}}, + Field{"doubleField", jdouble{}}, + Field{"stringField", jstring{}}, + Field{"classField", Class{"kClass2"}}, + Field{"selfField", Self{}} + }, +}; +// clang-format on + +TEST_F(JniTest, StaticBooleanField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("booleanField"), StrEq("Z"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticBooleanField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticBooleanField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["booleanField"].Get()); + StaticRef{}["booleanField"].Set(true); +} + +TEST_F(JniTest, StaticByteField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("byteField"), StrEq("B"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticByteField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticByteField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["byteField"].Get()); + StaticRef{}["byteField"].Set(true); +} + +TEST_F(JniTest, StaticCharField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("charField"), StrEq("C"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticCharField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticCharField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["charField"].Get()); + StaticRef{}["charField"].Set(true); +} + +TEST_F(JniTest, StaticShortField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("shortField"), StrEq("S"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticShortField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticShortField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["shortField"].Get()); + StaticRef{}["shortField"].Set(true); +} + +TEST_F(JniTest, StaticIntField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("intField"), StrEq("I"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticIntField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticIntField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["intField"].Get()); + StaticRef{}["intField"].Set(true); +} + +TEST_F(JniTest, StaticLongField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("longField"), StrEq("J"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticLongField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticLongField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["longField"].Get()); + StaticRef{}["longField"].Set(true); +} + +TEST_F(JniTest, StaticFloatField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("floatField"), StrEq("F"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticFloatField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticFloatField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["floatField"].Get()); + StaticRef{}["floatField"].Set(true); +} + +TEST_F(JniTest, StaticDoubleField) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("doubleField"), StrEq("D"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticDoubleField(_, Fake())) + .WillOnce(Return(true)); + EXPECT_CALL(*env_, SetStaticDoubleField(_, Fake(), true)); + + LocalObject obj; + EXPECT_TRUE(StaticRef{}["doubleField"].Get()); + StaticRef{}["doubleField"].Set(true); +} + +TEST_F(JniTest, StaticField_ObjectGet) { + EXPECT_CALL(*env_, + GetStaticFieldID(_, StrEq("classField"), StrEq("LkClass2;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticObjectField(_, Fake())) + .WillOnce(Return(Fake())); + + jni::LocalObject obj = StaticRef{}["classField"].Get(); +} + +TEST_F(JniTest, StaticField_StringSet) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("stringField"), + StrEq("Ljava/lang/String;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, + SetStaticObjectField(_, Fake(), Fake())); + + StaticRef{}["stringField"].Set( + LocalString{AdoptLocal{}, Fake()}); +} + +TEST_F(JniTest, StaticField_ObjectSet) { + EXPECT_CALL(*env_, + GetStaticFieldID(_, StrEq("classField"), StrEq("LkClass2;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, + SetStaticObjectField(_, Fake(), Fake())); + + StaticRef{}["classField"].Set( + LocalObject{AdoptLocal{}, Fake()}); +} + +TEST_F(JniTest, StaticField_SelfGet) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("selfField"), StrEq("LkClass;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, GetStaticObjectField(_, Fake())) + .WillOnce(Return(Fake())); + + jni::LocalObject obj = StaticRef{}["selfField"].Get(); +} + +TEST_F(JniTest, StaticField_SelfSet) { + EXPECT_CALL(*env_, GetStaticFieldID(_, StrEq("selfField"), StrEq("LkClass;"))) + .WillOnce(Return(Fake())); + EXPECT_CALL(*env_, + SetStaticObjectField(_, Fake(), Fake())); + + StaticRef{}["selfField"].Set( + LocalObject{AdoptLocal{}, Fake()}); +} + +//////////////////////////////////////////////////////////////////////////////// +// Static Methods. +//////////////////////////////////////////////////////////////////////////////// + +// clang-format off +static constexpr Class kMethodClass{ + "kMethodClass", + Static { + Method{"booleanMethod", ::jni::Return{jboolean{}}}, + Method{"byteMethod", ::jni::Return{jbyte{}}}, + Method{"charMethod", ::jni::Return{jchar{}}}, + Method{"shortMethod", ::jni::Return{jshort{}}}, + Method{"intMethod", ::jni::Return{jint{}}}, + Method{"longMethod", ::jni::Return{jlong{}}}, + Method{"floatMethod", ::jni::Return{jfloat{}}}, + Method{"doubleMethod", ::jni::Return{jdouble{}}}, + Method{"stringMethod", ::jni::Return{jstring{}}}, + Method{"objectMethod", ::jni::Return{Class{"kClass2"}}}, + Method{"rank1ArrayMethod", ::jni::Return{Array{Class{"kClass2"}}}}, + Method{"rank2ArrayMethod", ::jni::Return{Array{Class{"kClass2"}, Rank<2>{}}}}, + Method{"selfMethod", ::jni::Return{Self{}}}, + + Method{"simpleFunc", ::jni::Return{int{}}, Params{jfloat{}}}, + Method{"complexFunc", ::jni::Return{float{}}, + Params{ + Array{Class{"kClass2"}, Rank<2>{}}, int{}, float{}, Class{"kClass3"}, + } + } + }, +}; +// clang-format on + +TEST_F(JniTest, StaticExerciseAllReturns) { + EXPECT_CALL(*env_, + GetStaticMethodID(_, StrEq("booleanMethod"), StrEq("()Z"))); + EXPECT_CALL(*env_, CallStaticBooleanMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("byteMethod"), StrEq("()B"))); + EXPECT_CALL(*env_, CallStaticByteMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("charMethod"), StrEq("()C"))); + EXPECT_CALL(*env_, CallStaticCharMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("shortMethod"), StrEq("()S"))); + EXPECT_CALL(*env_, CallStaticShortMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("intMethod"), StrEq("()I"))); + EXPECT_CALL(*env_, CallStaticIntMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("longMethod"), StrEq("()J"))); + EXPECT_CALL(*env_, CallStaticLongMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("floatMethod"), StrEq("()F"))); + EXPECT_CALL(*env_, CallStaticFloatMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("doubleMethod"), StrEq("()D"))); + EXPECT_CALL(*env_, CallStaticDoubleMethodV); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("stringMethod"), + StrEq("()Ljava/lang/String;"))); + + EXPECT_CALL( + *env_, GetStaticMethodID(_, StrEq("objectMethod"), StrEq("()LkClass2;"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("rank1ArrayMethod"), + StrEq("()[LkClass2;"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("rank2ArrayMethod"), + StrEq("()[[LkClass2;"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("selfMethod"), + StrEq("()LkMethodClass;"))); + + EXPECT_CALL(*env_, CallStaticObjectMethodV).Times(5); + + StaticRef{}("booleanMethod"); + StaticRef{}("byteMethod"); + StaticRef{}("charMethod"); + StaticRef{}("shortMethod"); + StaticRef{}("intMethod"); + StaticRef{}("longMethod"); + StaticRef{}("floatMethod"); + StaticRef{}("doubleMethod"); + StaticRef{}("stringMethod"); + + StaticRef{}("objectMethod"); + StaticRef{}("rank1ArrayMethod"); + StaticRef{}("rank2ArrayMethod"); + + LocalObject self_ret = StaticRef{}("selfMethod"); +} + +// clang-format off +static constexpr Class kMethodClassSingleParam{ + "kMethodClassSingleParam", + Static { + Method{"booleanMethod", ::jni::Return{}, Params{}}, + Method{"byteMethod", ::jni::Return{}, Params{}}, + Method{"charMethod", ::jni::Return{}, Params{}}, + Method{"shortMethod", ::jni::Return{}, Params{}}, + Method{"intMethod", ::jni::Return{}, Params{}}, + Method{"longMethod", ::jni::Return{}, Params{}}, + Method{"floatMethod", ::jni::Return{}, Params{}}, + Method{"doubleMethod", ::jni::Return{}, Params{}}, + Method{"stringMethod", ::jni::Return{}, Params{}}, + Method{"objectMethod", ::jni::Return{}, Params{Class{"kClass2"}}}, + Method{"rank1ArrayMethod", ::jni::Return{}, Params{Array{Class{"kClass2"}}}}, + Method{"rank2ArrayMethod", ::jni::Return{}, Params{Array{Class{"kClass2"}, Rank<2>{}}}}, + Method{"selfMethod", ::jni::Return{}, Params{Self{}}} + }, +}; +// clang-format on + +TEST_F(JniTest, StaticExerciseAllTypesThroughSingleParam) { + EXPECT_CALL(*env_, + GetStaticMethodID(_, StrEq("booleanMethod"), StrEq("(Z)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("byteMethod"), StrEq("(B)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("charMethod"), StrEq("(C)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("shortMethod"), StrEq("(S)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("intMethod"), StrEq("(I)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("longMethod"), StrEq("(J)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("floatMethod"), StrEq("(F)V"))); + EXPECT_CALL(*env_, + GetStaticMethodID(_, StrEq("doubleMethod"), StrEq("(D)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("stringMethod"), + StrEq("(Ljava/lang/String;)V"))); + + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("objectMethod"), + StrEq("(LkClass2;)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("rank1ArrayMethod"), + StrEq("([LkClass2;)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("rank2ArrayMethod"), + StrEq("([[LkClass2;)V"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("selfMethod"), + StrEq("(LkMethodClassSingleParam;)V"))); + + EXPECT_CALL(*env_, CallStaticVoidMethodV).Times(13); + + StaticRef{}("booleanMethod", jboolean{true}); + StaticRef{}("byteMethod", jbyte{1}); + StaticRef{}("charMethod", jchar{'a'}); + StaticRef{}("shortMethod", jshort{1}); + StaticRef{}("intMethod", jint{123}); + StaticRef{}("longMethod", jlong{456}); + StaticRef{}("floatMethod", jfloat{789.f}); + StaticRef{}("doubleMethod", jdouble{101.}); + StaticRef{}("stringMethod", "test"); + + // It would be more complete to exercise all types here. + StaticRef{}("objectMethod", Fake()); + StaticRef{}("rank1ArrayMethod", + Fake()); + StaticRef{}("rank2ArrayMethod", + Fake()); + StaticRef{}("selfMethod", Fake()); +} + +TEST_F(JniTest, StaticExerciseComplexSetOfParams) { + // The primary difference for statics are how they handle their returns. + // Coverage is already fairly extensive for params. + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("simpleFunc"), StrEq("(F)I"))); + EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("complexFunc"), + StrEq("([[LkClass2;IFLkClass3;)F"))); + + StaticRef{}("simpleFunc", 123.f); + StaticRef{}("complexFunc", jobjectArray{nullptr}, 123, 456.f, + jobject{nullptr}); +} + +} // namespace diff --git a/implementation/legacy/string_ref_test.cc b/implementation/legacy/string_ref_test.cc new file mode 100644 index 00000000..17bfd6d3 --- /dev/null +++ b/implementation/legacy/string_ref_test.cc @@ -0,0 +1,238 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include "implementation/jni_helper/fake_test_constants.h" +#include "jni_bind.h" +#include "jni_test.h" + +namespace { + +using ::jni::AdoptGlobal; +using ::jni::Fake; +using ::jni::GlobalObject; +using ::jni::GlobalString; +using ::jni::kJavaLangString; +using ::jni::LocalObject; +using ::jni::LocalString; +using ::jni::NewRef; +using ::jni::UtfStringView; +using ::jni::test::AsNewLocalReference; +using ::jni::test::JniTest; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; + +const char* char_ptr = "TestString"; + +static constexpr jni::Class kClass{ + "Class", + jni::Method{"Foo", jni::Return{}, jni::Params{}}, + jni::Method{"TakesStrParam", jni::Return{}, jni::Params{}}, +}; + +//////////////////////////////////////////////////////////////////////////////// +// Local String Tests. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, LocalString_NullPtrT) { LocalString str{nullptr}; } + +TEST_F(JniTest, LocalString_IsImplicitlyConvertible) { + LocalString str{Fake()}; + EXPECT_EQ(static_cast(str), AsNewLocalReference(Fake())); +} + +TEST_F(JniTest, LocalString_NullWorks) { + EXPECT_CALL(*env_, DeleteLocalRef).Times(0); + LocalString str{jstring{nullptr}}; +} + +TEST_F(JniTest, LocalString_ConstructsFromObject) { + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + LocalObject undecorated_object{Fake()}; + LocalString decorated_object{std::move(undecorated_object)}; +} + +TEST_F(JniTest, LocalString_CopiesFromObject) { + EXPECT_CALL(*env_, DeleteLocalRef(AsNewLocalReference(Fake()))); + EXPECT_CALL(*env_, NewLocalRef(Fake())); + + LocalString decorated_object{NewRef{}, Fake()}; + + EXPECT_EQ(jstring{decorated_object}, AsNewLocalReference(Fake())); +} + +TEST_F(JniTest, LocalString_CopiesFromJString) { + EXPECT_CALL(*env_, DeleteLocalRef(Fake(2))); + EXPECT_CALL(*env_, NewLocalRef(Fake(1))) + .WillOnce(::testing::Return(Fake(2))); + + LocalString decorated_object{NewRef{}, Fake(1)}; + + EXPECT_EQ(jstring{decorated_object}, Fake(2)); +} + +TEST_F(JniTest, LocalString_ConstructsFromOutputOfMethod) { + LocalObject obj{}; + LocalString str{obj("Foo")}; +} + +TEST_F(JniTest, LocalString_ConstructsFromByteArray) { + EXPECT_CALL(*env_, GetMethodID(_, StrEq(""), StrEq("([B)V"))); + LocalString str{Fake()}; +} + +TEST_F(JniTest, LocalString_CreatesFromCharPtr) { + LocalString str{"TestString"}; +} + +TEST_F(JniTest, LocalString_CreatesFromStringView) { + EXPECT_CALL(*env_, NewStringUTF(StrEq("TestString"))) + .WillOnce(Return(Fake())); + + // jclass for temp String class reference. + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + // The variable str (which is itself an object). + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + // TODO(b/143908983): Currently strings leak one local during proxying. + // Temporary xref created during construction. + // EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + + LocalString str{std::string_view{char_ptr}}; +} + +TEST_F(JniTest, LocalString_CreatesFromString) { + EXPECT_CALL(*env_, NewStringUTF(StrEq("TestString"))) + .WillOnce(Return(Fake())); + + // jclass for temp String class reference. + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + // The variable str (which is itself an object). + EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + // TODO(b/143908983): Currently strings leak one local during proxying. + // Temporary xref created during construction. + // EXPECT_CALL(*env_, DeleteLocalRef(Fake())); + LocalString str{std::string{"TestString"}}; +} + +TEST_F(JniTest, LocalString_CreatesFromCharPtrForGlobals) { + GlobalString str{"TestString"}; +} + +TEST_F(JniTest, LocalString_PinsAndUnpinsMemoryForLocals) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq(""), StrEq("(Ljava/lang/String;)V"))); + EXPECT_CALL(*env_, NewStringUTF(StrEq("TestLocalString"))); + EXPECT_CALL(*env_, GetStringUTFChars(_, nullptr)).WillOnce(Return(char_ptr)); + EXPECT_CALL(*env_, ReleaseStringUTFChars(_, char_ptr)); + + LocalString str{"TestLocalString"}; + UtfStringView utf_string_view = str.Pin(); + EXPECT_EQ(utf_string_view.ToString().data(), char_ptr); +} + +TEST_F(JniTest, LocalString_AllowsLValueLocalString) { + LocalObject obj{}; + LocalString local_string{"abcde"}; + obj("TakesStrParam", local_string); +} + +TEST_F(JniTest, LocalString_AllowsRValueLocalString) { + LocalObject obj{}; + obj("TakesStrParam", LocalString{"abcde"}); +} + +//////////////////////////////////////////////////////////////////////////////// +// Global String Tests. +//////////////////////////////////////////////////////////////////////////////// +TEST_F(JniTest, GlobalString_NullWorks) { + GlobalString str{AdoptGlobal{}, jstring{nullptr}}; +} + +TEST_F(JniTest, GlobalString_ConstructsFromObject) { + EXPECT_CALL(*env_, DeleteGlobalRef).Times(1); + GlobalObject undecorated_object{AdoptGlobal{}, + Fake()}; + GlobalString decorated_object{std::move(undecorated_object)}; +} + +TEST_F(JniTest, GlobalString_GlobalsReleaseWithGlobalMechanism) { + EXPECT_CALL(*env_, DeleteGlobalRef); + GlobalString str{AdoptGlobal{}, Fake()}; +} + +TEST_F(JniTest, GlobalString_ConstructsFromOutputOfMethod) { + LocalObject obj{}; + GlobalString str{obj("Foo")}; +} + +TEST_F(JniTest, GlobalString_ConstructsFromByteArray) { + EXPECT_CALL(*env_, GetMethodID(_, StrEq(""), StrEq("([B)V"))); + GlobalString str{Fake()}; +} + +TEST_F(JniTest, GlobalString_CreatesFromCharPtr) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq(""), StrEq("(Ljava/lang/String;)V"))); + GlobalString str{"TestString"}; +} + +TEST_F(JniTest, GlobalString_CreatesFromStringView) { + EXPECT_CALL(*env_, NewStringUTF(StrEq("TestString"))) + .WillOnce(Return(Fake())); + GlobalString str{std::string_view{char_ptr}}; +} + +TEST_F(JniTest, GlobalString_CreatesFromString) { + EXPECT_CALL(*env_, NewStringUTF(StrEq("TestString"))) + .WillOnce(Return(Fake())); + GlobalString str{std::string{"TestString"}}; +} + +TEST_F(JniTest, GlobalString_CreatesFromCharPtrForGlobals) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq(""), StrEq("(Ljava/lang/String;)V"))); + GlobalString str{"TestString"}; +} + +TEST_F(JniTest, GlobalString_PinsAndUnpinsMemoryForLocals) { + EXPECT_CALL(*env_, + GetMethodID(_, StrEq(""), StrEq("(Ljava/lang/String;)V"))); + EXPECT_CALL(*env_, NewStringUTF(StrEq("TestGlobalString"))); + EXPECT_CALL(*env_, GetStringUTFChars(_, nullptr)).WillOnce(Return(char_ptr)); + EXPECT_CALL(*env_, ReleaseStringUTFChars(_, char_ptr)); + + GlobalString str{"TestGlobalString"}; + UtfStringView utf_string_view = str.Pin(); + EXPECT_EQ(utf_string_view.ToString().data(), char_ptr); +} + +TEST_F(JniTest, GlobalString_AllowsLValueGlobalString) { + LocalObject obj{}; + GlobalString global_string{"abcde"}; + obj("TakesStrParam", global_string); +} + +TEST_F(JniTest, GlobalString_AllowsRValueGlobalString) { + LocalObject obj{}; + obj("TakesStrParam", GlobalString{"abcde"}); +} + +} // namespace diff --git a/implementation/local_array_field_test.cc b/implementation/local_array_field_test.cc index e20f60a1..721c86bc 100644 --- a/implementation/local_array_field_test.cc +++ b/implementation/local_array_field_test.cc @@ -69,7 +69,7 @@ TEST_F(JniTest, ArrayField_Object_Iteration_Rank_1) { int i = 100; LocalObject obj{Fake()}; - for (LocalObject a : obj["ObjectArrayRank1"].Get().Pin()) { + for (LocalObject a : obj.Access<"ObjectArrayRank1">().Get().Pin()) { EXPECT_EQ(static_cast(a), Fake(i)); i++; } @@ -87,7 +87,7 @@ TEST_F(JniTest, ArrayField_Object_Iteration_Rank_2) { int i = 100; LocalObject obj{Fake(1)}; for (LocalArray a : - obj["ObjectArrayRank2"].Get().Pin()) { + obj.Access<"ObjectArrayRank2">().Get().Pin()) { EXPECT_EQ(static_cast(a), Fake(i)); i++; } @@ -124,26 +124,27 @@ TEST_F(JniTest, Array_FieldTests) { LocalObject obj{AdoptLocal{}, Fake()}; - EXPECT_EQ(static_cast(obj["BooleanArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"BooleanArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["ByteArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"ByteArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["CharArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"CharArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["ShortArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"ShortArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["IntArray"].Get()), Fake()); - EXPECT_EQ(static_cast(obj["FloatArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"IntArray">().Get()), + Fake()); + EXPECT_EQ(static_cast(obj.Access<"FloatArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["DoubleArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"DoubleArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["LongArray"].Get()), + EXPECT_EQ(static_cast(obj.Access<"LongArray">().Get()), Fake()); - EXPECT_EQ(static_cast(obj["ObjectArrayRank1"].Get()), + EXPECT_EQ(static_cast(obj.Access<"ObjectArrayRank1">().Get()), Fake(1)); - EXPECT_EQ(static_cast(obj["ObjectArrayRank2"].Get()), + EXPECT_EQ(static_cast(obj.Access<"ObjectArrayRank2">().Get()), Fake(2)); - EXPECT_EQ(static_cast(obj["ObjectArrayRank3"].Get()), + EXPECT_EQ(static_cast(obj.Access<"ObjectArrayRank3">().Get()), Fake(3)); } @@ -166,16 +167,18 @@ TEST_F(JniTest, Array_Field_Boolean_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["BooleanArray"].Get()}; + LocalArray arr{obj.Access<"BooleanArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["BooleanArray"].Set(Fake()); - obj["BooleanArray"].Set( + obj.Access<"BooleanArray">().Set(Fake()); + obj.Access<"BooleanArray">().Set( LocalArray{AdoptLocal{}, Fake()}); - obj["BooleanArray"].Set(arr2); - obj["BooleanArray"].Set(obj["BooleanArray"].Get()); + obj.Access<"BooleanArray">().Set(arr2); + obj.Access<"BooleanArray">().Set(obj.Access<"BooleanArray">().Get()); - EXPECT_EQ(obj["BooleanArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["BooleanArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"BooleanArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"BooleanArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Byte_Test) { @@ -197,14 +200,17 @@ TEST_F(JniTest, Array_Field_Byte_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["ByteArray"].Get()}; + LocalArray arr{obj.Access<"ByteArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["ByteArray"].Set(Fake()); - obj["ByteArray"].Set(LocalArray{AdoptLocal{}, Fake()}); - obj["ByteArray"].Set(arr2); - obj["ByteArray"].Set(obj["ByteArray"].Get()); - EXPECT_EQ(obj["ByteArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["ByteArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + obj.Access<"ByteArray">().Set(Fake()); + obj.Access<"ByteArray">().Set( + LocalArray{AdoptLocal{}, Fake()}); + obj.Access<"ByteArray">().Set(arr2); + obj.Access<"ByteArray">().Set(obj.Access<"ByteArray">().Get()); + EXPECT_EQ(obj.Access<"ByteArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"ByteArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Char_Test) { @@ -226,14 +232,17 @@ TEST_F(JniTest, Array_Field_Char_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["CharArray"].Get()}; + LocalArray arr{obj.Access<"CharArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["CharArray"].Set(Fake()); - obj["CharArray"].Set(LocalArray{AdoptLocal{}, Fake()}); - obj["CharArray"].Set(arr2); - obj["CharArray"].Set(obj["CharArray"].Get()); - EXPECT_EQ(obj["CharArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["CharArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + obj.Access<"CharArray">().Set(Fake()); + obj.Access<"CharArray">().Set( + LocalArray{AdoptLocal{}, Fake()}); + obj.Access<"CharArray">().Set(arr2); + obj.Access<"CharArray">().Set(obj.Access<"CharArray">().Get()); + EXPECT_EQ(obj.Access<"CharArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"CharArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Short_Test) { @@ -255,14 +264,17 @@ TEST_F(JniTest, Array_Field_Short_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["ShortArray"].Get()}; + LocalArray arr{obj.Access<"ShortArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["ShortArray"].Set(Fake()); - obj["ShortArray"].Set(LocalArray{AdoptLocal{}, Fake()}); - obj["ShortArray"].Set(arr2); - obj["ShortArray"].Set(obj["ShortArray"].Get()); - EXPECT_EQ(obj["ShortArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["ShortArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + obj.Access<"ShortArray">().Set(Fake()); + obj.Access<"ShortArray">().Set( + LocalArray{AdoptLocal{}, Fake()}); + obj.Access<"ShortArray">().Set(arr2); + obj.Access<"ShortArray">().Set(obj.Access<"ShortArray">().Get()); + EXPECT_EQ(obj.Access<"ShortArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"ShortArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Int_Test) { @@ -283,12 +295,13 @@ TEST_F(JniTest, Array_Field_Int_Test) { Fake(), fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["IntArray"].Get()}; + LocalArray arr{obj.Access<"IntArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["IntArray"].Set(Fake()); - obj["IntArray"].Set(LocalArray{AdoptLocal{}, Fake()}); - obj["IntArray"].Set(arr2); - obj["IntArray"].Set(obj["IntArray"].Get()); + obj.Access<"IntArray">().Set(Fake()); + obj.Access<"IntArray">().Set( + LocalArray{AdoptLocal{}, Fake()}); + obj.Access<"IntArray">().Set(arr2); + obj.Access<"IntArray">().Set(obj.Access<"IntArray">().Get()); EXPECT_EQ(obj["IntArray"].Get().Pin().ptr(), fake_storage_ptr.get()); EXPECT_EQ(obj["IntArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); } @@ -312,14 +325,17 @@ TEST_F(JniTest, Array_Field_Float_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["FloatArray"].Get()}; + LocalArray arr{obj.Access<"FloatArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["FloatArray"].Set(Fake()); - obj["FloatArray"].Set(LocalArray{AdoptLocal{}, Fake()}); - obj["FloatArray"].Set(arr2); - obj["FloatArray"].Set(obj["FloatArray"].Get()); - EXPECT_EQ(obj["FloatArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["FloatArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + obj.Access<"FloatArray">().Set(Fake()); + obj.Access<"FloatArray">().Set( + LocalArray{AdoptLocal{}, Fake()}); + obj.Access<"FloatArray">().Set(arr2); + obj.Access<"FloatArray">().Set(obj.Access<"FloatArray">().Get()); + EXPECT_EQ(obj.Access<"FloatArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"FloatArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Double_Test) { @@ -341,15 +357,17 @@ TEST_F(JniTest, Array_Field_Double_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["DoubleArray"].Get()}; + LocalArray arr{obj.Access<"DoubleArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["DoubleArray"].Set(Fake()); - obj["DoubleArray"].Set( + obj.Access<"DoubleArray">().Set(Fake()); + obj.Access<"DoubleArray">().Set( LocalArray{AdoptLocal{}, Fake()}); - obj["DoubleArray"].Set(arr2); - obj["DoubleArray"].Set(obj["DoubleArray"].Get()); - EXPECT_EQ(obj["DoubleArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["DoubleArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + obj.Access<"DoubleArray">().Set(arr2); + obj.Access<"DoubleArray">().Set(obj.Access<"DoubleArray">().Get()); + EXPECT_EQ(obj.Access<"DoubleArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"DoubleArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Long_Test) { @@ -371,14 +389,17 @@ TEST_F(JniTest, Array_Field_Long_Test) { fake_storage_ptr.get(), JNI_ABORT)); LocalObject obj{AdoptLocal{}, Fake()}; - LocalArray arr{obj["LongArray"].Get()}; + LocalArray arr{obj.Access<"LongArray">().Get()}; LocalArray arr2{AdoptLocal{}, Fake()}; - obj["LongArray"].Set(Fake()); - obj["LongArray"].Set(LocalArray{AdoptLocal{}, Fake()}); - obj["LongArray"].Set(arr2); - obj["LongArray"].Set(obj["LongArray"].Get()); - EXPECT_EQ(obj["LongArray"].Get().Pin().ptr(), fake_storage_ptr.get()); - EXPECT_EQ(obj["LongArray"].Get().Pin(false).ptr(), fake_storage_ptr.get()); + obj.Access<"LongArray">().Set(Fake()); + obj.Access<"LongArray">().Set( + LocalArray{AdoptLocal{}, Fake()}); + obj.Access<"LongArray">().Set(arr2); + obj.Access<"LongArray">().Set(obj.Access<"LongArray">().Get()); + EXPECT_EQ(obj.Access<"LongArray">().Get().Pin().ptr(), + fake_storage_ptr.get()); + EXPECT_EQ(obj.Access<"LongArray">().Get().Pin(false).ptr(), + fake_storage_ptr.get()); } TEST_F(JniTest, Array_Field_Object_Test) { @@ -394,13 +415,13 @@ TEST_F(JniTest, Array_Field_Object_Test) { LocalObject obj{AdoptLocal{}, Fake()}; LocalArray arr2{AdoptLocal{}, Fake()}; - LocalArray arr{obj["ObjectArrayRank1"].Get()}; - obj["ObjectArrayRank1"].Set(Fake()); - obj["ObjectArrayRank1"].Set( + LocalArray arr{obj.Access<"ObjectArrayRank1">().Get()}; + obj.Access<"ObjectArrayRank1">().Set(Fake()); + obj.Access<"ObjectArrayRank1">().Set( LocalArray{AdoptLocal{}, Fake()}); - obj["ObjectArrayRank1"].Set(arr2); - obj["ObjectArrayRank1"].Set(obj["ObjectArrayRank1"].Get()); - obj["ObjectArrayRank1"].Get().Get(2); + obj.Access<"ObjectArrayRank1">().Set(arr2); + obj.Access<"ObjectArrayRank1">().Set(obj.Access<"ObjectArrayRank1">().Get()); + obj.Access<"ObjectArrayRank1">().Get().Get(2); } TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_1) { @@ -414,7 +435,7 @@ TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_1) { EXPECT_CALL(*env_, GetFieldID(_, StrEq("Foo"), StrEq("[LkClass2;"))); LocalObject obj{Fake()}; - LocalArray{obj["Foo"].Get()}; + LocalArray{obj.Access<"Foo">().Get()}; } TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_2) { @@ -428,7 +449,7 @@ TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_2) { EXPECT_CALL(*env_, GetFieldID(_, StrEq("Foo"), StrEq("[[LkClass2;"))); LocalObject obj{Fake()}; - LocalArray arr_from_field{obj["Foo"].Get()}; + LocalArray arr_from_field{obj.Access<"Foo">().Get()}; } TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_2_Iteration) { @@ -471,7 +492,7 @@ TEST_F(JniTest, Array_Field_HandlesLValueLocalObject_Rank_2_Iteration) { LocalObject obj{AdoptLocal{}, Fake(1)}; int i = 100; - for (LocalArray obj : obj["Foo"].Get().Pin()) { + for (LocalArray obj : obj.Access<"Foo">().Get().Pin()) { EXPECT_EQ(static_cast(obj), Fake(i)); i++; } diff --git a/implementation/local_array_method_multidimensional_test.cc b/implementation/local_array_method_multidimensional_test.cc index f10f97dd..21671b6a 100644 --- a/implementation/local_array_method_multidimensional_test.cc +++ b/implementation/local_array_method_multidimensional_test.cc @@ -48,7 +48,7 @@ TEST_F(JniTest, V_2I) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("I"), StrEq("()[[I"))); LocalObject obj{Fake()}; - obj("I"); + obj.Call<"I">(); } TEST_F(JniTest, V_2LkClass) { @@ -58,7 +58,7 @@ TEST_F(JniTest, V_2LkClass) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("()[[LkClass2;"))); LocalObject obj{Fake()}; - obj("Foo"); + obj.Call<"Foo">(); } //////////////////////////////////////////////////////////////////////////////// @@ -77,7 +77,7 @@ TEST_F(JniTest, 3I1D_2LkClass) { GetMethodID(_, StrEq("Bar"), StrEq("([[[I[D)[[LkClass3;"))); LocalObject obj{Fake()}; - obj("Bar", Fake(), Fake()); + obj.Call<"Bar">(Fake(), Fake()); } TEST_F(JniTest, 2I_I) { @@ -88,8 +88,8 @@ TEST_F(JniTest, 2I_I) { LocalObject obj{Fake()}; // obj("I", jintArray{nullptr}); // doesn't compile (good). - obj("I", Fake()); - obj("I", LocalArray{Fake()}); + obj.Call<"I">(Fake()); + obj.Call<"I">(LocalArray{Fake()}); } TEST_F(JniTest, 3I_1LkClass) { @@ -103,8 +103,7 @@ TEST_F(JniTest, 3I_1LkClass) { // TODO(b/143908983): CTAD is failing. // LocalArray ret = obj("Baz", jobjectArray{nullptr}); - - LocalArray ret = obj("Baz", Fake()); + LocalArray ret = obj.Call<"Baz">(Fake()); static_assert(std::is_same_v>); } @@ -113,7 +112,7 @@ TEST_F(JniTest, 3I1D_1LkClass) { GetMethodID(_, StrEq("Bar"), StrEq("([[[I[D)[[LkClass3;"))); LocalObject obj{Fake()}; - obj("Bar", Fake(), Fake()); + obj.Call<"Bar">(Fake(), Fake()); } } // namespace diff --git a/implementation/local_array_method_test.cc b/implementation/local_array_method_test.cc index 24e27609..e9d87cef 100644 --- a/implementation/local_array_method_test.cc +++ b/implementation/local_array_method_test.cc @@ -78,31 +78,31 @@ TEST_F(JniTest, ReturnSmokeTest) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("ObjectArray"), StrEq("()[LkClass2;"))); - LocalArray bool_array{obj("BooleanArray")}; + LocalArray bool_array{obj.Call<"BooleanArray">()}; EXPECT_EQ((static_cast(bool_array)), (Fake())); - LocalArray byte_array{obj("ByteArray")}; + LocalArray byte_array{obj.Call<"ByteArray">()}; EXPECT_EQ((static_cast(byte_array)), (Fake())); - LocalArray char_array{obj("CharArray")}; + LocalArray char_array{obj.Call<"CharArray">()}; EXPECT_EQ((static_cast(char_array)), (Fake())); - LocalArray short_array{obj("ShortArray")}; + LocalArray short_array{obj.Call<"ShortArray">()}; EXPECT_EQ((static_cast(short_array)), (Fake())); - LocalArray int_array{obj("IntArray")}; + LocalArray int_array{obj.Call<"IntArray">()}; EXPECT_EQ((static_cast(int_array)), (Fake())); - LocalArray long_array{obj("LongArray")}; + LocalArray long_array{obj.Call<"LongArray">()}; EXPECT_EQ((static_cast(long_array)), (Fake())); - LocalArray float_array{obj("FloatArray")}; + LocalArray float_array{obj.Call<"FloatArray">()}; EXPECT_EQ((static_cast(float_array)), (Fake())); - LocalArray double_array{obj("DoubleArray")}; + LocalArray double_array{obj.Call<"DoubleArray">()}; EXPECT_EQ((static_cast(double_array)), (Fake())); - LocalArray object_array{obj("ObjectArray")}; + LocalArray object_array{obj.Call<"ObjectArray">()}; EXPECT_EQ((static_cast(object_array)), (Fake())); } @@ -135,15 +135,16 @@ TEST_F(JniTest, ParamsSmokeTest) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("ObjectArray"), StrEq("([LkClass2;)V"))); - obj("BooleanArray", LocalArray{Fake()}); - obj("ByteArray", LocalArray{Fake()}); - obj("CharArray", LocalArray{Fake()}); - obj("ShortArray", LocalArray{Fake()}); - obj("IntArray", LocalArray{Fake()}); - obj("FloatArray", LocalArray{Fake()}); - obj("DoubleArray", LocalArray{Fake()}); - obj("LongArray", LocalArray{Fake()}); - obj("ObjectArray", LocalArray{Fake()}); + obj.Call<"BooleanArray">(LocalArray{Fake()}); + obj.Call<"ByteArray">(LocalArray{Fake()}); + obj.Call<"CharArray">(LocalArray{Fake()}); + obj.Call<"ShortArray">(LocalArray{Fake()}); + obj.Call<"IntArray">(LocalArray{Fake()}); + obj.Call<"FloatArray">(LocalArray{Fake()}); + obj.Call<"DoubleArray">(LocalArray{Fake()}); + obj.Call<"LongArray">(LocalArray{Fake()}); + obj.Call<"ObjectArray">( + LocalArray{Fake()}); } //////////////////////////////////////////////////////////////////////////////// @@ -189,35 +190,35 @@ TEST_F(JniTest, ComplexSmokeTest) { StrEq("([LkClass2;)[LkClass2;"))); LocalArray bool_array{ - obj("BooleanArray", LocalArray{Fake()})}; + obj.Call<"BooleanArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(bool_array)), (Fake())); LocalArray byte_array{ - obj("ByteArray", LocalArray{Fake()})}; + obj.Call<"ByteArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(byte_array)), (Fake())); LocalArray char_array{ - obj("CharArray", LocalArray{Fake()})}; + obj.Call<"CharArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(char_array)), (Fake())); LocalArray short_array{ - obj("ShortArray", LocalArray{Fake()})}; + obj.Call<"ShortArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(short_array)), (Fake())); LocalArray int_array{ - obj("IntArray", LocalArray{Fake()})}; + obj.Call<"IntArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(int_array)), (Fake())); LocalArray float_array{ - obj("FloatArray", LocalArray{Fake()})}; + obj.Call<"FloatArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(float_array)), (Fake())); LocalArray double_array{ - obj("DoubleArray", LocalArray{Fake()})}; + obj.Call<"DoubleArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(double_array)), (Fake())); LocalArray long_array{ - obj("LongArray", LocalArray{Fake()})}; + obj.Call<"LongArray">(LocalArray{Fake()})}; EXPECT_EQ((static_cast(long_array)), (Fake())); LocalArray object_array{obj( diff --git a/implementation/local_array_string_test.cc b/implementation/local_array_string_test.cc index b0990e20..3bcf7d21 100644 --- a/implementation/local_array_string_test.cc +++ b/implementation/local_array_string_test.cc @@ -72,7 +72,7 @@ TEST_F(JniTest, Array_CorrectSignatureForStringParams) { LocalObject obj{jobject{nullptr}}; LocalArray arr{3}; - obj("StringArray", arr); + obj.Call<"StringArray">(arr); } //////////////////////////////////////////////////////////////////////////////// @@ -128,7 +128,7 @@ TEST_F(JniTest, Array_CorrectReturnSignatureForStrings) { LocalObject obj{Fake()}; EXPECT_CALL(*env_, GetMethodID(_, StrEq("StringArray"), StrEq("()[Ljava/lang/String;"))); - LocalArray arr = obj("StringArray"); + LocalArray arr = obj.Call<"StringArray">(); } //////////////////////////////////////////////////////////////////////////////// @@ -144,7 +144,7 @@ TEST_F(JniTest, Array_CorrectParamSignatureForStrings) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("StringArray"), StrEq("([Ljava/lang/String;)V"))); LocalArray arr{2}; - obj("StringArray", arr); + obj.Call<"StringArray">(arr); } TEST_F(JniTest, LocalStringArray_ConstructsObjectsForLValues) { @@ -196,9 +196,9 @@ TEST_F(JniTest, Array_CorrectFieldSignatureForStrings) { EXPECT_CALL(*env_, GetFieldID(_, StrEq("StringArrayRank3"), StrEq("[[[Ljava/lang/String;"))); - LocalArray arr1 = obj["StringArrayRank1"].Get(); - LocalArray arr2 = obj["StringArrayRank2"].Get(); - LocalArray arr3 = obj["StringArrayRank3"].Get(); + LocalArray arr1 = obj.Access<"StringArrayRank1">().Get(); + LocalArray arr2 = obj.Access<"StringArrayRank2">().Get(); + LocalArray arr3 = obj.Access<"StringArrayRank3">().Get(); } } // namespace diff --git a/implementation/local_object_test.cc b/implementation/local_object_test.cc index ef193ac3..87c1af07 100644 --- a/implementation/local_object_test.cc +++ b/implementation/local_object_test.cc @@ -124,9 +124,10 @@ TEST_F(JniTest, LocalObject_ObjectReturnsInstanceMethods) { EXPECT_CALL(*env_, DeleteLocalRef(Fake())).Times(1); LocalObject obj{}; - obj("Foo", 12345); - obj("Baz", 12345.f); - obj("AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplatesToHandle", + obj.Call<"Foo">(12345); + obj.Call<"Baz">(12345.f); + obj.Call< + "AMethodWithAReallyLongNameThatWouldPossiblyBeHardForTemplatesToHandle">( 12345, 12345.f, 12345, 12345.f, jdouble{12345}); } @@ -164,15 +165,15 @@ TEST_F(JniTest, LocalObject_ValuesWorkAfterMoveConstructor) { EXPECT_CALL(*env_, SetIntField).Times(4); LocalObject obj_1{Fake()}; - obj_1("Foo", 1); - obj_1("Foo", 2); - obj_1["BarField"].Set(1); + obj_1.Call<"Foo">(1); + obj_1.Call<"Foo">(2); + obj_1.Access<"BarField">().Set(1); LocalObject obj_2{std::move(obj_1)}; - obj_2("Foo", 3); + obj_2.Call<"Foo">(3); obj_2["BarField"].Set(2); - obj_2["BarField"].Set(3); - obj_2["BarField"].Set(4); + obj_2.Access<"BarField">().Set(3); + obj_2.Access<"BarField">().Set(4); } TEST_F(JniTest, LocalObject_ReleasesLocalsForAlternateConstructors) { @@ -249,7 +250,7 @@ TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnLvalue) { LocalObject a{}; LocalObject b{}; - b("Foo", a); + b.Call<"Foo">(a); } TEST_F(JniTest, LocalObject_SupportsReturningAClass) { @@ -257,7 +258,7 @@ TEST_F(JniTest, LocalObject_SupportsReturningAClass) { "Class1", Method{"Foo", jni::Return{kClass2}, jni::Params{}}}; LocalObject a{}; - a("Foo"); + a.Call<"Foo">(); } TEST_F(JniTest, LocalObject_SupportsReturningAString) { @@ -265,13 +266,13 @@ TEST_F(JniTest, LocalObject_SupportsReturningAString) { "Class1", Method{"Foo", jni::Return{}, jni::Params{}}}; LocalObject a{}; - a("Foo"); + a.Call<"Foo">(); } jobject ReturnOutputOfMethod() { static constexpr Class kClass2{"Class2", Method{"Foo", jni::Return{kClass}}}; - return LocalObject{}("Foo").Release(); + return LocalObject{}.Call<"Foo">().Release(); } TEST_F(JniTest, LocalObject_CompilesWhenReturnReleasing) { @@ -284,7 +285,7 @@ TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnPrvalue) { LocalObject a{}; LocalObject b{}; - b("Foo", std::move(a)); + b.Call<"Foo">(std::move(a)); } TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnXvalue) { @@ -292,7 +293,7 @@ TEST_F(JniTest, LocalObject_SupportsPassingAnObjectAsAnXvalue) { "Class2", Method{"Foo", jni::Return{}, jni::Params{kClass}}}; LocalObject b{}; - b("Foo", LocalObject{}); + b.Call<"Foo">(LocalObject{}); } TEST_F(JniTest, LocalObject_MovesInContainerStruct) { @@ -320,8 +321,8 @@ TEST_F(JniTest, LocalObject_DoesntCrossTalkOverClassMethodIds) { LocalObject obj_2{Fake(2)}; // These are different method IDs (they are different classes). - obj_1("Foo", 1); - obj_2("Foo", 1); + obj_1.Call<"Foo">(1); + obj_2.Call<"Foo">(1); } } // namespace diff --git a/implementation/method_ref_test.cc b/implementation/method_ref_test.cc index 65c907ef..3406428a 100644 --- a/implementation/method_ref_test.cc +++ b/implementation/method_ref_test.cc @@ -193,7 +193,7 @@ TEST_F(JniTest, MethodRef_ReturnsObjects) { EXPECT_CALL(*env_, NewObjectV).WillOnce(testing::Return(Fake())); GlobalObject global_object{}; - LocalObject new_obj{global_object("Foo", 5)}; + LocalObject new_obj{global_object.Call<"Foo">(5)}; } TEST_F(JniTest, MethodRef_PassesObjects) { @@ -209,7 +209,7 @@ TEST_F(JniTest, MethodRef_PassesObjects) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(Lcom/google/Bazz;)I"))); - global_object("Foo", local_object); + global_object.Call<"Foo">(local_object); } TEST_F(JniTest, MethodRef_PassesAndReturnsMultipleObjects) { @@ -229,7 +229,7 @@ TEST_F(JniTest, MethodRef_PassesAndReturnsMultipleObjects) { LocalObject obj4{Fake(4)}; LocalObject object_under_test{Fake(5)}; - LocalObject obj5{object_under_test("Foo", obj1, obj2, obj3, obj4)}; + LocalObject obj5{object_under_test.Call<"Foo">(obj1, obj2, obj3, obj4)}; } TEST_F(JniTest, MethodRef_SupportsForwardDefines) { @@ -255,20 +255,21 @@ TEST_F(JniTest, MethodRef_SupportsForwardDefines) { LocalObject c2_obj1{Fake(3)}; LocalObject c2_obj2{Fake(4)}; - c1_obj1("m1", c1_obj1); - c1_obj1("m2", c2_obj1); - c1_obj1("m1", c1_obj1("m3")); - c1_obj1("m2", c1_obj1("m4")); + c1_obj1.Call<"m1">(c1_obj1); + c1_obj1.Call<"m2">(c2_obj1); + c1_obj1.Call<"m1">(c1_obj1.Call<"m3">()); + c1_obj1.Call<"m2">(c1_obj1.Call<"m4">()); - c2_obj1("m1", c1_obj1); - c2_obj1("m2", c2_obj2); - c2_obj1("m2", std::move(std::move(c2_obj2))); + c2_obj1.Call<"m1">(c1_obj1); + c2_obj1.Call<"m2">(c2_obj2); + c2_obj1.Call<"m2">(std::move(std::move(c2_obj2))); - c1_obj1("m2", std::move(c2_obj1)); + c1_obj1.Call<"m2">(std::move(c2_obj1)); - // c2_obj1("m1", c1_obj1); // illegal! triggers warnings (post move read). - // c2_obj1("m2", c2_obj2); // illegal! triggers warnings (post move read). - // c2_obj1("m2", std::move(c2_obj2)); // illegal! triggers warnings (post + // c2_obj1.Call<"m1">(c1_obj1); // illegal! triggers warnings (post move + // read). c2_obj1.Call<"m2">(c2_obj2); // illegal! triggers warnings (post + // move read). c2_obj1.Call<"m2">(std::move(c2_obj2)); // illegal! triggers + // warnings (post // move read). } @@ -281,9 +282,9 @@ TEST_F(JniTest, MethodRef_SupportsStrings) { }; LocalObject obj1{Fake()}; - obj1("Foo", "This is a method."); - obj1("Bar", "This is a method.", "It takes strings"); - obj1("Baz"); + obj1.Call<"Foo">("This is a method."); + obj1.Call<"Bar">("This is a method.", "It takes strings"); + obj1.Call<"Baz">(); } TEST_F(JniTest, MethodRef_SupportsArrays) { @@ -295,7 +296,7 @@ TEST_F(JniTest, MethodRef_SupportsArrays) { LocalArray local_array{nullptr}; LocalObject obj1{Fake()}; - obj1("Foo", local_array); + obj1.Call<"Foo">(local_array); } } // namespace diff --git a/implementation/multi_type_test.cc b/implementation/multi_type_test.cc index 3aceb6b0..b926bdbb 100644 --- a/implementation/multi_type_test.cc +++ b/implementation/multi_type_test.cc @@ -71,13 +71,13 @@ TEST_F(JniTest, MultiTypeTest_SimpleSmokeTestForSingleObject) { EXPECT_CALL(*env_, DeleteGlobalRef(Fake(2))).Times(1); jni::GlobalObject obj{}; - obj("Foo", 1, 2.f); - obj("Baz", 1.f); - obj("Baz", 1.f); - obj("Baz", 2.f); - obj("Baz", 3.f); - obj("Bar"); - obj["SomeField"].Get(); + obj.Call<"Foo">(1, 2.f); + obj.Call<"Baz">(1.f); + obj.Call<"Baz">(1.f); + obj.Call<"Baz">(2.f); + obj.Call<"Baz">(3.f); + obj.Call<"Bar">(); + obj.Access<"SomeField">().Get(); } TEST_F(JniTest, MultiTypeTest_MethodsOfSameNameButDifferentClassAreUnique) { @@ -94,21 +94,21 @@ TEST_F(JniTest, MultiTypeTest_MethodsOfSameNameButDifferentClassAreUnique) { jni::LocalObject obj1{Fake(1)}; jni::LocalObject obj2{Fake(2)}; - obj1("Foo", 12345); - obj2("Foo", 12345); + obj1.Call<"Foo">(12345); + obj2.Call<"Foo">(12345); // All of these calls ought not query for a method ID again. - obj1("Foo", 12345); - obj1("Foo", 12345); - obj1("Foo", 12345); - obj1("Foo", 12345); - obj2("Foo", 12345); - obj2("Foo", 12345); - obj2("Foo", 12345); + obj1.Call<"Foo">(12345); + obj1.Call<"Foo">(12345); + obj1.Call<"Foo">(12345); + obj1.Call<"Foo">(12345); + obj2.Call<"Foo">(12345); + obj2.Call<"Foo">(12345); + obj2.Call<"Foo">(12345); jni::LocalObject obj3{Fake(3)}; - obj3("Foo", 12345); - obj3("Foo", 12345); + obj3.Call<"Foo">(12345); + obj3.Call<"Foo">(12345); } } // namespace diff --git a/implementation/object_ref.h b/implementation/object_ref.h index e3fef711..de6e7415 100644 --- a/implementation/object_ref.h +++ b/implementation/object_ref.h @@ -34,8 +34,11 @@ #include "implementation/ref_base.h" #include "jni_dep.h" #include "metaprogramming/invocable_map.h" +#include "metaprogramming/invocable_map_20.h" #include "metaprogramming/queryable_map.h" +#include "metaprogramming/queryable_map_20.h" #include "metaprogramming/string_contains.h" +#include "metaprogramming/string_literal.h" namespace jni { @@ -47,11 +50,19 @@ namespace jni { // operator[]. template class ObjectRef + // C++17 augmentations. : public metaprogramming::InvocableMap< ObjectRef, JniT::stripped_class_v, typename JniT::ClassT, &JniT::ClassT::methods_>, public metaprogramming::QueryableMap_t< ObjectRef, JniT::stripped_class_v, &JniT::ClassT::fields_>, + // C++ 20 augmentations. + public metaprogramming::InvocableMap20< + ObjectRef, JniT::stripped_class_v, ObjectRef, + decltype(&JniT::ClassT::methods_), &JniT::ClassT::methods_>, + public metaprogramming::QueryableMap20< + ObjectRef, JniT::stripped_class_v, ObjectRef, + decltype(&JniT::ClassT::fields_), &JniT::ClassT::fields_>, public RefBase { protected: static_assert( @@ -83,6 +94,12 @@ class ObjectRef explicit ObjectRef(RefBaseT&& rhs) : RefBaseT(std::move(rhs)) {} + //////////////////////////////////////////////////////////////////////////////// + // Implementation: C++17 + clang + // Supports syntax like: "obj("foo", 123, args)", "obj["foo"].Get()" + // This syntax is less portable and may be removed in a major future release. + //////////////////////////////////////////////////////////////////////////////// +#if __clang__ // Invoked through CRTP from InvocableMap. template auto InvocableMapCall(const char* key, Args&&... args) const { @@ -103,10 +120,41 @@ class ObjectRef auto QueryableMapCall(const char* key) const { return FieldRef{GetJClass(), RefBaseT::object_ref_}; } +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// C++20 Implementation (will only be included if supported). +// Supports syntax like: "obj.Call<"foo">(123, args)", +// "obj.Access<"foo">().Get()" Prefer using this syntax as they are more +// portable. +//////////////////////////////////////////////////////////////////////////////// +#if __cplusplus >= 202002L + // Invoked through CRTP from InvocableMap, C++20 only. + template + auto InvocableMap20Call(Args&&... args) const { + using IdT = Id; + using MethodSelectionForArgs = + OverloadSelector; + + static_assert(MethodSelectionForArgs::kIsValidArgSet, + "JNI Error: Invalid argument set."); + + return MethodSelectionForArgs::OverloadRef::Invoke( + GetJClass(), RefBaseT::object_ref_, std::forward(args)...); + } + + // Invoked through CRTP from QueryableMap20, C++20 only. + template + auto QueryableMap20Call() const { + return FieldRef{GetJClass(), RefBaseT::object_ref_}; + } +#endif // __cplusplus >= 202002L }; // Imbues constructors for ObjectRefs and handles calling the correct -// intermediate constructors. Access to this class is constrainted for non +// intermediate constructors. Access to this class is constrained for non // default classloaders (see |ValidatorProxy|). template class ConstructorValidator : public ObjectRef { diff --git a/implementation/overload_ref_test.cc b/implementation/overload_ref_test.cc index b42e71e6..d452b9b3 100644 --- a/implementation/overload_ref_test.cc +++ b/implementation/overload_ref_test.cc @@ -40,7 +40,7 @@ TEST_F(JniTest, MethodRef_AsksForCorrectMethods1) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;)V"))); - obj("Foo", "test"); + obj.Call<"Foo">("test"); } TEST_F(JniTest, MethodRef_AsksForCorrectMethods2) { @@ -54,9 +54,9 @@ TEST_F(JniTest, MethodRef_AsksForCorrectMethods2) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;)V"))); - obj("Foo", 1); - // obj("Foo", 2.f); doesn't compile (good). - obj("Foo", "test"); + obj.Call<"Foo">(1); + // obj.Call<"Foo">(2.f); doesn't compile (good). + obj.Call<"Foo">("test"); } TEST_F(JniTest, MethodRef_AsksForCorrectMethods3) { @@ -77,11 +77,11 @@ TEST_F(JniTest, MethodRef_AsksForCorrectMethods3) { GetMethodID(_, StrEq("Foo"), StrEq("(Ljava/lang/String;Ljava/lang/String;)V"))); - obj("Foo", 1); - // obj("Foo", 2.f); // doesn't compile (good). - obj("Foo", "test"); - obj("Foo", "test1", "test2"); - obj("Foo", "this_doesnt", "trigger_method_lookup"); + obj.Call<"Foo">(1); + // obj.Call<"Foo">(2.f); // doesn't compile (good). + obj.Call<"Foo">("test"); + obj.Call<"Foo">("test1", "test2"); + obj.Call<"Foo">("this_doesnt", "trigger_method_lookup"); } TEST_F(JniTest, MethodRef_AsksForCorrectMethodsWhenMultiplePresent4) { @@ -113,14 +113,14 @@ TEST_F(JniTest, MethodRef_AsksForCorrectMethodsWhenMultiplePresent4) { StrEq("(Ljava/lang/String;Ljava/lang/String;)V"))); EXPECT_CALL(*env_, GetMethodID(_, StrEq("Baz"), StrEq("(FF)V"))); - obj("Foo", 1); - // obj("Foo", 2.f); // doesn't compile (good). - obj("Foo", "test"); - obj("Foo", "test1", "test2"); - obj("Foo", "this_doesnt", "trigger_method_lookup"); - obj("Baz", 1); - obj("Baz", "test3", "test4"); - obj("Baz", 1234.f, 5678.f); + obj.Call<"Foo">(1); + // obj.Call<"Foo">(2.f); // doesn't compile (good). + obj.Call<"Foo">("test"); + obj.Call<"Foo">("test1", "test2"); + obj.Call<"Foo">("this_doesnt", "trigger_method_lookup"); + obj.Call<"Baz">(1); + obj.Call<"Baz">("test3", "test4"); + obj.Call<"Baz">(1234.f, 5678.f); } } // namespace diff --git a/implementation/proxy_test.cc b/implementation/proxy_test.cc index f82ed8b6..3a9a15e5 100644 --- a/implementation/proxy_test.cc +++ b/implementation/proxy_test.cc @@ -23,6 +23,7 @@ #include #include "jni_bind.h" #include "jni_test.h" +#include "metaprogramming/type_to_type_map.h" using ::jni::AsDecl_t; using ::jni::Class; @@ -190,28 +191,28 @@ TEST_F(JniTest, MaterializationTests) { // Objects can be rvalues (here an x-value). LocalObject generator_obj{}; - LocalObject local_1{generator_obj("ReturnsObj")}; + LocalObject local_1{generator_obj.Call<"ReturnsObj">()}; // Objects can also be described with no actual class definition. // These objects won't be usable, but they are "name-safe". - LocalObject local_2{generator_obj("ReturnsObj")}; + LocalObject local_2{generator_obj.Call<"ReturnsObj">()}; // doesn't compile because of invalid class (good). - // LocalObject local_3 { generator_obj("ReturnsObj") }; } + // LocalObject local_3 { generator_obj.Call<"ReturnsObj">() }; } // Globals can be built from locals. - GlobalObject global_1{generator_obj("ReturnsObj")}; - global_1("Bar"); + GlobalObject global_1{generator_obj.Call<"ReturnsObj">()}; + global_1.Call<"Bar">(); // Globals can be built from expiring locals without a full type. - GlobalObject global_2{generator_obj("ReturnsObj")}; + GlobalObject global_2{generator_obj.Call<"ReturnsObj">()}; // But they lack sufficient definition to have any invocation! - // global_2("Bar"); + // global_2.Call<"Bar">(); // They can be promoted however, and thus restore their type info. GlobalObject global_3{std::move(global_2)}; - global_3("Bar"); + global_3.Call<"Bar">(); } } // namespace jni diff --git a/implementation/return.h b/implementation/return.h index 8f352be3..7b6e8f57 100644 --- a/implementation/return.h +++ b/implementation/return.h @@ -31,7 +31,7 @@ struct Return : ReturnBase { using Raw = Raw_; - constexpr Return() = default; + constexpr Return() {} template constexpr explicit Return(Raw raw) : raw_(raw) {} @@ -42,7 +42,7 @@ struct Return : ReturnBase { using Raw = void; const Void raw_{}; - constexpr Return() = default; + constexpr Return() {} }; Return() -> Return; diff --git a/implementation/self_test.cc b/implementation/self_test.cc index 68e81d25..d2fd2e81 100644 --- a/implementation/self_test.cc +++ b/implementation/self_test.cc @@ -44,7 +44,7 @@ TEST_F(JniTest, SelfCanBeUsedAsAReturnValue) { StrEq("()LBuilder;"))); LocalObject obj{Fake()}; - obj("build"); + obj.Call<"build">(); } TEST_F(JniTest, SelfCanBeUsedAsAReturnValueAndMaintainsRichDecoration) { @@ -52,7 +52,7 @@ TEST_F(JniTest, SelfCanBeUsedAsAReturnValueAndMaintainsRichDecoration) { EXPECT_CALL(*env_, GetMethodID(_, StrEq("build"), StrEq("()LBuilder;"))); LocalObject obj{Fake()}; - obj("build")("build")("build"); + obj.Call<"build">().Call<"build">().Call<"build">(); } TEST_F(JniTest, SelfCanBeUsedAsParam) { @@ -62,7 +62,7 @@ TEST_F(JniTest, SelfCanBeUsedAsParam) { LocalObject obj_1{Fake(1)}; LocalObject obj_2{Fake(2)}; - obj_1("takesBuilder", obj_2); + obj_1.Call<"takesBuilder">(obj_2); } } // namespace diff --git a/implementation/static_ref.h b/implementation/static_ref.h index ff5ec6b0..a4e55063 100644 --- a/implementation/static_ref.h +++ b/implementation/static_ref.h @@ -32,7 +32,10 @@ #include "implementation/no_idx.h" #include "jni_dep.h" #include "metaprogramming/invocable_map.h" +#include "metaprogramming/invocable_map_20.h" #include "metaprogramming/queryable_map.h" +#include "metaprogramming/queryable_map_20.h" +#include "metaprogramming/string_literal.h" namespace jni { @@ -41,13 +44,26 @@ template ; + // C++17 augmentations. using MethodMapT = metaprogramming::InvocableMap; using FieldMapT = metaprogramming::QueryableMap_t; + + // C++ 20 augmentations. + using MethodMap20T = metaprogramming::InvocableMap20< + CrtpBase_, JniT::static_v, + StaticRefHelper, + decltype(&JniT::StaticT::methods_), &JniT::StaticT::methods_>; + + using FieldMap20T = metaprogramming::QueryableMap20< + CrtpBase_, JniT::static_v, + StaticRefHelper, + decltype(&JniT::StaticT::fields_), &JniT::StaticT::fields_>; }; +// C++17 augmentations. template using StaticRefHelperMethodMap_t = @@ -60,21 +76,45 @@ using StaticRefHelperFieldMap_t = typename StaticRefHelper::FieldMapT; +// C++20 augmentations. +template +using StaticRefHelperMethodMap20_t = + typename StaticRefHelper::MethodMap20T; +template +using StaticRefHelperFieldMap20_t = + typename StaticRefHelper::FieldMap20T; + template struct StaticRef - : public StaticRefHelperMethodMap_t< - StaticRef, class_v_, - class_loader_v_, jvm_v_>, + : public + // C++17 augmentations. + StaticRefHelperMethodMap_t, + class_v_, class_loader_v_, jvm_v_>, StaticRefHelperFieldMap_t, - class_v_, class_loader_v_, jvm_v_> { + class_v_, class_loader_v_, jvm_v_>, + // C++ 20 augmentations. + StaticRefHelperMethodMap20_t, + class_v_, class_loader_v_, jvm_v_>, + StaticRefHelperFieldMap20_t, + class_v_, class_loader_v_, jvm_v_> { using JniT = JniT; jclass GetJClass() const { return ClassRef_t::GetAndMaybeLoadClassRef(nullptr); } + //////////////////////////////////////////////////////////////////////////////// + // Implementation: C++17 + clang + // Supports syntax like: "obj("foo", 123, args)", "obj["foo"].Get()" + // This syntax is less portable and may be removed in a major future release. + //////////////////////////////////////////////////////////////////////////////// +#if __clang__ template auto InvocableMapCall(const char* key, Args&&... args) const { using IdT = Id; @@ -93,6 +133,31 @@ struct StaticRef auto QueryableMapCall(const char* key) const { return FieldRef{GetJClass(), nullptr}; } +#endif // __clang__ + +#if __cplusplus >= 202002L + // Invoked through CRTP from InvocableMap, C++20 only. + template + auto InvocableMap20Call(Args&&... args) const { + using IdT = Id; + using MethodSelectionForArgs = + OverloadSelector; + + static_assert(MethodSelectionForArgs::kIsValidArgSet, + "JNI Error: Invalid argument set."); + + return MethodSelectionForArgs::OverloadRef::Invoke( + GetJClass(), nullptr, std::forward(args)...); + } + + // Invoked through CRTP from QueryableMap20, C++20 only. + template + auto QueryableMap20Call() const { + return FieldRef{GetJClass(), nullptr}; + } +#endif // __cplusplus >= 202002L }; } // namespace jni diff --git a/implementation/static_ref_test.cc b/implementation/static_ref_test.cc index 2a560458..9938f89b 100644 --- a/implementation/static_ref_test.cc +++ b/implementation/static_ref_test.cc @@ -73,7 +73,7 @@ TEST_F(JniTest, StaticBooleanField) { EXPECT_CALL(*env_, SetStaticBooleanField(_, Fake(), true)); LocalObject obj; - EXPECT_TRUE(StaticRef{}["booleanField"].Get()); + EXPECT_TRUE(StaticRef{}.Access<"booleanField">().Get()); StaticRef{}["booleanField"].Set(true); } @@ -276,21 +276,21 @@ TEST_F(JniTest, StaticExerciseAllReturns) { EXPECT_CALL(*env_, CallStaticObjectMethodV).Times(5); - StaticRef{}("booleanMethod"); - StaticRef{}("byteMethod"); - StaticRef{}("charMethod"); - StaticRef{}("shortMethod"); - StaticRef{}("intMethod"); - StaticRef{}("longMethod"); - StaticRef{}("floatMethod"); - StaticRef{}("doubleMethod"); - StaticRef{}("stringMethod"); - - StaticRef{}("objectMethod"); - StaticRef{}("rank1ArrayMethod"); - StaticRef{}("rank2ArrayMethod"); - - LocalObject self_ret = StaticRef{}("selfMethod"); + StaticRef{}.Call<"booleanMethod">(); + StaticRef{}.Call<"byteMethod">(); + StaticRef{}.Call<"charMethod">(); + StaticRef{}.Call<"shortMethod">(); + StaticRef{}.Call<"intMethod">(); + StaticRef{}.Call<"longMethod">(); + StaticRef{}.Call<"floatMethod">(); + StaticRef{}.Call<"doubleMethod">(); + StaticRef{}.Call<"stringMethod">(); + + StaticRef{}.Call<"objectMethod">(); + StaticRef{}.Call<"rank1ArrayMethod">(); + StaticRef{}.Call<"rank2ArrayMethod">(); + + LocalObject self_ret = StaticRef{}.Call<"selfMethod">(); } // clang-format off @@ -339,23 +339,23 @@ TEST_F(JniTest, StaticExerciseAllTypesThroughSingleParam) { EXPECT_CALL(*env_, CallStaticVoidMethodV).Times(13); - StaticRef{}("booleanMethod", jboolean{true}); - StaticRef{}("byteMethod", jbyte{1}); - StaticRef{}("charMethod", jchar{'a'}); - StaticRef{}("shortMethod", jshort{1}); - StaticRef{}("intMethod", jint{123}); - StaticRef{}("longMethod", jlong{456}); - StaticRef{}("floatMethod", jfloat{789.f}); - StaticRef{}("doubleMethod", jdouble{101.}); - StaticRef{}("stringMethod", "test"); + StaticRef{}.Call<"booleanMethod">(jboolean{true}); + StaticRef{}.Call<"byteMethod">(jbyte{1}); + StaticRef{}.Call<"charMethod">(jchar{'a'}); + StaticRef{}.Call<"shortMethod">(jshort{1}); + StaticRef{}.Call<"intMethod">(jint{123}); + StaticRef{}.Call<"longMethod">(jlong{456}); + StaticRef{}.Call<"floatMethod">(jfloat{789.f}); + StaticRef{}.Call<"doubleMethod">(jdouble{101.}); + StaticRef{}.Call<"stringMethod">("test"); // It would be more complete to exercise all types here. - StaticRef{}("objectMethod", Fake()); - StaticRef{}("rank1ArrayMethod", - Fake()); - StaticRef{}("rank2ArrayMethod", - Fake()); - StaticRef{}("selfMethod", Fake()); + StaticRef{}.Call<"objectMethod">(Fake()); + StaticRef{}.Call<"rank1ArrayMethod">( + Fake()); + StaticRef{}.Call<"rank2ArrayMethod">( + Fake()); + StaticRef{}.Call<"selfMethod">(Fake()); } TEST_F(JniTest, StaticExerciseComplexSetOfParams) { @@ -365,9 +365,9 @@ TEST_F(JniTest, StaticExerciseComplexSetOfParams) { EXPECT_CALL(*env_, GetStaticMethodID(_, StrEq("complexFunc"), StrEq("([[LkClass2;IFLkClass3;)F"))); - StaticRef{}("simpleFunc", 123.f); - StaticRef{}("complexFunc", jobjectArray{nullptr}, 123, 456.f, - jobject{nullptr}); + StaticRef{}.Call<"simpleFunc">(123.f); + StaticRef{}.Call<"complexFunc">(jobjectArray{nullptr}, 123, + 456.f, jobject{nullptr}); } } // namespace diff --git a/implementation/string_ref_test.cc b/implementation/string_ref_test.cc index 722def8f..debc55e3 100644 --- a/implementation/string_ref_test.cc +++ b/implementation/string_ref_test.cc @@ -14,6 +14,11 @@ * limitations under the License. */ +#include +#include +#include + +#include #include #include "implementation/jni_helper/fake_test_constants.h" #include "jni_bind.h" @@ -86,7 +91,7 @@ TEST_F(JniTest, LocalString_CopiesFromJString) { TEST_F(JniTest, LocalString_ConstructsFromOutputOfMethod) { LocalObject obj{}; - LocalString str{obj("Foo")}; + LocalString str{obj.Call<"Foo">()}; } TEST_F(JniTest, LocalString_ConstructsFromByteArray) { @@ -146,12 +151,12 @@ TEST_F(JniTest, LocalString_PinsAndUnpinsMemoryForLocals) { TEST_F(JniTest, LocalString_AllowsLValueLocalString) { LocalObject obj{}; LocalString local_string{"abcde"}; - obj("TakesStrParam", local_string); + obj.Call<"TakesStrParam">(local_string); } TEST_F(JniTest, LocalString_AllowsRValueLocalString) { LocalObject obj{}; - obj("TakesStrParam", LocalString{"abcde"}); + obj.Call<"TakesStrParam">(LocalString{"abcde"}); } //////////////////////////////////////////////////////////////////////////////// @@ -222,12 +227,12 @@ TEST_F(JniTest, GlobalString_PinsAndUnpinsMemoryForLocals) { TEST_F(JniTest, GlobalString_AllowsLValueGlobalString) { LocalObject obj{}; GlobalString global_string{"abcde"}; - obj("TakesStrParam", global_string); + obj.Call<"TakesStrParam">(global_string); } TEST_F(JniTest, GlobalString_AllowsRValueGlobalString) { LocalObject obj{}; - obj("TakesStrParam", GlobalString{"abcde"}); + obj.Call<"TakesStrParam">(GlobalString{"abcde"}); } } // namespace diff --git a/java/com/google/main.cc b/java/com/google/main.cc index de5ea5ea..df9fca2f 100644 --- a/java/com/google/main.cc +++ b/java/com/google/main.cc @@ -46,7 +46,7 @@ void RunIterationsToCompletion(jni::JvmRef *jvm_ref) { for (int i = 0; i < kNumIterations; ++i) { printf("Iteration %i: %s\n", i, - random_string("format").Pin().ToString().data()); + random_string.Call<"format">().Pin().ToString().data()); } } diff --git a/javatests/com/jnibind/android/array_test_jni.cc b/javatests/com/jnibind/android/array_test_jni.cc index e193bf68..864f8c6b 100644 --- a/javatests/com/jnibind/android/array_test_jni.cc +++ b/javatests/com/jnibind/android/array_test_jni.cc @@ -55,11 +55,11 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeBooleanTests( // Simple lvalue pass through works as expected. LocalArray local_arr{boolean_array}; - rjni_test_helper("rJniBooleanArray", false, local_arr); + rjni_test_helper.Call<"rJniBooleanArray">(false, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniBooleanArray", false, - LocalArray{boolean_array}); + rjni_test_helper.Call<"rJniBooleanArray">( + false, LocalArray{boolean_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -69,7 +69,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeBooleanTests( array_view.ptr()[i] = true; } } - rjni_test_helper("rJniBooleanArray", true, new_array); + rjni_test_helper.Call<"rJniBooleanArray">(true, new_array); // You can pull the view multiple times. { @@ -78,7 +78,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeBooleanTests( array_view.ptr()[i] = true; } } - rjni_test_helper("rJniBooleanArray", true, new_array); + rjni_test_helper.Call<"rJniBooleanArray">(true, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeByteTests( @@ -87,10 +87,11 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeByteTests( // Simple lvalue pass through works as expected. LocalArray local_arr{byte_array}; - rjni_test_helper("rJniByteArray", jbyte{0}, local_arr); + rjni_test_helper.Call<"rJniByteArray">(jbyte{0}, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniByteArray", jbyte{0}, LocalArray{byte_array}); + rjni_test_helper.Call<"rJniByteArray">(jbyte{0}, + LocalArray{byte_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -100,7 +101,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeByteTests( array_view.ptr()[i] = static_cast(i); } } - rjni_test_helper("rJniByteArray", jbyte{0}, new_array); + rjni_test_helper.Call<"rJniByteArray">(jbyte{0}, new_array); // You can pull the view multiple times. { @@ -109,7 +110,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeByteTests( array_view.ptr()[i] += 5; } } - rjni_test_helper("rJniByteArray", jbyte{5}, new_array); + rjni_test_helper.Call<"rJniByteArray">(jbyte{5}, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeCharTests( @@ -118,10 +119,11 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeCharTests( // Simple lvalue pass through works as expected. LocalArray local_arr{char_array}; - rjni_test_helper("rJniCharArray", jchar{0}, local_arr); + rjni_test_helper.Call<"rJniCharArray">(jchar{0}, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniCharArray", jchar{0}, LocalArray{char_array}); + rjni_test_helper.Call<"rJniCharArray">(jchar{0}, + LocalArray{char_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -131,7 +133,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeCharTests( array_view.ptr()[i] = static_cast(i); } } - rjni_test_helper("rJniCharArray", jchar{0}, new_array); + rjni_test_helper.Call<"rJniCharArray">(jchar{0}, new_array); // You can pull the view multiple times. { @@ -140,7 +142,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeCharTests( array_view.ptr()[i] += 5; } } - rjni_test_helper("rJniCharArray", jchar{5}, new_array); + rjni_test_helper.Call<"rJniCharArray">(jchar{5}, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeShortTests( @@ -149,11 +151,11 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeShortTests( // Simple lvalue pass through works as expected. LocalArray local_arr{short_array}; - rjni_test_helper("rJniShortArray", jshort{0}, local_arr); + rjni_test_helper.Call<"rJniShortArray">(jshort{0}, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniShortArray", jshort{0}, - LocalArray{short_array}); + rjni_test_helper.Call<"rJniShortArray">(jshort{0}, + LocalArray{short_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -163,7 +165,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeShortTests( array_view.ptr()[i] = static_cast(i); } } - rjni_test_helper("rJniShortArray", jshort{0}, new_array); + rjni_test_helper.Call<"rJniShortArray">(jshort{0}, new_array); // You can pull the view multiple times. { @@ -172,7 +174,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeShortTests( array_view.ptr()[i] += 5; } } - rjni_test_helper("rJniShortArray", jshort{5}, new_array); + rjni_test_helper.Call<"rJniShortArray">(jshort{5}, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeIntTests( @@ -181,10 +183,10 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeIntTests( // Simple lvalue pass through works as expected. LocalArray local_arr{int_array}; - rjni_test_helper("rJniIntArray", 0, local_arr); + rjni_test_helper.Call<"rJniIntArray">(0, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniIntArray", 0, LocalArray{int_array}); + rjni_test_helper.Call<"rJniIntArray">(0, LocalArray{int_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -194,7 +196,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeIntTests( array_view.ptr()[i] = i; } } - rjni_test_helper("rJniIntArray", 0, new_array); + rjni_test_helper.Call<"rJniIntArray">(0, new_array); // You can pull the view multiple times. { @@ -203,7 +205,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeIntTests( array_view.ptr()[i] += 5; } } - rjni_test_helper("rJniIntArray", 5, new_array); + rjni_test_helper.Call<"rJniIntArray">(5, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeLongTests( @@ -212,10 +214,11 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeLongTests( // Simple lvalue pass through works as expected. LocalArray local_arr{long_array}; - rjni_test_helper("rJniLongArray", jlong{0}, local_arr); + rjni_test_helper.Call<"rJniLongArray">(jlong{0}, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniLongArray", jlong{0}, LocalArray{long_array}); + rjni_test_helper.Call<"rJniLongArray">(jlong{0}, + LocalArray{long_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -225,7 +228,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeLongTests( array_view.ptr()[i] = static_cast(i); } } - rjni_test_helper("rJniLongArray", jlong{0}, new_array); + rjni_test_helper.Call<"rJniLongArray">(jlong{0}, new_array); // You can pull the view multiple times. { @@ -234,7 +237,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeLongTests( array_view.ptr()[i] += 5; } } - rjni_test_helper("rJniLongArray", jlong{5}, new_array); + rjni_test_helper.Call<"rJniLongArray">(jlong{5}, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeFloatTests( @@ -242,7 +245,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeFloatTests( LocalObject rjni_test_helper{test_fixture}; LocalArray local_arr{float_array}; - rjni_test_helper("rJniFloatArray", 0.f, local_arr); + rjni_test_helper.Call<"rJniFloatArray">(0.f, local_arr); { ArrayView array_view{local_arr.Pin(true)}; @@ -250,7 +253,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeFloatTests( array_view.ptr()[i] += 2.5f; } } - rjni_test_helper("rJniFloatArray", 2.5f, local_arr); + rjni_test_helper.Call<"rJniFloatArray">(2.5f, local_arr); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeDoubleTests( @@ -259,11 +262,11 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeDoubleTests( // Simple lvalue pass through works as expected. LocalArray local_arr{double_array}; - rjni_test_helper("rJniDoubleArray", jdouble{0}, local_arr); + rjni_test_helper.Call<"rJniDoubleArray">(jdouble{0}, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniDoubleArray", jdouble{0}, - LocalArray{double_array}); + rjni_test_helper.Call<"rJniDoubleArray">(jdouble{0}, + LocalArray{double_array}); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -273,7 +276,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeDoubleTests( array_view.ptr()[i] = static_cast(i); } } - rjni_test_helper("rJniDoubleArray", jdouble{0}, new_array); + rjni_test_helper.Call<"rJniDoubleArray">(jdouble{0}, new_array); // You can pull the view multiple times. { @@ -282,7 +285,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeDoubleTests( array_view.ptr()[i] += 5; } } - rjni_test_helper("rJniDoubleArray", jdouble{5}, new_array); + rjni_test_helper.Call<"rJniDoubleArray">(jdouble{5}, new_array); } JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeObjectTests( @@ -291,12 +294,12 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeObjectTests( // Simple lvalue pass through works as expected. LocalArray local_arr{object_array}; - rjni_test_helper("rJniObjectArray", 0, local_arr); + rjni_test_helper.Call<"rJniObjectArray">(0, local_arr); // Simple rvalue pass through works as expected. - rjni_test_helper("rJniObjectArray", 5, - LocalArray{ - 1, LocalObject{5, 5, 5}}); + rjni_test_helper.Call<"rJniObjectArray">( + 5, LocalArray{ + 1, LocalObject{5, 5, 5}}); // Building a new array, and setting all the values by hand works. LocalObject obj{0, 0, 0}; @@ -307,17 +310,17 @@ JNIEXPORT void JNICALL Java_com_jnibind_android_ArrayTest_nativeObjectTests( i, LocalObject{jint{i}, jint{i}, jint{i}}); } } - rjni_test_helper("rJniObjectArray", 0, new_array); + rjni_test_helper.Call<"rJniObjectArray">(0, new_array); // You can pull the view multiple times. { for (int i = 0; i < new_array.Length(); ++i) { - new_array.Set(i, - LocalObject{2, 2, 2}( - "returnNewObjectWithFieldSetToSum", new_array.Get(i))); + new_array.Set( + i, LocalObject{2, 2, 2} + .Call<"returnNewObjectWithFieldSetToSum">(new_array.Get(i))); } } - rjni_test_helper("rJniObjectArray", 2, new_array); + rjni_test_helper.Call<"rJniObjectArray">(2, new_array); } } // extern "C" diff --git a/javatests/com/jnibind/android/class_loader_test_jni.cc b/javatests/com/jnibind/android/class_loader_test_jni.cc index 83f84975..fe6cfc0c 100644 --- a/javatests/com/jnibind/android/class_loader_test_jni.cc +++ b/javatests/com/jnibind/android/class_loader_test_jni.cc @@ -74,8 +74,8 @@ Java_com_jnibind_android_ClassLoaderTest_jniBuildNewObjectsFromClassLoader( LocalObject helper_obj = class_loader.BuildLocalObject(1, 2, 3); return LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", helper_obj) + kJvmWithCustomClassLoaderSupport>{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">(helper_obj) .Release(); } @@ -85,8 +85,8 @@ Java_com_jnibind_android_ClassLoaderTest_jniBuildNewObjectsFromDefaultClassLoade GlobalClassLoader class_loader{PromoteToGlobal{}, jclass_loader_obj}; LocalObject helper_obj{2, 3, 4}; - return LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", helper_obj) + return LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">(helper_obj) .Release(); } @@ -101,7 +101,7 @@ Java_com_jnibind_android_ClassLoaderTest_jniBuildsRemoteSubclassFromClassLoader( jint{value}); LocalObject - parent_obj{helper_obj("castToParent")}; + parent_obj{helper_obj.Call<"castToParent">()}; return parent_obj("getValue"); } diff --git a/javatests/com/jnibind/android/context_test_jni.cc b/javatests/com/jnibind/android/context_test_jni.cc index 6ae2de6e..420e3eb3 100644 --- a/javatests/com/jnibind/android/context_test_jni.cc +++ b/javatests/com/jnibind/android/context_test_jni.cc @@ -39,13 +39,14 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { } JNIEXPORT void JNICALL Java_com_jnibind_android_ContextTest_DoSetup(JNIEnv* env, - jclass) { + jclass) { jvm.reset(new jni::JvmRef{env}); } JNIEXPORT jlong JNICALL -Java_com_jnibind_android_ContextTest_CreateNativeContextObject(JNIEnv* env, jclass, - jint val) { +Java_com_jnibind_android_ContextTest_CreateNativeContextObject(JNIEnv* env, + jclass, + jint val) { auto* ctx_struct = new ContextStruct{GlobalObject{}}; ctx_struct->obj["intVal1"].Set(jint{val}); @@ -57,25 +58,28 @@ Java_com_jnibind_android_ContextTest_CreateNativeContextObjectSetValToSum( JNIEnv* env, jclass, jint val1, jint val2) { // Creates a temporary test helper, calls its member method, and releases the // returned object across the C API boundary (then destroys the temporary). - return jni::LocalObject{}( - "returnNewObjectWithFieldSetToSum", val1, val2) + return jni::LocalObject{} + .Call<"returnNewObjectWithFieldSetToSum">(val1, val2) .Release(); } -JNIEXPORT jobject JNICALL Java_com_jnibind_android_ContextTest_QueryNativeObject( - JNIEnv* env, jclass, void* ctx_void_ptr) { +JNIEXPORT jobject JNICALL +Java_com_jnibind_android_ContextTest_QueryNativeObject(JNIEnv* env, jclass, + void* ctx_void_ptr) { ContextStruct* ctx_struct = static_cast(ctx_void_ptr); return jobject{ctx_struct->obj}; } -JNIEXPORT jobject JNICALL Java_com_jnibind_android_ContextTest_ExtractNativeObject( - JNIEnv* env, jclass, void* ctx_void_ptr) { +JNIEXPORT jobject JNICALL +Java_com_jnibind_android_ContextTest_ExtractNativeObject(JNIEnv* env, jclass, + void* ctx_void_ptr) { ContextStruct* ctx_struct = static_cast(ctx_void_ptr); return ctx_struct->obj.Release(); } -JNIEXPORT void JNICALL Java_com_jnibind_android_ContextTest_DestroyNativeContext( - JNIEnv* env, jclass, void* ctx_void_ptr) { +JNIEXPORT void JNICALL +Java_com_jnibind_android_ContextTest_DestroyNativeContext(JNIEnv* env, jclass, + void* ctx_void_ptr) { delete static_cast(ctx_void_ptr); } diff --git a/javatests/com/jnibind/android/field_test_jni.cc b/javatests/com/jnibind/android/field_test_jni.cc index 0b7fcc0a..569b5672 100644 --- a/javatests/com/jnibind/android/field_test_jni.cc +++ b/javatests/com/jnibind/android/field_test_jni.cc @@ -42,22 +42,22 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { JNIEXPORT jint JNICALL Java_com_jnibind_android_FieldTest_jniIntField( JNIEnv* env, jclass, jobject object, jint val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["intField"].Set(jint{val}); - return rjni_test_helper["intField"].Get(); + rjni_test_helper.Access<"intField">().Set(jint{val}); + return rjni_test_helper.Access<"intField">().Get(); } JNIEXPORT jfloat JNICALL Java_com_jnibind_android_FieldTest_jniFloatField( JNIEnv* env, jclass, jobject object, jfloat val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["floatField"].Set(jfloat{val}); - return rjni_test_helper["floatField"].Get(); + rjni_test_helper.Access<"floatField">().Set(jfloat{val}); + return rjni_test_helper.Access<"floatField">().Get(); } JNIEXPORT jdouble JNICALL Java_com_jnibind_android_FieldTest_jniDoubleField( JNIEnv* env, jclass, jobject object, jdouble val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["doubleField"].Set(jdouble{val}); - return rjni_test_helper["doubleField"].Get(); + rjni_test_helper.Access<"doubleField">().Set(jdouble{val}); + return rjni_test_helper.Access<"doubleField">().Get(); } } // extern "C" diff --git a/javatests/com/jnibind/android/global_object_test_jni.cc b/javatests/com/jnibind/android/global_object_test_jni.cc index aa296ff1..6808682d 100644 --- a/javatests/com/jnibind/android/global_object_test_jni.cc +++ b/javatests/com/jnibind/android/global_object_test_jni.cc @@ -32,7 +32,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { } JNIEXPORT jobject JNICALL -Java_com_jnibind_android_GlobalObjectTest_jniCreateNewObject(JNIEnv* env, jclass) { +Java_com_jnibind_android_GlobalObjectTest_jniCreateNewObject(JNIEnv* env, + jclass) { return jni::GlobalObject{1, 2, 3}.Release(); } @@ -46,8 +47,8 @@ JNIEXPORT jobject JNICALL Java_com_jnibind_android_GlobalObjectTest_jniBuildNewObjectsFromExistingObjects( JNIEnv* env, jclass, jobject test_helper_object, jobject object_to_mutate) { jni::LocalObject helper_obj{object_to_mutate}; - return jni::LocalObject{test_helper_object}( - "methodTakesGlobalObjectReturnsNewObject", helper_obj) + return jni::LocalObject{test_helper_object} + .Call<"methodTakesGlobalObjectReturnsNewObject">(helper_obj) .Release(); } @@ -55,17 +56,17 @@ JNIEXPORT jobject JNICALL Java_com_jnibind_android_GlobalObjectTest_jniManipulateNewGlobalObjectSetIntVal238( JNIEnv* env, jclass, jobject jtest_obj) { jni::GlobalObject helper_obj{2, 3, 8}; - return jni::LocalObject{jtest_obj}( - "methodTakesGlobalObjectReturnsNewObject", std::move(helper_obj)) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesGlobalObjectReturnsNewObject">(std::move(helper_obj)) .Release(); } JNIEXPORT jobject JNICALL Java_com_jnibind_android_GlobalObjectTest_jniMaterializeNewGlobalObjectSetIntVal159( JNIEnv* env, jclass, jobject jtest_obj) { - return jni::LocalObject{jtest_obj}( - "methodTakesGlobalObjectReturnsNewObject", - jni::GlobalObject{1, 5, 9}) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesGlobalObjectReturnsNewObject">( + jni::GlobalObject{1, 5, 9}) .Release(); } diff --git a/javatests/com/jnibind/android/local_object_test_jni.cc b/javatests/com/jnibind/android/local_object_test_jni.cc index 0ce67f13..9897466f 100644 --- a/javatests/com/jnibind/android/local_object_test_jni.cc +++ b/javatests/com/jnibind/android/local_object_test_jni.cc @@ -32,7 +32,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { } JNIEXPORT jobject JNICALL -Java_com_jnibind_android_LocalObjectTest_jniCreateNewObject(JNIEnv* env, jclass) { +Java_com_jnibind_android_LocalObjectTest_jniCreateNewObject(JNIEnv* env, + jclass) { return jni::LocalObject{1, 2, 3}.Release(); } @@ -53,8 +54,8 @@ JNIEXPORT jobject JNICALL Java_com_jnibind_android_LocalObjectTest_jniBuildNewObjectsFromExistingObjects( JNIEnv* env, jclass, jobject jtest_obj, jobject jhelper_obj) { jni::LocalObject helper_obj{jhelper_obj}; - return jni::LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", helper_obj) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">(helper_obj) .Release(); } @@ -62,17 +63,17 @@ JNIEXPORT jobject JNICALL Java_com_jnibind_android_LocalObjectTest_jniManipulateNewLocalObjectSetIntVal238( JNIEnv* env, jclass, jobject jtest_obj) { jni::LocalObject helper_obj{2, 3, 8}; - return jni::LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", std::move(helper_obj)) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">(std::move(helper_obj)) .Release(); } JNIEXPORT jobject JNICALL Java_com_jnibind_android_LocalObjectTest_jniMaterializeNewLocalObjectSetIntVal159( JNIEnv* env, jclass, jobject jtest_obj) { - return jni::LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", - jni::LocalObject{1, 5, 9}) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">( + jni::LocalObject{1, 5, 9}) .Release(); } diff --git a/javatests/com/jnibind/android/method_test_jni.cc b/javatests/com/jnibind/android/method_test_jni.cc index b558a0a1..d4337188 100644 --- a/javatests/com/jnibind/android/method_test_jni.cc +++ b/javatests/com/jnibind/android/method_test_jni.cc @@ -78,42 +78,39 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { JNIEXPORT void JNICALL Java_com_jnibind_android_MethodTest_jniVoidMethod( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - rjni_test_helper("voidMethod"); + rjni_test_helper.Call<"voidMethod">(); } JNIEXPORT void JNICALL -Java_com_jnibind_android_MethodTest_jniVoidMethodTakesOneInt(JNIEnv* env, jclass, - jobject object, - jint i) { +Java_com_jnibind_android_MethodTest_jniVoidMethodTakesOneInt(JNIEnv* env, + jclass, + jobject object, + jint i) { LocalObject rjni_test_helper{object}; - rjni_test_helper("voidMethodTakesOneInt", jint{i}); + rjni_test_helper.Call<"voidMethodTakesOneInt">(jint{i}); } JNIEXPORT void JNICALL -Java_com_jnibind_android_MethodTest_jniVoidMethodTakesFiveInts(JNIEnv* env, jclass, - jobject object, - jint i1, jint i2, - jint i3, jint i4, - jint i5) { +Java_com_jnibind_android_MethodTest_jniVoidMethodTakesFiveInts( + JNIEnv* env, jclass, jobject object, jint i1, jint i2, jint i3, jint i4, + jint i5) { LocalObject rjni_test_helper{object}; - rjni_test_helper("voidMethodTakesFiveInts", jint{i1}, jint{i2}, jint{i3}, - jint{i4}, jint{i5}); + rjni_test_helper.Call<"voidMethodTakesFiveInts">(jint{i1}, jint{i2}, jint{i3}, + jint{i4}, jint{i5}); } /** Boolean Method Tests. */ JNIEXPORT jboolean JNICALL Java_com_jnibind_android_MethodTest_jniBooleanMethod( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("booleanMethod"); + return rjni_test_helper.Call<"booleanMethod">(); } JNIEXPORT jboolean JNICALL -Java_com_jnibind_android_MethodTest_jniBooleanMethodTakesOneBoolean(JNIEnv* env, - jclass, - jobject object, - jboolean i) { +Java_com_jnibind_android_MethodTest_jniBooleanMethodTakesOneBoolean( + JNIEnv* env, jclass, jobject object, jboolean i) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("booleanMethodTakesOneBoolean", jboolean{i}); + return rjni_test_helper.Call<"booleanMethodTakesOneBoolean">(jboolean{i}); } JNIEXPORT jboolean JNICALL @@ -121,48 +118,48 @@ Java_com_jnibind_android_MethodTest_jniBooleanMethodTakesFiveBooleans( JNIEnv* env, jclass, jobject object, jboolean i1, jboolean i2, jboolean i3, jboolean i4, jboolean i5) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("booleanMethodTakesFiveBooleans", jboolean{i1}, - jboolean{i2}, jboolean{i3}, jboolean{i4}, - jboolean{i5}); + return rjni_test_helper.Call<"booleanMethodTakesFiveBooleans">( + jboolean{i1}, jboolean{i2}, jboolean{i3}, jboolean{i4}, jboolean{i5}); } /** Int Method Tests. */ JNIEXPORT jint JNICALL Java_com_jnibind_android_MethodTest_jniIntMethod( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("intMethod"); + return rjni_test_helper.Call<"intMethod">(); } -JNIEXPORT jint JNICALL Java_com_jnibind_android_MethodTest_jniIntMethodTakesOneInt( - JNIEnv* env, jclass, jobject object, jint i) { +JNIEXPORT jint JNICALL +Java_com_jnibind_android_MethodTest_jniIntMethodTakesOneInt(JNIEnv* env, jclass, + jobject object, + jint i) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("intMethodTakesOneInt", jint{i}); + return rjni_test_helper.Call<"intMethodTakesOneInt">(jint{i}); } JNIEXPORT jint JNICALL -Java_com_jnibind_android_MethodTest_jniIntMethodTakesFiveInts(JNIEnv* env, jclass, - jobject object, - jint i1, jint i2, - jint i3, jint i4, - jint i5) { +Java_com_jnibind_android_MethodTest_jniIntMethodTakesFiveInts( + JNIEnv* env, jclass, jobject object, jint i1, jint i2, jint i3, jint i4, + jint i5) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("intMethodTakesFiveInts", jint{i1}, jint{i2}, - jint{i3}, jint{i4}, jint{i5}); + return rjni_test_helper.Call<"intMethodTakesFiveInts">( + jint{i1}, jint{i2}, jint{i3}, jint{i4}, jint{i5}); } /** Long Method Tests. */ JNIEXPORT jlong JNICALL Java_com_jnibind_android_MethodTest_jniLongMethod( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("longMethod"); + return rjni_test_helper.Call<"longMethod">(); } JNIEXPORT jlong JNICALL -Java_com_jnibind_android_MethodTest_jniLongMethodTakesOneLong(JNIEnv* env, jclass, - jobject object, - jlong i) { +Java_com_jnibind_android_MethodTest_jniLongMethodTakesOneLong(JNIEnv* env, + jclass, + jobject object, + jlong i) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("longMethodTakesOneLong", jlong{i}); + return rjni_test_helper.Call<"longMethodTakesOneLong">(jlong{i}); } JNIEXPORT jlong JNICALL @@ -170,24 +167,24 @@ Java_com_jnibind_android_MethodTest_jniLongMethodTakesFiveLongs( JNIEnv* env, jclass, jobject object, jlong i1, jlong i2, jlong i3, jlong i4, jlong i5) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("longMethodTakesFiveLongs", jlong{i1}, jlong{i2}, - jlong{i3}, jlong{i4}, jlong{i5}); + return rjni_test_helper.Call<"longMethodTakesFiveLongs">( + jlong{i1}, jlong{i2}, jlong{i3}, jlong{i4}, jlong{i5}); } /** Float Method Tests. */ JNIEXPORT jfloat JNICALL Java_com_jnibind_android_MethodTest_jniFloatMethod( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("floatMethod"); + return rjni_test_helper.Call<"floatMethod">(); } JNIEXPORT jfloat JNICALL Java_com_jnibind_android_MethodTest_jniFloatMethodTakesOneFloat(JNIEnv* env, - jclass, - jobject object, - jfloat i) { + jclass, + jobject object, + jfloat i) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("floatMethodTakesOneFloat", jfloat{i}); + return rjni_test_helper.Call<"floatMethodTakesOneFloat">(jfloat{i}); } JNIEXPORT jfloat JNICALL @@ -195,24 +192,22 @@ Java_com_jnibind_android_MethodTest_jniFloatMethodTakesFiveFloats( JNIEnv* env, jclass, jobject object, jfloat i1, jfloat i2, jfloat i3, jfloat i4, jfloat i5) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("floatMethodTakesFiveFloats", jfloat{i1}, jfloat{i2}, - jfloat{i3}, jfloat{i4}, jfloat{i5}); + return rjni_test_helper.Call<"floatMethodTakesFiveFloats">( + jfloat{i1}, jfloat{i2}, jfloat{i3}, jfloat{i4}, jfloat{i5}); } /** Double Method Tests. */ JNIEXPORT jdouble JNICALL Java_com_jnibind_android_MethodTest_jniDoubleMethod( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("doubleMethod"); + return rjni_test_helper.Call<"doubleMethod">(); } JNIEXPORT jdouble JNICALL -Java_com_jnibind_android_MethodTest_jniDoubleMethodTakesOneDouble(JNIEnv* env, - jclass, - jobject object, - jdouble i) { +Java_com_jnibind_android_MethodTest_jniDoubleMethodTakesOneDouble( + JNIEnv* env, jclass, jobject object, jdouble i) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("doubleMethodTakesOneDouble", jdouble{i}); + return rjni_test_helper.Call<"doubleMethodTakesOneDouble">(jdouble{i}); } JNIEXPORT jdouble JNICALL @@ -220,27 +215,27 @@ Java_com_jnibind_android_MethodTest_jniDoubleMethodTakesFiveDoubles( JNIEnv* env, jclass, jobject object, jdouble i1, jdouble i2, jdouble i3, jdouble i4, jdouble i5) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("doubleMethodTakesFiveDoubles", jdouble{i1}, - jdouble{i2}, jdouble{i3}, jdouble{i4}, jdouble{i5}); + return rjni_test_helper.Call<"doubleMethodTakesFiveDoubles">( + jdouble{i1}, jdouble{i2}, jdouble{i3}, jdouble{i4}, jdouble{i5}); } /** Overload Method Tests. */ JNIEXPORT int JNICALL Java_com_jnibind_android_MethodTest_jniCallFooOverload1( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("foo"); + return rjni_test_helper.Call<"foo">(); } JNIEXPORT int JNICALL Java_com_jnibind_android_MethodTest_jniCallFooOverload2( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("foo", 123.f); + return rjni_test_helper.Call<"foo">(123.f); } JNIEXPORT int JNICALL Java_com_jnibind_android_MethodTest_jniCallFooOverload3( JNIEnv* env, jclass, jobject object) { LocalObject rjni_test_helper{object}; - return rjni_test_helper("foo", 123.f, 456.f); + return rjni_test_helper.Call<"foo">(123.f, 456.f); } } // extern "C" diff --git a/javatests/com/jnibind/android/string_test_jni.cc b/javatests/com/jnibind/android/string_test_jni.cc index b75ae007..e440eed2 100644 --- a/javatests/com/jnibind/android/string_test_jni.cc +++ b/javatests/com/jnibind/android/string_test_jni.cc @@ -51,9 +51,10 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { /** Void return type tests. */ JNIEXPORT void JNICALL -Java_com_jnibind_android_StringTest_jniVoidMethodTakesString(JNIEnv* env, jclass, - jobject object, - jstring string) { +Java_com_jnibind_android_StringTest_jniVoidMethodTakesString(JNIEnv* env, + jclass, + jobject object, + jstring string) { LocalObject r_jni_string_test_helper{object}; // TODO(b/175083373): The following (and below) should compile with overload // sets. @@ -62,17 +63,17 @@ Java_com_jnibind_android_StringTest_jniVoidMethodTakesString(JNIEnv* env, jclass r_jni_string_test_helper.GetMethod("voidMethodTakesString", std::string{LocalString{string}.Pin().ToString()}); */ - r_jni_string_test_helper("voidMethodTakesString", - std::string{LocalString{string}.Pin().ToString()}); + r_jni_string_test_helper.Call<"voidMethodTakesString">( + std::string{LocalString{string}.Pin().ToString()}); } JNIEXPORT void JNICALL Java_com_jnibind_android_StringTest_jniVoidMethodTakesTwoStrings( JNIEnv* env, jclass, jobject object, jstring s1, jstring s2) { LocalObject r_jni_string_test_helper{object}; - r_jni_string_test_helper("voidMethodTakesTwoStrings", - std::string{LocalString{s1}.Pin().ToString()}, - std::string{LocalString{s2}.Pin().ToString()}); + r_jni_string_test_helper.Call<"voidMethodTakesTwoStrings">( + std::string{LocalString{s1}.Pin().ToString()}, + std::string{LocalString{s2}.Pin().ToString()}); } JNIEXPORT void JNICALL @@ -80,26 +81,26 @@ Java_com_jnibind_android_StringTest_jniVoidMethodTakesFiveStrings( JNIEnv* env, jclass, jobject object, jstring s1, jstring s2, jstring s3, jstring s4, jstring s5) { LocalObject r_jni_string_test_helper{object}; - r_jni_string_test_helper("voidMethodTakesFiveStrings", - std::string{LocalString{s1}.Pin().ToString()}, - std::string{LocalString{s2}.Pin().ToString()}, - std::string{LocalString{s3}.Pin().ToString()}, - std::string{LocalString{s4}.Pin().ToString()}, - std::string{LocalString{s5}.Pin().ToString()}); + r_jni_string_test_helper.Call<"voidMethodTakesFiveStrings">( + std::string{LocalString{s1}.Pin().ToString()}, + std::string{LocalString{s2}.Pin().ToString()}, + std::string{LocalString{s3}.Pin().ToString()}, + std::string{LocalString{s4}.Pin().ToString()}, + std::string{LocalString{s5}.Pin().ToString()}); } /** String return type tests. */ JNIEXPORT jstring JNICALL -Java_com_jnibind_android_StringTest_jniStringMethodTakesString(JNIEnv* env, jclass, - jobject object, - jstring string) { +Java_com_jnibind_android_StringTest_jniStringMethodTakesString(JNIEnv* env, + jclass, + jobject object, + jstring string) { LocalObject r_jni_string_test_helper{object}; - // TODO(b/174272629): This declaration is clumsy because Return is - // an ObjectRef and thus not implicitly convertible to jstring. The following - // needs to be simpler to express. - return LocalString{r_jni_string_test_helper( - "stringMethodTakesString", + // TODO(b/174272629): This declaration is clumsy because Return + // is an ObjectRef and thus not implicitly convertible to jstring. The + // following needs to be simpler to express. + return LocalString{r_jni_string_test_helper.Call<"stringMethodTakesString">( std::string{LocalString{string}.Pin().ToString()})} .Release(); } @@ -109,9 +110,9 @@ Java_com_jnibind_android_StringTest_jniStringMethodTakesTwoStrings( JNIEnv* env, jclass, jobject object, jstring s1, jstring s2) { LocalObject r_jni_string_test_helper{object}; return LocalString{ - r_jni_string_test_helper("stringMethodTakesTwoStrings", - std::string{LocalString{s1}.Pin().ToString()}, - std::string{LocalString{s2}.Pin().ToString()})} + r_jni_string_test_helper.Call<"stringMethodTakesTwoStrings">( + std::string{LocalString{s1}.Pin().ToString()}, + std::string{LocalString{s2}.Pin().ToString()})} .Release(); } @@ -121,12 +122,12 @@ Java_com_jnibind_android_StringTest_jniStringMethodTakesFiveStrings( jstring s4, jstring s5) { LocalObject r_jni_string_test_helper{object}; return LocalString{ - r_jni_string_test_helper("stringMethodTakesFiveStrings", - std::string{LocalString{s1}.Pin().ToString()}, - std::string{LocalString{s2}.Pin().ToString()}, - std::string{LocalString{s3}.Pin().ToString()}, - std::string{LocalString{s4}.Pin().ToString()}, - std::string{LocalString{s5}.Pin().ToString()})} + r_jni_string_test_helper.Call<"stringMethodTakesFiveStrings">( + std::string{LocalString{s1}.Pin().ToString()}, + std::string{LocalString{s2}.Pin().ToString()}, + std::string{LocalString{s3}.Pin().ToString()}, + std::string{LocalString{s4}.Pin().ToString()}, + std::string{LocalString{s5}.Pin().ToString()})} .Release(); } diff --git a/javatests/com/jnibind/android/thread_test_jni.cc b/javatests/com/jnibind/android/thread_test_jni.cc index 992db5ba..a5708fc8 100644 --- a/javatests/com/jnibind/android/thread_test_jni.cc +++ b/javatests/com/jnibind/android/thread_test_jni.cc @@ -62,7 +62,7 @@ Java_com_jnibind_android_ThreadTest_runsThreadedWorkOnObject(JNIEnv* env, ThreadGuard thread_guard{}; // This will crash without fallback loader (`jclass` will be unknown). - fixture["objectTestHelper"].Get()("foo"); + fixture["objectTestHelper"].Get().Call<"foo">(); }}; worker.join(); diff --git a/javatests/com/jnibind/test/BUILD b/javatests/com/jnibind/test/BUILD index 8af8d7f6..1f95abc6 100644 --- a/javatests/com/jnibind/test/BUILD +++ b/javatests/com/jnibind/test/BUILD @@ -53,6 +53,7 @@ cc_library( ":object_test_helper_jni", "//:jni_bind", "//metaprogramming:lambda_string", + "//metaprogramming:string_literal", ], alwayslink = True, ) diff --git a/javatests/com/jnibind/test/array_test_field_rank_1_jni.cc b/javatests/com/jnibind/test/array_test_field_rank_1_jni.cc index 21abf4c9..82def753 100644 --- a/javatests/com/jnibind/test/array_test_field_rank_1_jni.cc +++ b/javatests/com/jnibind/test/array_test_field_rank_1_jni.cc @@ -33,6 +33,7 @@ using ::jni::LocalArray; using ::jni::LocalObject; using ::jni::LocalString; using ::jni::StaticRef; +using ::jni::metaprogramming::StringLiteral; static std::unique_ptr> jvm; @@ -58,17 +59,14 @@ static constexpr Class kArrayTestFieldRank1 { // Generic field test suitable for simple primitive types. // Strings are passed through lambdas as field indexing is compile time. -template +template void GenericFieldTest(LocalObject fixture, - SpanType max_val, SpanType base, SpanType stride, - FieldNameLambda field_name_lambda, - MethodNameLambda method_name_lambda) { + SpanType max_val, SpanType base, SpanType stride) { // Field starts in default state. - LocalArray arr{fixture[field_name_lambda()].Get()}; - StaticRef{}(method_name_lambda(), SpanType{0}, - SpanType{0}, - fixture[field_name_lambda()].Get()); + LocalArray arr{fixture.Access().Get()}; + StaticRef{}.Call( + SpanType{0}, SpanType{0}, fixture.Access().Get()); // Updating the field manually works. { @@ -78,9 +76,8 @@ void GenericFieldTest(LocalObject fixture, } } - StaticRef{}(method_name_lambda(), SpanType{0}, - SpanType{1}, - fixture[field_name_lambda()].Get()); + StaticRef{}.Call( + SpanType{0}, SpanType{1}, fixture.Access().Get()); // Updating the field repeatedly works. { @@ -89,13 +86,15 @@ void GenericFieldTest(LocalObject fixture, pin.ptr()[i] = base + static_cast(stride * i); } } - StaticRef{}(method_name_lambda(), base, stride, - fixture[field_name_lambda()].Get()); + StaticRef{}.Call( + base, stride, fixture.Access().Get()); // Not pinning the value has no effect. - { std::memset(arr.Pin(false).ptr(), 0, arr.Length() * sizeof(SpanType)); } - StaticRef{}(method_name_lambda(), base, stride, - fixture[field_name_lambda()].Get()); + { + std::memset(arr.Pin(false).ptr(), 0, arr.Length() * sizeof(SpanType)); + } + StaticRef{}.Call( + base, stride, fixture.Access().Get()); } extern "C" { @@ -113,64 +112,64 @@ JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_jniTearDown( JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeBooleanTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - jboolean{true}, jboolean{0}, jboolean{true}, - STR("booleanArrayField"), STR("assertBoolean1D")); + GenericFieldTest( + LocalObject{test_fixture}, jboolean{true}, + jboolean{0}, jboolean{true}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeByteTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jbyte{10}, jbyte{1}, - STR("byteArrayField"), STR("assertByte1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jbyte{10}, jbyte{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeCharTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jchar{10}, jchar{1}, - STR("charArrayField"), STR("assertChar1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jchar{10}, jchar{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeShortTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jshort{10}, jshort{1}, - STR("shortArrayField"), STR("assertShort1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jshort{10}, jshort{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeIntTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jint{10}, jint{1}, - STR("intArrayField"), STR("assertInt1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jint{10}, jint{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeLongTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jlong{10}, jlong{1}, - STR("longArrayField"), STR("assertLong1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jlong{10}, jlong{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeFloatTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jfloat{10.f}, - jfloat{1.f}, STR("floatArrayField"), STR("assertFloat1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jfloat{10.f}, jfloat{1.f}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank1_nativeDoubleTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jdouble{10}, jdouble{1}, - STR("doubleArrayField"), STR("assertDouble1D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jdouble{10}, jdouble{1}); } JNIEXPORT void JNICALL @@ -179,41 +178,44 @@ Java_com_jnibind_test_ArrayTestFieldRank1_nativeStringTests( LocalObject fixture{test_fixture}; // Field starts in default state. - StaticRef{}( - "assertString1D", fixture["stringArrayField"].Get(), jboolean{false}); + StaticRef{}("assertString1D", + fixture.Access<"stringArrayField">().Get(), + jboolean{false}); // Updating the field manually works. - LocalArray arr = fixture["stringArrayField"].Get(); + LocalArray arr = fixture.Access<"stringArrayField">().Get(); arr.Set(0, LocalString{"Foo"}); arr.Set(1, LocalString{"Baz"}); arr.Set(2, LocalString{"Bar"}); - StaticRef{}("assertString1D", arr, true); + StaticRef{}.Call<"assertString1D">(arr, true); // Updating the field repeatedly works. arr.Set(0, LocalString{"FAKE"}); arr.Set(1, LocalString{"DEAD"}); arr.Set(2, LocalString{"BEEF"}); - StaticRef{}("assertString1D", arr, false); + StaticRef{}.Call<"assertString1D">(arr, false); // Updating the field repeatedly works. arr.Set(0, "Foo"); const char* kBaz = "Baz"; arr.Set(1, kBaz); arr.Set(2, std::string{"Bar"}); - StaticRef{}("assertString1D", arr, true); + StaticRef{}.Call<"assertString1D">(arr, true); // Iterating over values works. static constexpr std::array vals{"Foo", "Baz", "Bar"}; int i = 0; for (LocalString local_string : arr.Pin()) { - StaticRef{}("assertString", local_string, vals[i]); + StaticRef{}.Call<"assertString">(local_string, + vals[i]); ++i; } // Iterating over passed values works. i = 0; for (LocalString local_string : LocalArray{object_array}.Pin()) { - StaticRef{}("assertString", local_string, vals[i]); + StaticRef{}.Call<"assertString">(local_string, + vals[i]); ++i; } } @@ -223,12 +225,12 @@ Java_com_jnibind_test_ArrayTestFieldRank1_nativeObjectTests( JNIEnv* env, jclass, jobject test_fixture, jobjectArray) { LocalObject fixture{test_fixture}; LocalArray arr = - fixture["objectArrayField"].Get(); + fixture.Access<"objectArrayField">().Get(); int i = 0; for (LocalObject new_obj : fixture["objectArrayField"].Get().Pin()) { - StaticRef{}("assertObject", i, new_obj); + StaticRef{}.Call<"assertObject">(i, new_obj); i++; } } diff --git a/javatests/com/jnibind/test/array_test_field_rank_2_jni.cc b/javatests/com/jnibind/test/array_test_field_rank_2_jni.cc index a645e6ef..d27d2f5f 100644 --- a/javatests/com/jnibind/test/array_test_field_rank_2_jni.cc +++ b/javatests/com/jnibind/test/array_test_field_rank_2_jni.cc @@ -23,6 +23,7 @@ #include "object_test_helper_jni.h" #include "jni_bind.h" #include "metaprogramming/lambda_string.h" +#include "metaprogramming/string_literal.h" using ::jni::Array; using ::jni::ArrayView; @@ -33,6 +34,7 @@ using ::jni::LocalObject; using ::jni::Modulo; using ::jni::Rank; using ::jni::StaticRef; +using ::jni::metaprogramming::StringLiteral; static std::unique_ptr> jvm; @@ -58,18 +60,15 @@ static constexpr Class kArrayTestFieldRank2 { // Generic field test suitable for simple primitive types. // Strings are passed through lambdas as field indexing is compile time. -template +template void GenericFieldTest(LocalObject fixture, - SpanType max_val, SpanType base, SpanType stride, - FieldNameLambda field_name_lambda, - MethodNameLambda method_name_lambda) { - LocalArray arr{fixture[field_name_lambda()].Get()}; + SpanType max_val, SpanType base, SpanType stride) { + LocalArray arr{fixture.Access().Get()}; // Field starts in default state. - StaticRef{}(method_name_lambda(), SpanType{0}, - SpanType{0}, - fixture[field_name_lambda()].Get()); + StaticRef{}.Call( + SpanType{0}, SpanType{0}, fixture.Access().Get()); // Updating the field manually works. { @@ -82,8 +81,8 @@ void GenericFieldTest(LocalObject fixture, } } - StaticRef{}(method_name_lambda(), base, SpanType{1}, - fixture[field_name_lambda()].Get()); + StaticRef{}.Call( + base, SpanType{1}, fixture.Access().Get()); } // Updating the field repeatedly works. @@ -101,8 +100,8 @@ void GenericFieldTest(LocalObject fixture, } } } - StaticRef{}(method_name_lambda(), base, stride, - fixture[field_name_lambda()].Get()); + StaticRef{}.Call( + base, stride, fixture.Access().Get()); } } @@ -113,8 +112,8 @@ void GenericFieldTest(LocalObject fixture, arr_rank_1.Length() * sizeof(SpanType)); } } - StaticRef{}(method_name_lambda(), base, stride, - fixture[field_name_lambda()].Get()); + StaticRef{}.Call( + base, stride, fixture.Access().Get()); } extern "C" { @@ -132,68 +131,68 @@ JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_jniTearDown( JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeBooleanTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, jboolean{2}, - jboolean{0}, jboolean{1}, STR("booleanArrayField"), - STR("assertBoolean2D")); + GenericFieldTest( + LocalObject{test_fixture}, jboolean{2}, jboolean{0}, + jboolean{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeByteTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jbyte{10}, jbyte{2}, - STR("byteArrayField"), STR("assertByte2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jbyte{10}, jbyte{2}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeCharTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jchar{0}, jchar{1}, - STR("charArrayField"), STR("assertChar2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jchar{0}, jchar{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeShortTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jshort{0}, jshort{1}, - STR("shortArrayField"), STR("assertShort2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jshort{0}, jshort{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeIntTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jint{0}, jint{1}, - STR("intArrayField"), STR("assertInt2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jint{0}, jint{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeLongTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jlong{0}, jlong{1}, - STR("longArrayField"), STR("assertLong2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jlong{0}, jlong{1}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeFloatTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jfloat{0.f}, jfloat{1.f}, - STR("floatArrayField"), STR("assertFloat2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jfloat{0.f}, jfloat{1.f}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeDoubleTests( JNIEnv* env, jclass, jobject test_fixture) { - GenericFieldTest(LocalObject{test_fixture}, - std::numeric_limits::max(), jdouble{0}, jdouble{1}, - STR("doubleArrayField"), STR("assertDouble2D")); + GenericFieldTest( + LocalObject{test_fixture}, + std::numeric_limits::max(), jdouble{0}, jdouble{1}); } // TODO(b/143908983): This is broken, but using regular `jobject` works, -// so there is still an alternative path to be take. +// so there is still an alternative path to be taken. JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestFieldRank2_nativeStringTests( JNIEnv* env, jclass, jobject test_fixture) { @@ -202,10 +201,11 @@ Java_com_jnibind_test_ArrayTestFieldRank2_nativeStringTests( // Field starts in default state. StaticRef{}( - "assertString1D", fixture["stringArrayField"].Get(), jboolean{false}); + "assertString1D", fixture.Access<"stringArrayField">().Get(), + jboolean{false}); // Updating the field manually works. - LocalArray arr = fixture["stringArrayField"].Get(); + LocalArray arr = fixture.Access<"stringArrayField">().Get(); arr.Set(0, LocalString{"Foo"}); arr.Set(1, LocalString{"Baz"}); arr.Set(2, LocalString{"Bar"}); @@ -245,29 +245,29 @@ void Java_com_jnibind_test_ArrayTestFieldRank2_nativeObjectTests( JNIEnv* env, jclass, jobject test_fixture) { LocalObject fixture{test_fixture}; LocalArray arr = - fixture["objectArrayField"].Get(); + fixture.Access<"objectArrayField">().Get(); int i = 0; { for (LocalArray arr_rank_1 : arr.Pin()) { for (LocalObject obj : arr_rank_1.Pin()) { - obj["intVal1"].Set(i); + obj.Access<"intVal1">().Set(i); i++; - obj["intVal2"].Set(i); + obj.Access<"intVal2">().Set(i); i++; - obj["intVal3"].Set(i); + obj.Access<"intVal3">().Set(i); i++; } } } // Object on fixture is already updated. - StaticRef{}("assertObject2D", 0, 1, - fixture["objectArrayField"].Get()); + StaticRef{}.Call<"assertObject2D">( + 0, 1, fixture.Access<"objectArrayField">().Get()); // But using local reference works too. - StaticRef{}("assertObject2D", 0, 1, arr); + StaticRef{}.Call<"assertObject2D">(0, 1, arr); } } // extern "C" diff --git a/javatests/com/jnibind/test/array_test_method_rank_1_jni.cc b/javatests/com/jnibind/test/array_test_method_rank_1_jni.cc index 42d85212..c664db59 100644 --- a/javatests/com/jnibind/test/array_test_method_rank_1_jni.cc +++ b/javatests/com/jnibind/test/array_test_method_rank_1_jni.cc @@ -21,7 +21,6 @@ #include "array_test_helpers_native.h" #include "object_test_helper_jni.h" #include "jni_bind.h" -#include "metaprogramming/lambda_string.h" using ::jni::ArrayView; using ::jni::LocalArray; @@ -29,21 +28,21 @@ using ::jni::LocalObject; using ::jni::LocalString; using ::jni::RegularToArrayTypeMap_t; using ::jni::StaticRef; +using ::jni::metaprogramming::StringLiteral; static std::unique_ptr> jvm; // Generic method test suitable for simple primitive types. // Strings are passed through lambdas as method indexing is compile time. -template -void GenericMethodTest(LocalArray local_arr, SpanType base, - MethodNameLambda method_name_lambda) { +template +void GenericMethodTest(LocalArray local_arr, SpanType base) { // Simple lvalue pass through works as expected. - StaticRef{}(method_name_lambda(), base, SpanType{1}, - local_arr); + StaticRef{}.Call( + base, SpanType{1}, local_arr); // Simple rvalue pass through works as expected. - StaticRef{}(method_name_lambda(), base, SpanType{1}, - std::move(local_arr)); + StaticRef{}.Call( + base, SpanType{1}, std::move(local_arr)); // Building a new array, and setting all the values by hand works. LocalArray new_array{8}; @@ -53,8 +52,8 @@ void GenericMethodTest(LocalArray local_arr, SpanType base, array_view.ptr()[i] = base + static_cast(i); } } - StaticRef{}(method_name_lambda(), base, SpanType{1}, - new_array); + StaticRef{}.Call( + base, SpanType{1}, new_array); // You can pull the view multiple times. { @@ -72,8 +71,8 @@ void GenericMethodTest(LocalArray local_arr, SpanType base, val = base + i; i++; } - StaticRef{}(method_name_lambda(), base, SpanType{1}, - new_array); + StaticRef{}.Call( + base, SpanType{1}, new_array); // You can build an array of null values and set the values manually. LocalArray arr_built_from_null{5}; @@ -84,8 +83,8 @@ void GenericMethodTest(LocalArray local_arr, SpanType base, val = base + j; j++; } - StaticRef{}(method_name_lambda(), base, SpanType{1}, - new_array); + StaticRef{}.Call( + base, SpanType{1}, new_array); } extern "C" { @@ -103,57 +102,57 @@ JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_jniTearDown( JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeBooleanTests( JNIEnv* env, jclass, jbooleanArray boolean_array) { - GenericMethodTest(LocalArray{boolean_array}, jboolean{true}, - STR("assertBoolean1D")); + GenericMethodTest( + LocalArray{boolean_array}, jboolean{true}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeByteTests( JNIEnv* env, jclass, jbyteArray byte_array) { - GenericMethodTest(LocalArray{byte_array}, jbyte{0}, - STR("assertByte1D")); + GenericMethodTest(LocalArray{byte_array}, + jbyte{0}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeCharTests( JNIEnv* env, jclass, jcharArray char_array) { - GenericMethodTest(LocalArray{char_array}, jchar{0}, - STR("assertChar1D")); + GenericMethodTest(LocalArray{char_array}, + jchar{0}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeShortTests( JNIEnv* env, jclass, jshortArray short_array) { - GenericMethodTest(LocalArray{short_array}, jshort{0}, - STR("assertShort1D")); + GenericMethodTest(LocalArray{short_array}, + jshort{0}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeIntTests(JNIEnv* env, jclass, jintArray int_array) { - GenericMethodTest(LocalArray{int_array}, jint{0}, STR("assertInt1D")); + GenericMethodTest(LocalArray{int_array}, jint{0}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeLongTests( JNIEnv* env, jclass, jlongArray long_array) { - GenericMethodTest(LocalArray{long_array}, jlong{0}, - STR("assertLong1D")); + GenericMethodTest(LocalArray{long_array}, + jlong{0}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeFloatTests( JNIEnv* env, jclass, jfloatArray float_array) { - GenericMethodTest(LocalArray{float_array}, jfloat{0}, - STR("assertFloat1D")); + GenericMethodTest(LocalArray{float_array}, + jfloat{0}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank1_nativeDoubleTests( JNIEnv* env, jclass, jdoubleArray double_array) { - GenericMethodTest(LocalArray{double_array}, jdouble{0}, - STR("assertDouble1D")); + GenericMethodTest( + LocalArray{double_array}, jdouble{0}); } JNIEXPORT void JNICALL @@ -161,7 +160,7 @@ Java_com_jnibind_test_ArrayTestMethodRank1_nativeStringTests( JNIEnv* env, jclass, jobjectArray object_array) { // Simple lvalue pass through works as expected. LocalArray local_arr{object_array}; - StaticRef{}("assertString1D", local_arr, true); + StaticRef{}.Call<"assertString1D">(local_arr, true); // TODO(b/143908983): Currently not possible to write. // Simple rvalue pass through works as expected. @@ -177,7 +176,7 @@ Java_com_jnibind_test_ArrayTestMethodRank1_nativeStringTests( new_array.Set(0, LocalString{"Foo"}); new_array.Set(1, LocalString{"Baz"}); new_array.Set(2, LocalString{"Bar"}); - StaticRef{}("assertString1D", new_array, true); + StaticRef{}.Call<"assertString1D">(new_array, true); // And it can be iterated over. std::size_t i = 0; @@ -187,7 +186,8 @@ Java_com_jnibind_test_ArrayTestMethodRank1_nativeStringTests( i++; } - StaticRef{}("assertString1D", validator_array, true); + StaticRef{}.Call<"assertString1D">(validator_array, + true); } JNIEXPORT void JNICALL @@ -195,18 +195,17 @@ Java_com_jnibind_test_ArrayTestMethodRank1_nativeObjectTests( JNIEnv* env, jclass, jobjectArray object_array) { // Creating arrays of nulls with just size works. LocalArray local_arr_nulls{5}; - StaticRef{}("assertObjectArrayOfNulls1D", - local_arr_nulls); + StaticRef{}.Call<"assertObjectArrayOfNulls1D">( + local_arr_nulls); // Simple lvalue pass through works as expected. LocalArray local_arr{object_array}; - StaticRef{}("assertObject1D", 0, local_arr); + StaticRef{}.Call<"assertObject1D">(0, local_arr); // Simple rvalue pass through works as expected. - StaticRef{}( - "assertObject1D", 5, - LocalArray{ - 1, LocalObject{5, 5, 5}}); + StaticRef{}.Call<"assertObject1D">( + 5, LocalArray{ + 1, LocalObject{5, 5, 5}}); // Building a new array, and setting all the values by hand works. LocalObject obj{0, 0, 0}; @@ -217,14 +216,14 @@ Java_com_jnibind_test_ArrayTestMethodRank1_nativeObjectTests( i, LocalObject{jint{i}, jint{i}, jint{i}}); } } - StaticRef{}("assertObject1D", 0, new_array); + StaticRef{}.Call<"assertObject1D">(0, new_array); // You can pull the view multiple times. { for (int i = 0; i < new_array.Length(); ++i) { - new_array.Set(i, - LocalObject{2, 2, 2}( - "returnNewObjectWithFieldSetToSum", new_array.Get(i))); + new_array.Set( + i, LocalObject{2, 2, 2} + .Call<"returnNewObjectWithFieldSetToSum">(new_array.Get(i))); } } @@ -233,12 +232,12 @@ Java_com_jnibind_test_ArrayTestMethodRank1_nativeObjectTests( // is being iterated over, and upcast to a LocalObject. // Also note: Pin is not to contiguous memory, so no copyback bool either. for (LocalObject val : new_array.Pin()) { - val["intVal1"].Set(val["intVal1"].Get() + 3); - val["intVal2"].Set(val["intVal2"].Get() + 3); - val["intVal3"].Set(val["intVal3"].Get() + 3); + val.Access<"intVal1">().Set(val["intVal1"].Get() + 3); + val.Access<"intVal2">().Set(val["intVal2"].Get() + 3); + val.Access<"intVal3">().Set(val["intVal3"].Get() + 3); } - StaticRef{}("assertObject1D", 2 + 3, new_array); + StaticRef{}.Call<"assertObject1D">(2 + 3, new_array); } } // extern "C" diff --git a/javatests/com/jnibind/test/array_test_method_rank_2_jni.cc b/javatests/com/jnibind/test/array_test_method_rank_2_jni.cc index 6de4baa4..40d535f8 100644 --- a/javatests/com/jnibind/test/array_test_method_rank_2_jni.cc +++ b/javatests/com/jnibind/test/array_test_method_rank_2_jni.cc @@ -22,29 +22,29 @@ #include "modulo.h" #include "javatests/com/jnibind/test/object_test_helper_jni.h" #include "jni_bind.h" -#include "metaprogramming/lambda_string.h" using ::jni::LocalArray; using ::jni::LocalObject; using ::jni::Modulo; using ::jni::RegularToArrayTypeMap_t; using ::jni::StaticRef; +using ::jni::metaprogramming::StringLiteral; static std::unique_ptr> jvm; // Generic method test suitable for simple primitive types. // Strings are passed through lambdas as method indexing is compile time. -template +template void GenericMethodTest( - MethodNameLambda method_name_lambda, LocalArray arr, + LocalArray arr, SpanType max_val = std::numeric_limits::max()) { // Simple lvalue pass through works as expected. - StaticRef{}(method_name_lambda(), SpanType{0}, - SpanType{1}, arr); + StaticRef{}.Call( + SpanType{0}, SpanType{1}, arr); // Simple rvalue pass through works as expected. - StaticRef{}(method_name_lambda(), SpanType{0}, - SpanType{1}, std::move(arr)); + StaticRef{}.Call( + SpanType{0}, SpanType{1}, std::move(arr)); // Building a new array, and setting all the values by hand works. LocalArray new_array{3}; @@ -69,8 +69,8 @@ void GenericMethodTest( new_array.Set(1, row2); new_array.Set(2, row3); - StaticRef{}(method_name_lambda(), SpanType{0}, - SpanType{1}, new_array); + StaticRef{}.Call( + SpanType{0}, SpanType{1}, new_array); } // You can pull the view multiple times with iterators (each value ticked 1). @@ -80,8 +80,8 @@ void GenericMethodTest( val = Modulo(1, val, max_val); } } - StaticRef{}(method_name_lambda(), SpanType{1}, - SpanType{1}, new_array); + StaticRef{}.Call( + SpanType{1}, SpanType{1}, new_array); } // You can pull the view multiple times with raw loops. @@ -98,8 +98,8 @@ void GenericMethodTest( } // Each variant increments base by 1, so 2 is used here. - StaticRef{}( - method_name_lambda(), Modulo(2, {0}, max_val), SpanType{1}, new_array); + StaticRef{}.Call( + Modulo(2, {0}, max_val), SpanType{1}, new_array); } extern "C" { @@ -118,53 +118,53 @@ JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeBooleanTests2D( JNIEnv*, jclass, jobjectArray arr) { // Boolean has a max of "2" which makes the generic moduloing logic work. - GenericMethodTest(STR("assertBoolean2D"), LocalArray{arr}, - jboolean{2}); + GenericMethodTest(LocalArray{arr}, + jboolean{2}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeByteTests2D(JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertByte2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeCharTests2D(JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertChar2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeShortTests2D( JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertShort2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeIntTests2D(JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertInt2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeLongTests2D(JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertLong2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeFloatTests2D( JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertFloat2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL Java_com_jnibind_test_ArrayTestMethodRank2_nativeDoubleTests2D( JNIEnv*, jclass, jobjectArray arr) { - GenericMethodTest(STR("assertDouble2D"), LocalArray{arr}); + GenericMethodTest(LocalArray{arr}); } JNIEXPORT void JNICALL @@ -172,10 +172,11 @@ Java_com_jnibind_test_ArrayTestMethodRank2_nativeObjectTests2D( JNIEnv* env, jclass, jobjectArray arr_jobjectArray) { // Simple lvalue pass through works as expected. LocalArray arr{arr_jobjectArray}; - StaticRef{}("assertObject2D", 0, 1, arr); + StaticRef{}.Call<"assertObject2D">(0, 1, arr); // Simple rvalue pass through works as expected. - StaticRef{}("assertObject2D", 0, 1, std::move(arr)); + StaticRef{}.Call<"assertObject2D">(0, 1, + std::move(arr)); // Building a new array, and setting all the values by hand works. LocalArray new_array{3, nullptr}; @@ -199,18 +200,18 @@ Java_com_jnibind_test_ArrayTestMethodRank2_nativeObjectTests2D( new_array.Set(1, row2); new_array.Set(2, row3); - StaticRef{}("assertObject2D", 0, 1, new_array); + StaticRef{}.Call<"assertObject2D">(0, 1, new_array); // You can pull the view multiple times with iterators (each value ticked 1). { int i = 0; for (LocalArray inner_array : new_array.Pin()) { for (LocalObject obj : inner_array.Pin()) { - obj["intVal1"].Set(i); + obj.Access<"intVal1">().Set(i); i++; - obj["intVal2"].Set(i); + obj.Access<"intVal2">().Set(i); i++; - obj["intVal3"].Set(i); + obj.Access<"intVal3">().Set(i); i++; } } @@ -222,13 +223,13 @@ Java_com_jnibind_test_ArrayTestMethodRank2_nativeObjectTests2D( for (int i = 0; i < new_array.Length(); ++i) { LocalArray inn_arr{new_array.Get(i)}; for (int j = 0; j < inn_arr.Length(); ++j) { - inn_arr.Get(j)("increment", 1); + inn_arr.Get(j).Call<"increment">(1); } } } // Each variant increments base by 1, so 2 is used here. - StaticRef{}("assertObject2D", 1, 1, new_array); + StaticRef{}.Call<"assertObject2D">(1, 1, new_array); } } // extern "C" diff --git a/javatests/com/jnibind/test/builder_jni.cc b/javatests/com/jnibind/test/builder_jni.cc index a5719432..5f927494 100644 --- a/javatests/com/jnibind/test/builder_jni.cc +++ b/javatests/com/jnibind/test/builder_jni.cc @@ -56,8 +56,11 @@ JNI_BIND_EXPORT jint JNI_BIND_CALL JNI_OnLoad(JavaVM* pjvm, void* reserved) { JNI_MTHD(void, nativeJniTeardown) { jvm = nullptr; } JNI_MTHD(jobject, useBuilderToCreateObject) { - return LocalObject{}("setOne", 111)("setTwo", 222)("setThree", - 333)("build") + return LocalObject{} + .Call<"setOne">(111) + .Call<"setTwo">(222) + .Call<"setThree">(333) + .Call<"build">() .Release(); } diff --git a/javatests/com/jnibind/test/context_test_jni.cc b/javatests/com/jnibind/test/context_test_jni.cc index 0786406c..e7bc4db3 100644 --- a/javatests/com/jnibind/test/context_test_jni.cc +++ b/javatests/com/jnibind/test/context_test_jni.cc @@ -55,7 +55,7 @@ JNIEXPORT void JNICALL Java_com_jnibind_test_ContextTest_DoSetup(JNIEnv* env, JNIEXPORT jlong JNICALL Java_com_jnibind_test_ContextTest_nativeCreateContext( JNIEnv* env, jclass, jint val) { auto* ctx_struct = new ContextStruct{GlobalObject{}}; - ctx_struct->obj["intVal1"].Set(jint{val}); + ctx_struct->obj.Access<"intVal1">().Set(jint{val}); return reinterpret_cast(ctx_struct); } diff --git a/javatests/com/jnibind/test/field_test_jni.cc b/javatests/com/jnibind/test/field_test_jni.cc index 349daa14..cfa09b08 100644 --- a/javatests/com/jnibind/test/field_test_jni.cc +++ b/javatests/com/jnibind/test/field_test_jni.cc @@ -60,33 +60,33 @@ Java_com_jnibind_test_FieldTest_jniTearDown(JavaVM* pjvm, void* reserved) { JNIEXPORT jint JNICALL Java_com_jnibind_test_FieldTest_jniIntField( JNIEnv* env, jclass, jobject object, jint val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["intField"].Set(jint{val}); + rjni_test_helper.Access<"intField">().Set(jint{val}); - return rjni_test_helper["intField"].Get(); + return rjni_test_helper.Access<"intField">().Get(); } JNIEXPORT jfloat JNICALL Java_com_jnibind_test_FieldTest_jniFloatField( JNIEnv* env, jclass, jobject object, jfloat val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["floatField"].Set(jfloat{val}); + rjni_test_helper.Access<"floatField">().Set(jfloat{val}); - return rjni_test_helper["floatField"].Get(); + return rjni_test_helper.Access<"floatField">().Get(); } JNIEXPORT jdouble JNICALL Java_com_jnibind_test_FieldTest_jniDoubleField( JNIEnv* env, jclass, jobject object, jdouble val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["doubleField"].Set(jdouble{val}); + rjni_test_helper.Access<"doubleField">().Set(jdouble{val}); - return rjni_test_helper["doubleField"].Get(); + return rjni_test_helper.Access<"doubleField">().Get(); } JNIEXPORT jstring JNICALL Java_com_jnibind_test_FieldTest_jniStringField( JNIEnv* env, jclass, jobject object, jstring val) { LocalObject rjni_test_helper{object}; - rjni_test_helper["stringField"].Set(val); + rjni_test_helper.Access<"stringField">().Set(val); - return rjni_test_helper["stringField"].Get().Release(); + return rjni_test_helper.Access<"stringField">().Get().Release(); } JNIEXPORT void JNICALL Java_com_jnibind_test_FieldTest_jniObjectFieldSet( @@ -95,12 +95,15 @@ JNIEXPORT void JNICALL Java_com_jnibind_test_FieldTest_jniObjectFieldSet( LocalObject field_test{test_class}; LocalObject obj{intVal, floatVal, doubleVal}; - field_test["fieldTestHelper"].Set(obj); + field_test.Access<"fieldTestHelper">().Set(obj); } JNIEXPORT jobject JNICALL Java_com_jnibind_test_FieldTest_jniObjectFieldGet( JNIEnv* env, jclass, jobject test_class) { - return LocalObject{test_class}["fieldTestHelper"].Get().Release(); + return LocalObject{test_class} + .Access<"fieldTestHelper">() + .Get() + .Release(); } } // extern "C" diff --git a/javatests/com/jnibind/test/global_object_test_jni.cc b/javatests/com/jnibind/test/global_object_test_jni.cc index ee27b44d..97fe83b5 100644 --- a/javatests/com/jnibind/test/global_object_test_jni.cc +++ b/javatests/com/jnibind/test/global_object_test_jni.cc @@ -63,8 +63,8 @@ Java_com_jnibind_test_GlobalObjectTest_jniBuildNewObjectsFromExistingObjects( JNIEnv* env, jclass, jobject test_helper_object, jobject object_to_mutate) { jni::LocalObject helper_obj{object_to_mutate}; - return jni::LocalObject{test_helper_object}( - "methodTakesGlobalObjectReturnsNewObject", helper_obj) + return jni::LocalObject{test_helper_object} + .Call<"methodTakesGlobalObjectReturnsNewObject">(helper_obj) .Release(); } @@ -73,17 +73,17 @@ Java_com_jnibind_test_GlobalObjectTest_jniManipulateNewGlobalObjectSetIntVal238( JNIEnv* env, jclass, jobject jtest_obj) { jni::GlobalObject helper_obj{2, 3, 8}; - return jni::LocalObject{jtest_obj}( - "methodTakesGlobalObjectReturnsNewObject", std::move(helper_obj)) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesGlobalObjectReturnsNewObject">(std::move(helper_obj)) .Release(); } JNIEXPORT jobject JNICALL Java_com_jnibind_test_GlobalObjectTest_jniMaterializeNewGlobalObjectSetIntVal159( JNIEnv* env, jclass, jobject jtest_obj) { - return jni::LocalObject{jtest_obj}( - "methodTakesGlobalObjectReturnsNewObject", - jni::GlobalObject{1, 5, 9}) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesGlobalObjectReturnsNewObject">( + jni::GlobalObject{1, 5, 9}) .Release(); } diff --git a/javatests/com/jnibind/test/local_object_test_jni.cc b/javatests/com/jnibind/test/local_object_test_jni.cc index f2cf225e..fda520b4 100644 --- a/javatests/com/jnibind/test/local_object_test_jni.cc +++ b/javatests/com/jnibind/test/local_object_test_jni.cc @@ -65,8 +65,8 @@ Java_com_jnibind_test_LocalObjectTest_jniBuildNewObjectsFromExistingObjects( JNIEnv* env, jclass, jobject jtest_obj, jobject jhelper_obj) { jni::LocalObject helper_obj{jhelper_obj}; - return jni::LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", helper_obj) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">(helper_obj) .Release(); } @@ -75,17 +75,17 @@ Java_com_jnibind_test_LocalObjectTest_jniManipulateNewLocalObjectSetIntVal238( JNIEnv* env, jclass, jobject jtest_obj) { jni::LocalObject helper_obj{2, 3, 8}; - return jni::LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", std::move(helper_obj)) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">(std::move(helper_obj)) .Release(); } JNIEXPORT jobject JNICALL Java_com_jnibind_test_LocalObjectTest_jniMaterializeNewLocalObjectSetIntVal159( JNIEnv* env, jclass, jobject jtest_obj) { - return jni::LocalObject{jtest_obj}( - "methodTakesLocalObjectReturnsNewObject", - jni::LocalObject{1, 5, 9}) + return jni::LocalObject{jtest_obj} + .Call<"methodTakesLocalObjectReturnsNewObject">( + jni::LocalObject{1, 5, 9}) .Release(); } diff --git a/javatests/com/jnibind/test/method_test_jni.cc b/javatests/com/jnibind/test/method_test_jni.cc index f298c5b3..51928989 100644 --- a/javatests/com/jnibind/test/method_test_jni.cc +++ b/javatests/com/jnibind/test/method_test_jni.cc @@ -92,7 +92,7 @@ Java_com_jnibind_test_MethodTest_jniTearDown(JavaVM* pjvm, void* reserved) { JNIEXPORT void JNICALL Java_com_jnibind_test_MethodTest_jniVoidMethod( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - gtest_helper("voidMethod"); + gtest_helper.Call<"voidMethod">(); } JNIEXPORT void JNICALL @@ -100,7 +100,7 @@ Java_com_jnibind_test_MethodTest_jniVoidMethodTakesOneInt(JNIEnv* env, jclass, jobject object, jint i) { LocalObject gtest_helper{object}; - gtest_helper("voidMethodTakesOneInt", jint{i}); + gtest_helper.Call<"voidMethodTakesOneInt">(jint{i}); } JNIEXPORT void JNICALL @@ -110,8 +110,8 @@ Java_com_jnibind_test_MethodTest_jniVoidMethodTakesFiveInts(JNIEnv* env, jclass, jint i3, jint i4, jint i5) { LocalObject gtest_helper{object}; - gtest_helper("voidMethodTakesFiveInts", jint{i1}, jint{i2}, jint{i3}, - jint{i4}, jint{i5}); + gtest_helper.Call<"voidMethodTakesFiveInts">(jint{i1}, jint{i2}, jint{i3}, + jint{i4}, jint{i5}); } /** Boolean Method Tests. */ @@ -119,7 +119,7 @@ JNIEXPORT jboolean JNICALL Java_com_jnibind_test_MethodTest_jniBooleanMethod( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("booleanMethod"); + return gtest_helper.Call<"booleanMethod">(); } JNIEXPORT jboolean JNICALL @@ -128,7 +128,7 @@ Java_com_jnibind_test_MethodTest_jniBooleanMethodTakesOneBoolean(JNIEnv* env, jobject object, jboolean i) { LocalObject gtest_helper{object}; - return gtest_helper("booleanMethodTakesOneBoolean", jboolean{i}); + return gtest_helper.Call<"booleanMethodTakesOneBoolean">(jboolean{i}); } JNIEXPORT jboolean JNICALL @@ -137,8 +137,8 @@ Java_com_jnibind_test_MethodTest_jniBooleanMethodTakesFiveBooleans( jboolean i4, jboolean i5) { LocalObject gtest_helper{object}; - return gtest_helper("booleanMethodTakesFiveBooleans", jboolean{i1}, - jboolean{i2}, jboolean{i3}, jboolean{i4}, jboolean{i5}); + return gtest_helper.Call<"booleanMethodTakesFiveBooleans">( + jboolean{i1}, jboolean{i2}, jboolean{i3}, jboolean{i4}, jboolean{i5}); } /** Int Method Tests. */ @@ -146,14 +146,14 @@ JNIEXPORT jint JNICALL Java_com_jnibind_test_MethodTest_jniIntMethod( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("intMethod"); + return gtest_helper.Call<"intMethod">(); } JNIEXPORT jint JNICALL Java_com_jnibind_test_MethodTest_jniIntMethodTakesOneInt( JNIEnv* env, jclass, jobject object, jint i) { LocalObject gtest_helper{object}; - return gtest_helper("intMethodTakesOneInt", jint{i}); + return gtest_helper.Call<"intMethodTakesOneInt">(jint{i}); } JNIEXPORT jint JNICALL @@ -164,8 +164,8 @@ Java_com_jnibind_test_MethodTest_jniIntMethodTakesFiveInts(JNIEnv* env, jclass, jint i5) { LocalObject gtest_helper{object}; - return gtest_helper("intMethodTakesFiveInts", jint{i1}, jint{i2}, jint{i3}, - jint{i4}, jint{i5}); + return gtest_helper.Call<"intMethodTakesFiveInts">( + jint{i1}, jint{i2}, jint{i3}, jint{i4}, jint{i5}); } /** Long Method Tests. */ @@ -173,7 +173,7 @@ JNIEXPORT jlong JNICALL Java_com_jnibind_test_MethodTest_jniLongMethod( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("longMethod"); + return gtest_helper.Call<"longMethod">(); } JNIEXPORT jlong JNICALL @@ -182,7 +182,7 @@ Java_com_jnibind_test_MethodTest_jniLongMethodTakesOneLong(JNIEnv* env, jclass, jlong i) { LocalObject gtest_helper{object}; - return gtest_helper("longMethodTakesOneLong", jlong{i}); + return gtest_helper.Call<"longMethodTakesOneLong">(jlong{i}); } JNIEXPORT jlong JNICALL @@ -191,8 +191,8 @@ Java_com_jnibind_test_MethodTest_jniLongMethodTakesFiveLongs( jlong i5) { LocalObject gtest_helper{object}; - return gtest_helper("longMethodTakesFiveLongs", jlong{i1}, jlong{i2}, - jlong{i3}, jlong{i4}, jlong{i5}); + return gtest_helper.Call<"longMethodTakesFiveLongs">( + jlong{i1}, jlong{i2}, jlong{i3}, jlong{i4}, jlong{i5}); } /** Float Method Tests. */ @@ -200,7 +200,7 @@ JNIEXPORT jfloat JNICALL Java_com_jnibind_test_MethodTest_jniFloatMethod( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("floatMethod"); + return gtest_helper.Call<"floatMethod">(); } JNIEXPORT jfloat JNICALL @@ -210,7 +210,7 @@ Java_com_jnibind_test_MethodTest_jniFloatMethodTakesOneFloat(JNIEnv* env, jfloat i) { LocalObject gtest_helper{object}; - return gtest_helper("floatMethodTakesOneFloat", jfloat{i}); + return gtest_helper.Call<"floatMethodTakesOneFloat">(jfloat{i}); } JNIEXPORT jfloat JNICALL @@ -219,8 +219,8 @@ Java_com_jnibind_test_MethodTest_jniFloatMethodTakesFiveFloats( jfloat i4, jfloat i5) { LocalObject gtest_helper{object}; - return gtest_helper("floatMethodTakesFiveFloats", jfloat{i1}, jfloat{i2}, - jfloat{i3}, jfloat{i4}, jfloat{i5}); + return gtest_helper.Call<"floatMethodTakesFiveFloats">( + jfloat{i1}, jfloat{i2}, jfloat{i3}, jfloat{i4}, jfloat{i5}); } /** Double Method Tests. */ @@ -228,7 +228,7 @@ JNIEXPORT jdouble JNICALL Java_com_jnibind_test_MethodTest_jniDoubleMethod( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("doubleMethod"); + return gtest_helper.Call<"doubleMethod">(); } JNIEXPORT jdouble JNICALL @@ -238,7 +238,7 @@ Java_com_jnibind_test_MethodTest_jniDoubleMethodTakesOneDouble(JNIEnv* env, jdouble i) { LocalObject gtest_helper{object}; - return gtest_helper("doubleMethodTakesOneDouble", jdouble{i}); + return gtest_helper.Call<"doubleMethodTakesOneDouble">(jdouble{i}); } JNIEXPORT jdouble JNICALL @@ -247,8 +247,8 @@ Java_com_jnibind_test_MethodTest_jniDoubleMethodTakesFiveDoubles( jdouble i4, jdouble i5) { LocalObject gtest_helper{object}; - return gtest_helper("doubleMethodTakesFiveDoubles", jdouble{i1}, jdouble{i2}, - jdouble{i3}, jdouble{i4}, jdouble{i5}); + return gtest_helper.Call<"doubleMethodTakesFiveDoubles">( + jdouble{i1}, jdouble{i2}, jdouble{i3}, jdouble{i4}, jdouble{i5}); } /** Overload Method Tests. */ @@ -256,29 +256,29 @@ JNIEXPORT int JNICALL Java_com_jnibind_test_MethodTest_jniCallFooOverload1( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("foo"); + return gtest_helper.Call<"foo">(); } JNIEXPORT int JNICALL Java_com_jnibind_test_MethodTest_jniCallFooOverload2( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("foo", 123.f); + return gtest_helper.Call<"foo">(123.f); } JNIEXPORT int JNICALL Java_com_jnibind_test_MethodTest_jniCallFooOverload3( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - return gtest_helper("foo", 123.f, 456.f); + return gtest_helper.Call<"foo">(123.f, 456.f); } /** Overload int/double disambiguation. */ JNIEXPORT void JNICALL Java_com_jnibind_test_MethodTest_jniCallFooOverload4( JNIEnv* env, jclass, jobject object) { LocalObject gtest_helper{object}; - gtest_helper("intDouble", 123, 456.); - gtest_helper("intDouble", 456., 123); + gtest_helper.Call<"intDouble">(123, 456.); + gtest_helper.Call<"intDouble">(456., 123); } } // extern "C" diff --git a/javatests/com/jnibind/test/static_test_jni.cc b/javatests/com/jnibind/test/static_test_jni.cc index 26260150..558bdcda 100644 --- a/javatests/com/jnibind/test/static_test_jni.cc +++ b/javatests/com/jnibind/test/static_test_jni.cc @@ -73,49 +73,49 @@ Java_com_jnibind_test_StaticTest_jniTearDown(JavaVM* pjvm, void* reserved) { //////////////////////////////////////////////////////////////////////////////// JNIEXPORT void JNICALL Java_com_jnibind_test_StaticTest_voidMethodTestNative( JavaVM* pjvm, void* reserved) { - StaticRef{}("voidFunc"); + StaticRef{}.Call<"voidFunc">(); } JNIEXPORT jbyte JNICALL Java_com_jnibind_test_StaticTest_byteMethodTestNative( JavaVM* pjvm, void* reserved) { - return StaticRef{}("byteFunc"); + return StaticRef{}.Call<"byteFunc">(); } JNIEXPORT jchar JNICALL Java_com_jnibind_test_StaticTest_charMethodTestNative( JavaVM* pjvm, void* reserved) { - return StaticRef{}("charFunc"); + return StaticRef{}.Call<"charFunc">(); } JNIEXPORT jshort JNICALL Java_com_jnibind_test_StaticTest_shortMethodTestNative( JavaVM* pjvm, void* reserved) { - return StaticRef{}("shortFunc"); + return StaticRef{}.Call<"shortFunc">(); } JNIEXPORT jint JNICALL Java_com_jnibind_test_StaticTest_intMethodTestNative( JavaVM* pjvm, void* reserved) { - return StaticRef{}("intFunc"); + return StaticRef{}.Call<"intFunc">(); } JNIEXPORT jlong JNICALL Java_com_jnibind_test_StaticTest_longMethodTestNative( JavaVM* pjvm, void* reserved) { - return StaticRef{}("longFunc"); + return StaticRef{}.Call<"longFunc">(); } JNIEXPORT jfloat JNICALL Java_com_jnibind_test_StaticTest_floatMethodTestNative( JavaVM* pjvm, void* reserved) { - return StaticRef{}("floatFunc"); + return StaticRef{}.Call<"floatFunc">(); } JNIEXPORT jdouble JNICALL Java_com_jnibind_test_StaticTest_doubleMethodTestNative(JavaVM* pjvm, void* reserved) { - return StaticRef{}("doubleFunc"); + return StaticRef{}.Call<"doubleFunc">(); } JNIEXPORT jobject JNICALL Java_com_jnibind_test_StaticTest_objectMethodTestNative(JavaVM* pjvm, void* reserved) { - return StaticRef{}("objectFunc").Release(); + return StaticRef{}.Call<"objectFunc">().Release(); } JNIEXPORT jstring JNICALL @@ -123,7 +123,7 @@ Java_com_jnibind_test_StaticTest_complexMethodTestNative(JavaVM* pjvm, void* reserved, jint a, jfloat b, jstring c, jobjectArray d) { - return StaticRef{}("complexFunc", a, b, c, d).Release(); + return StaticRef{}.Call<"complexFunc">(a, b, c, d).Release(); } //////////////////////////////////////////////////////////////////////////////// @@ -131,57 +131,57 @@ Java_com_jnibind_test_StaticTest_complexMethodTestNative(JavaVM* pjvm, //////////////////////////////////////////////////////////////////////////////// JNIEXPORT bool JNICALL Java_com_jnibind_test_StaticTest_booleanFieldTestNative( JavaVM* pjvm, void* reserved, bool val) { - StaticRef{}["booleanField"].Set(val); - return StaticRef{}["booleanField"].Get(); + StaticRef{}.Access<"booleanField">().Set(val); + return StaticRef{}.Access<"booleanField">().Get(); } JNIEXPORT jbyte JNICALL Java_com_jnibind_test_StaticTest_byteFieldTestNative( JavaVM* pjvm, void* reserved, jbyte val) { - StaticRef{}["byteField"].Set(val); - return StaticRef{}["byteField"].Get(); + StaticRef{}.Access<"byteField">().Set(val); + return StaticRef{}.Access<"byteField">().Get(); } JNIEXPORT char JNICALL Java_com_jnibind_test_StaticTest_charFieldTestNative( JavaVM* pjvm, void* reserved, jchar val) { - StaticRef{}["charField"].Set(val); - return StaticRef{}["charField"].Get(); + StaticRef{}.Access<"charField">().Set(val); + return StaticRef{}.Access<"charField">().Get(); } JNIEXPORT short JNICALL Java_com_jnibind_test_StaticTest_shortFieldTestNative( JavaVM* pjvm, void* reserved, jshort val) { - StaticRef{}["shortField"].Set(val); - return StaticRef{}["shortField"].Get(); + StaticRef{}.Access<"shortField">().Set(val); + return StaticRef{}.Access<"shortField">().Get(); } JNIEXPORT int JNICALL Java_com_jnibind_test_StaticTest_intFieldTestNative( JavaVM* pjvm, void* reserved, jint val) { - StaticRef{}["intField"].Set(val); - return StaticRef{}["intField"].Get(); + StaticRef{}.Access<"intField">().Set(val); + return StaticRef{}.Access<"intField">().Get(); } JNIEXPORT long JNICALL Java_com_jnibind_test_StaticTest_longFieldTestNative( JavaVM* pjvm, void* reserved, jlong val) { - StaticRef{}["longField"].Set(val); - return StaticRef{}["longField"].Get(); + StaticRef{}.Access<"longField">().Set(val); + return StaticRef{}.Access<"longField">().Get(); } JNIEXPORT float JNICALL Java_com_jnibind_test_StaticTest_floatFieldTestNative( JavaVM* pjvm, void* reserved, jfloat val) { - StaticRef{}["floatField"].Set(val); - return StaticRef{}["floatField"].Get(); + StaticRef{}.Access<"floatField">().Set(val); + return StaticRef{}.Access<"floatField">().Get(); } JNIEXPORT double JNICALL Java_com_jnibind_test_StaticTest_doubleFieldTestNative( JavaVM* pjvm, void* reserved, jdouble val) { - StaticRef{}["doubleField"].Set(val); - return StaticRef{}["doubleField"].Get(); + StaticRef{}.Access<"doubleField">().Set(val); + return StaticRef{}.Access<"doubleField">().Get(); } JNIEXPORT jobject JNICALL Java_com_jnibind_test_StaticTest_objectFieldTestNative(JavaVM* pjvm, void* reserved, jobject val) { - StaticRef{}["objectField"].Set(val); - return StaticRef{}["objectField"].Get().Release(); + StaticRef{}.Access<"objectField">().Set(val); + return StaticRef{}.Access<"objectField">().Get().Release(); } } diff --git a/javatests/com/jnibind/test/thread_test_jni.cc b/javatests/com/jnibind/test/thread_test_jni.cc index 4ea7d098..f1d6072e 100644 --- a/javatests/com/jnibind/test/thread_test_jni.cc +++ b/javatests/com/jnibind/test/thread_test_jni.cc @@ -57,7 +57,7 @@ Java_com_jnibind_test_ThreadTest_RunsThreadedWorkOnObject(JNIEnv* env, jclass, std::thread worker{ [global_object_lambda_scope{std::move(global_obj)}]() mutable { ThreadGuard thread_guard{}; - global_object_lambda_scope("foo"); + global_object_lambda_scope.Call<"foo">(); }}; worker.join(); diff --git a/jni_bind.h b/jni_bind.h index 6c4c79c3..dfaca903 100644 --- a/jni_bind.h +++ b/jni_bind.h @@ -50,6 +50,7 @@ static constexpr Configuration kConfig{ #include "implementation/jni_helper/jni_helper.h" #include "implementation/jni_helper/lifecycle.h" #include "implementation/jni_helper/static_field_value.h" +#include "metaprogramming/string_literal.h" // Headers for static definitions. #include "implementation/array.h" diff --git a/metaprogramming/BUILD b/metaprogramming/BUILD index 69114bec..ba725914 100644 --- a/metaprogramming/BUILD +++ b/metaprogramming/BUILD @@ -230,29 +230,6 @@ cc_test( ], ) -################################################################################ -# Conjunction. -################################################################################ -cc_library( - name = "conjunction", - hdrs = ["conjunction.h"], - deps = [ - ":invoke", - ":next", - ], -) - -cc_test( - name = "conjunction_test", - srcs = ["conjunction_test.cc"], - deps = [ - ":conjunction", - ":invoke", - ":next", - "@googletest//:gtest_main", - ], -) - ################################################################################ # Contains. ################################################################################ @@ -345,27 +322,8 @@ cc_test( ) ################################################################################ -# Deep Equal. +# Deep Equal Diminished. ################################################################################ -cc_library( - name = "deep_equal", - hdrs = ["deep_equal.h"], - deps = [ - ":pack_discriminator", - ":vals_equal", - ], -) - -cc_test( - name = "deep_equal_test", - srcs = ["deep_equal_test.cc"], - deps = [ - ":deep_equal", - ":pack_discriminator", - "@googletest//:gtest_main", - ], -) - cc_library( name = "deep_equal_diminished", hdrs = ["deep_equal_diminished.h"], @@ -606,50 +564,50 @@ cc_test( ) ################################################################################ -# Invoke. +# Invocable Map 20. ################################################################################ cc_library( - name = "invoke", - hdrs = ["invoke.h"], + name = "invocable_map_20", + hdrs = ["invocable_map_20.h"], deps = [ - ":per_element", - ":tuple_manipulation", - ":type_of_nth_element", + ":modified_max", + ":string_literal", ], ) cc_test( - name = "invoke_test", - srcs = ["invoke_test.cc"], + name = "invocable_map_test_20", + srcs = ["invocable_map_20_test.cc"], + tags = ["cpp20"], deps = [ - ":increment", - ":invoke", - ":per_element", - ":same", + ":invocable_map_20", + ":modified_max", + ":string_literal", "@googletest//:gtest_main", ], ) ################################################################################ -# Lambda Compatible. +# Invoke. ################################################################################ cc_library( - name = "lambda_compatible", - hdrs = ["lambda_compatible.h"], + name = "invoke", + hdrs = ["invoke.h"], deps = [ - ":deep_equal", + ":per_element", ":tuple_manipulation", ":type_of_nth_element", ], ) cc_test( - name = "lambda_compatible_test", - srcs = ["lambda_compatible_test.cc"], + name = "invoke_test", + srcs = ["invoke_test.cc"], deps = [ - ":deep_equal", - ":lambda_compatible", - ":vals", + ":increment", + ":invoke", + ":per_element", + ":same", "@googletest//:gtest_main", ], ) @@ -961,6 +919,35 @@ cc_test( ], ) +################################################################################ +# Queryable Map 20. +################################################################################ +cc_library( + name = "queryable_map_20", + hdrs = ["queryable_map_20.h"], + deps = [ + ":interleave", + ":lambda_string", + ":modified_max", + ":string_literal", + ":tuple_from_size", + ":tuple_manipulation", + ":type_of_nth_element", + ], +) + +cc_test( + name = "queryable_map_20_test", + srcs = ["queryable_map_20_test.cc"], + tags = ["cpp20"], + deps = [ + ":modified_max", + ":queryable_map_20", + ":string_literal", + "@googletest//:gtest_main", + ], +) + ################################################################################ # Reduce. ################################################################################ diff --git a/metaprogramming/conjunction.h b/metaprogramming/conjunction.h deleted file mode 100644 index fc099661..00000000 --- a/metaprogramming/conjunction.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef JNI_BIND_METAPROGRAMMING_CONJUNCTION_H_ -#define JNI_BIND_METAPROGRAMMING_CONJUNCTION_H_ - -#include - -#include "metaprogramming/invoke.h" -#include "metaprogramming/next.h" - -namespace jni::metaprogramming { - -template -struct Conjunction; - -template -struct ConjunctionHelper { - static constexpr bool value = - cur_val && Invoke_t::value && - Conjunction::template value, Iter2>; - using type = Invoke_t; -}; - -template -struct ConjunctionHelper { - static constexpr bool value = cur_val && Invoke_t::value; -}; - -template -struct Conjunction { - template - static constexpr bool value = - ConjunctionHelper::value; - - template - using type = std::bool_constant>; -}; - -// Applies lambda from |Iter1| to |Iter2| inclusive. -template -static constexpr bool Conjunction_v = - Conjunction::template value; - -} // namespace jni::metaprogramming - -#endif // JNI_BIND_METAPROGRAMMING_CONJUNCTION_H_ diff --git a/metaprogramming/conjunction_test.cc b/metaprogramming/conjunction_test.cc deleted file mode 100644 index b3acbf2e..00000000 --- a/metaprogramming/conjunction_test.cc +++ /dev/null @@ -1,80 +0,0 @@ -#include "metaprogramming/conjunction.h" - -#include -#include - -#include "metaprogramming/invoke.h" -#include "metaprogramming/next.h" - -namespace { - -using ::jni::metaprogramming::Conjunction_v; -using ::jni::metaprogramming::Invoke_t; -using ::jni::metaprogramming::NextVal; // NOLINT - -template -struct ValCounter { - static constexpr std::size_t value = I; -}; - -} // namespace - -template <> -struct NextVal { - template - struct Helper; - - template - struct Helper> { - using type = ValCounter; - }; - - template - using type = typename Helper::type; -}; - -struct Not3 { - template - static constexpr bool value = T::value != 3; - - template - using type = std::bool_constant>; -}; - -static_assert(Invoke_t>::value); -static_assert(Invoke_t>::value); -static_assert(Invoke_t>::value); -static_assert(!Invoke_t>::value); -static_assert(Invoke_t>::value); -static_assert(Invoke_t>::value); - -using Iter1 = ValCounter<1>; -using Iter2 = ValCounter<2>; -using Iter3 = ValCounter<3>; -using Iter4 = ValCounter<4>; -using Iter5 = ValCounter<5>; - -// Iterators spanning 1 element. -static_assert(Conjunction_v); -static_assert(Conjunction_v); -static_assert(!Conjunction_v); -static_assert(Conjunction_v); -static_assert(Conjunction_v); - -// Iterators spanning multiple elements (starting at 1). -static_assert(Conjunction_v); -static_assert(!Conjunction_v); -static_assert(!Conjunction_v); -static_assert(!Conjunction_v); - -// Iterators spanning multiple elements (starting at 2). -static_assert(!Conjunction_v); -static_assert(!Conjunction_v); -static_assert(!Conjunction_v); - -// Iterators spanning multiple elements (starting at 3). -static_assert(!Conjunction_v); -static_assert(!Conjunction_v); - -// Iterators spanning multiple elements (starting at 4). -static_assert(Conjunction_v); diff --git a/metaprogramming/even_odd.h b/metaprogramming/even_odd.h index 85554525..4fe516ca 100644 --- a/metaprogramming/even_odd.h +++ b/metaprogramming/even_odd.h @@ -23,35 +23,35 @@ namespace jni::metaprogramming { -// Metafunction to return only even elements. -struct Even { - template - struct EvenHelper {}; +template +struct EvenHelper {}; - template <> - struct EvenHelper> { - using type = std::tuple<>; - }; +template <> +struct EvenHelper> { + using type = std::tuple<>; +}; - template - struct OddHelper {}; +template +struct OddHelper {}; - template <> - struct OddHelper> { - using type = std::tuple<>; - }; +template <> +struct OddHelper> { + using type = std::tuple<>; +}; - template - struct OddHelper> { - using type = typename EvenHelper>::type; - }; +template +struct EvenHelper> { + using type = ConcatenateTup_t, + typename OddHelper>::type>; +}; - template - struct EvenHelper> { - using type = ConcatenateTup_t, - typename OddHelper>::type>; - }; +template +struct OddHelper> { + using type = typename EvenHelper>::type; +}; +// Metafunction to return only even elements. +struct Even { template using type = typename EvenHelper>::type; }; @@ -61,16 +61,6 @@ using Even_t = typename Even::template type; // Metafunction to return only odd elements. struct Odd { - template - struct OddHelper { - using type = std::tuple<>; - }; - - template - struct OddHelper> { - using type = Even_t; - }; - template using type = typename OddHelper>::type; }; diff --git a/metaprogramming/invocable_map.h b/metaprogramming/invocable_map.h index e1c99afc..40badde6 100644 --- a/metaprogramming/invocable_map.h +++ b/metaprogramming/invocable_map.h @@ -26,7 +26,7 @@ namespace jni::metaprogramming { template + typename TupContainerT, const auto TupContainerT::* nameable_member> class InvocableMap; // This is an interface that can be inherited from to expose an operator(...). @@ -42,7 +42,7 @@ class InvocableMap; // auto InvocableMapCall(const char* key, Args&&... args); // // If i is the index where |tup_container_v.*nameable_member|.name_ == key, -// then InvocablemapCall will forward the args from operator() with the +// then InvocableMapCall will forward the args from operator() with the // same args. Static memory can be used in this function call and it will // be unique because of the I non-type template parameter. // @@ -54,23 +54,23 @@ class InvocableMap; // the the const char cannot be propagated without losing its constexpr-ness, // and so the clang extension can no longer restrict function candidates. template ::*nameable_member> + const auto std::decay_t::* nameable_member> using InvocableMap_t = InvocableMap, nameable_member>; template class InvocableMapEntry; template class InvocableMapBase {}; template class InvocableMapBase> @@ -78,11 +78,12 @@ class InvocableMapBase... { public: using InvocableMapEntry::operator()...; + nameable_member, idxs>:: + operator()...; }; template class InvocableMapEntry { public: @@ -116,7 +117,7 @@ class InvocableMapEntry { //============================================================================== template + typename TupContainerT, const auto TupContainerT::* nameable_member> class InvocableMap : public InvocableMapBase< CrtpBase, tup_container_v, TupContainerT, nameable_member, diff --git a/metaprogramming/invocable_map_20.h b/metaprogramming/invocable_map_20.h new file mode 100644 index 00000000..2e280f47 --- /dev/null +++ b/metaprogramming/invocable_map_20.h @@ -0,0 +1,98 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JNI_BIND_METAPROGRAMMING_INVOCABLE_MAP_20_H +#define JNI_BIND_METAPROGRAMMING_INVOCABLE_MAP_20_H + +#include +#include +#include +#include +#include + +#include "modified_max.h" +#include "string_literal.h" + +namespace jni::metaprogramming { + +// This class enables compile time lookup that perfectly forward arguments +// to a method named `Call`. This is a C++ 20 version of InvocableMap. +// +// This is an interface that can be inherited from to expose a method named +// `Call`. It provides compile time string index lookup with no macros. +// +// To use this API, inherit from this class using template types as follows: +// +// |CrtpBase|: The name of the class inheriting from the map. This class +// will inherit an operator(). It must implement this exact signature: +// +// template +// auto InvocableMap20Call(Args&&... args); +// +// If i is the index where |tup_container_v.*nameable_member|.name_ == key, +// then InvocableMap20Call will forward the args from operator() with the +// same args. Static memory can be used in this function call and it will +// be unique because of the I non-type template parameter. +// +// |tup_container_v| is a static instance of an object whose |nameable_member| +// contains a public field called name_. It might seem strange not to +// directly pass a const auto&, however, this prevents accessing subobjects. +// |TupContainerT| is the type of the container for the member. +// |MemberT| is the type of a *pointer to member* in the container, *not* the +// actual type of the member itself. +// |nameable_member| is pointer to member of the nameable type. +// +// The motivation for using inheritance as opposed to a simple member is that +// the the const char cannot be propagated without losing its constexpr-ness, +// and so the clang extension can no longer restrict function candidates. +template +class InvocableMap20 { +#if __cplusplus >= 202002L + public: + template + constexpr auto Do(Args&&... args) { + return (*static_cast(this)) + .template InvocableMap20Call( + std::forward(args)...); + } + + template + static constexpr std::size_t SelectCandidate(StringLiteral string_literal, + std::index_sequence) { + return ModifiedMax( + {((std::get(tup_container_v.*nameable_member).name_ == + std::string_view{string_literal.value}) + ? std::size_t{Is} + : kNegativeOne)..., + kNegativeOne}); + } + + template + constexpr auto Call(Args&&... args) { + return Do>>())>( + std::forward(args)...); + } +#endif // __cplusplus >= 202002L +}; + +} // namespace jni::metaprogramming + +#endif // JNI_BIND_METAPROGRAMMING_INVOCABLE_MAP_20_H diff --git a/metaprogramming/invocable_map_20_test.cc b/metaprogramming/invocable_map_20_test.cc new file mode 100644 index 00000000..703c662d --- /dev/null +++ b/metaprogramming/invocable_map_20_test.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "invocable_map_20.h" + +#include +#include +#include +#include + +#include "string_literal.h" +#include + +using jni::metaprogramming::InvocableMap20; +using jni::metaprogramming::StringLiteral; + +struct Str { + const char* name_; + constexpr Str(const char* name) : name_(name) {} +}; + +struct NameContainer { + std::tuple container1_; + std::tuple container2_; +}; + +constexpr NameContainer name_container{ + {{"Foo"}, {"Bar"}, {"Baz"}}, + {{"Fizz"}, {"Buzz"}, {"Bang"}}, +}; + +//////////////////////////////////////////////////////////////////////////////// +class SampleClassNowExposingCallOperator1 + : public InvocableMap20< + SampleClassNowExposingCallOperator1, name_container, NameContainer, + decltype(&NameContainer::container1_), &NameContainer::container1_> { + protected: + friend InvocableMap20; + + template + auto InvocableMap20Call(Args&&... ts) { + if (std::string_view(key_literal.value) == "Foo") { + EXPECT_TRUE(I == 0); + EXPECT_TRUE((std::is_same_v, std::tuple>)); + } else if (std::string_view(key_literal.value) == "Bar") { + EXPECT_TRUE(I == 1); + EXPECT_TRUE( + (std::is_same_v, std::tuple>)); + } else if (std::string_view(key_literal.value) == "Baz") { + EXPECT_TRUE(I == 2); + EXPECT_TRUE(( + std::is_same_v, std::tuple>)); + } else { + FAIL(); + } + } +}; + +TEST(InvocableMapTest1, HasCorrectTypesAndForwardsCalls) { + SampleClassNowExposingCallOperator1 val; + val.Call<"Foo">(1); + val.Call<"Bar">(2.f, 3.f); + val.Call<"Baz">(4, 5.f, double{6}); + + // By design, doesn't compile. + // val("BazNar", 7, 8, 9); +} diff --git a/metaprogramming/lambda_compatible.h b/metaprogramming/lambda_compatible.h deleted file mode 100644 index 21fd34f5..00000000 --- a/metaprogramming/lambda_compatible.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef JNI_BIND_METAPROGRAMMING_LAMBDA_COMPATIBLE_H_ -#define JNI_BIND_METAPROGRAMMING_LAMBDA_COMPATIBLE_H_ - -#include -#include -#include -#include - -#include "deep_equal.h" -#include "type_of_nth_element.h" - -namespace jni::metaprogramming { - -// Allows lambda argument comparison with deep equality. -// Iff arg lists are equal, and each element is pointwise equal, `val` is true. -template -struct LambdaCompatible { - template - struct Helper; - - using LambdaT = std::decay_t())>; - static constexpr std::size_t kNumArgs = std::tuple_size_v; - - template - struct Helper> { - using QueryT = std::decay_t; - static constexpr std::size_t kNumQueryArgs = std::tuple_size_v; - - // Sizes aren't equal. - template - struct InnerHelper { - static constexpr bool val = false; - }; - - // Sizes are equal, deep comparison (templated to defer compilatino). - template - struct InnerHelper { - static constexpr bool val = - (DeepEqual_v, - TypeOfNthTupleElement_t> && - ...); - }; - - static constexpr bool val = - InnerHelper::val; - }; - - template - static constexpr bool val = - Helper>::val; -}; - -template -static constexpr bool LambdaCompatible_v = - LambdaCompatible::template val; - -} // namespace jni::metaprogramming - -#endif // JNI_BIND_METAPROGRAMMING_LAMBDA_COMPATIBLE_H_ diff --git a/metaprogramming/lambda_compatible_test.cc b/metaprogramming/lambda_compatible_test.cc deleted file mode 100644 index 3569c892..00000000 --- a/metaprogramming/lambda_compatible_test.cc +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "metaprogramming/lambda_compatible.h" - -#include - -#include "metaprogramming/vals.h" - -using ::jni::metaprogramming::LambdaCompatible_v; -using ::jni::metaprogramming::Vals; - -namespace { - -constexpr auto l1{[]() { return std::tuple{'a'}; }}; -constexpr auto l1_prime{[]() { return std::tuple{'a'}; }}; -constexpr auto l2{[]() { return std::tuple{'a', 'a', 'a'}; }}; - -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(!LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(!LambdaCompatible_v); - -// Deliberately does *not* have constexpr construction. -struct A { - A(int a) : a_(a) {} - - int a_; -}; - -constexpr auto l3{[]() { return std::tuple{A{1}}; }}; -constexpr auto l3_prime{[]() { return std::tuple{A{1}}; }}; -constexpr auto l3_prime_prime{[]() { return std::tuple{A{9999}}; }}; - -// Note: Value portion of A cannot be compared, so l3 == l3''. -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(!LambdaCompatible_v); - -// Non-type template arguments compare fine. -constexpr auto l4{[]() { return std::tuple{Vals<'a'>{}}; }}; -constexpr auto l4_prime{[]() { return std::tuple{Vals<'a'>{}}; }}; -constexpr auto l4_prime_prime{ - []() { return std::tuple{Vals<'a', 'a', 'a'>{}}; }}; -constexpr auto l4b_prime_prime{ - []() { return std::tuple{Vals<'b', 'b', 'b'>{}}; }}; - -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(LambdaCompatible_v); -static_assert(!LambdaCompatible_v); - -static_assert(!LambdaCompatible_v); -static_assert(!LambdaCompatible_v); - -} // namespace diff --git a/metaprogramming/n_bit_sequence.h b/metaprogramming/n_bit_sequence.h index 99861a3c..2ca60827 100644 --- a/metaprogramming/n_bit_sequence.h +++ b/metaprogramming/n_bit_sequence.h @@ -36,6 +36,35 @@ struct NBitSequence<> { using Increment = NBitSequence<>; }; +template +using GetBit = TypeOfNthTupleElement_t; + +// If the next bit is overflowing (i.e. it just rolled over), this current +// value increments. Bits only overflow for a single cycle, otherwise higher +// order bits flip when no rollover is happening. +template +struct IncrementedBit { + using type = std::conditional_t< + IncrementedBit::type::overflow_bit_, + typename GetBit::Increment, + typename GetBit::ResetOverflow>; +}; + +template +struct IncrementedBit { + using type = typename GetBit::Increment; +}; + +template +struct IncrementSequenceHelper {}; + +template +struct IncrementSequenceHelper> { + using type = NBitSequence< + typename IncrementedBit::type...>; +}; + // Represents a sequence of "NBits". This can be useful for generating // selections of pools of objects of various sizes. // @@ -52,9 +81,6 @@ struct NBitSequence...> { using Sequence = std::tuple...>; using TypeMask = TypeIndexMask; - template - using GetBit = TypeOfNthTupleElement_t; - // Helper method to compare values of all NBits. template static constexpr bool Same(std::index_sequence = {}) { @@ -68,31 +94,8 @@ struct NBitSequence...> { template static constexpr bool same_ = Same(std::index_sequence{}); - // If the next bit is overflowing (i.e. it just rolled over), this current - // value increments. Bits only overflow for a single cycle, otherwise higher - // order bits flip when no rollover is happening. - template - struct IncrementedBit { - using type = std::conditional_t::type::overflow_bit_, - typename GetBit::Increment, - typename GetBit::ResetOverflow>; - }; - - template <> - struct IncrementedBit { - using type = typename GetBit::Increment; - }; - - template - struct IncrementSequenceHelper {}; - - template - struct IncrementSequenceHelper> { - using type = NBitSequence::type...>; - }; - using Increment = typename IncrementSequenceHelper< - std::make_index_sequence>::type; + Sequence, sequence_size_, std::make_index_sequence>::type; }; } // namespace jni::metaprogramming diff --git a/metaprogramming/next.h b/metaprogramming/next.h index ec440ffa..217643e5 100644 --- a/metaprogramming/next.h +++ b/metaprogramming/next.h @@ -43,7 +43,7 @@ struct EndConstRefVal; // |T| must always be a container of a single kind of template expression. template using Next_t = - typename PackDiscriminatedForward::template type; } // namespace jni::metaprogramming diff --git a/metaprogramming/next_test.cc b/metaprogramming/next_test.cc index 565b290f..59f01fea 100644 --- a/metaprogramming/next_test.cc +++ b/metaprogramming/next_test.cc @@ -28,6 +28,8 @@ using ::jni::metaprogramming::NextConstRefVal; // NOLINT using ::jni::metaprogramming::NextType; // NOLINT using ::jni::metaprogramming::NextVal; // NOLINT +namespace jni::metaprogramming { + //////////////////////////////////////////////////////////////////////////////// // Type tests. //////////////////////////////////////////////////////////////////////////////// @@ -58,6 +60,7 @@ struct NextType { }; static_assert(std::is_same_v>, TypeCounter_t<1, 3>>); + static_assert( std::is_same_v>>, TypeCounter_t<2, 3>>); static_assert(std::is_same_v>>>, @@ -91,12 +94,14 @@ struct NextVal { using type = typename Helper::type; }; +/* static_assert(std::is_same_v>, Counter<1, 3>>); static_assert(std::is_same_v>>, Counter<2, 3>>); static_assert( std::is_same_v>>>, Counter<3, 3>>); static_assert(std::is_same_v>>>>, EndVal>); + */ //////////////////////////////////////////////////////////////////////////////// // Const Auto& tests. @@ -110,18 +115,18 @@ static constexpr std::size_t kTheValue0 = 0; static constexpr std::size_t kMaxValSizeGlobal = 3; template <> -struct ::jni::metaprogramming::NextConstRefVal<::ConstRefCounter> { +struct NextConstRefVal { template struct Helper; template - struct Helper<::ConstRefCounter> { + struct Helper> { static constexpr std::size_t kNextVal = I + 1; using type = ConstRefCounter; }; template - struct Helper<::ConstRefCounter> { + struct Helper> { using type = EndConstRefVal; }; @@ -138,3 +143,5 @@ static_assert( Next_t< Next_t>>>::val == 3); + +} // namespace jni::metaprogramming diff --git a/metaprogramming/pack_discriminator.h b/metaprogramming/pack_discriminator.h index 84558777..33c05775 100644 --- a/metaprogramming/pack_discriminator.h +++ b/metaprogramming/pack_discriminator.h @@ -22,8 +22,6 @@ namespace jni::metaprogramming { enum class PackType { NOT_CONTAINER, TYPES, - AUTO, - AUTO_REF, CONST_AUTO_REF, }; @@ -41,16 +39,6 @@ struct PackDiscrimator { static constexpr PackType val = PackType::TYPES; }; - template