/* * Copyright (C) 2011 The Android Open Source Project * * 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 "reflection.h" #include <float.h> #include <limits.h> #include "ScopedLocalRef.h" #include "art_method-inl.h" #include "base/enums.h" #include "common_compiler_test.h" #include "java_vm_ext.h" #include "jni_internal.h" #include "scoped_thread_state_change-inl.h" namespace art { // TODO: Convert to CommonRuntimeTest. Currently MakeExecutable is used. class ReflectionTest : public CommonCompilerTest { protected: virtual void SetUp() { CommonCompilerTest::SetUp(); vm_ = Runtime::Current()->GetJavaVM(); // Turn on -verbose:jni for the JNI tests. // gLogVerbosity.jni = true; vm_->AttachCurrentThread(&env_, nullptr); ScopedLocalRef<jclass> aioobe(env_, env_->FindClass("java/lang/ArrayIndexOutOfBoundsException")); CHECK(aioobe.get() != nullptr); aioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(aioobe.get())); ScopedLocalRef<jclass> ase(env_, env_->FindClass("java/lang/ArrayStoreException")); CHECK(ase.get() != nullptr); ase_ = reinterpret_cast<jclass>(env_->NewGlobalRef(ase.get())); ScopedLocalRef<jclass> sioobe(env_, env_->FindClass("java/lang/StringIndexOutOfBoundsException")); CHECK(sioobe.get() != nullptr); sioobe_ = reinterpret_cast<jclass>(env_->NewGlobalRef(sioobe.get())); } void CleanUpJniEnv() { if (aioobe_ != nullptr) { env_->DeleteGlobalRef(aioobe_); aioobe_ = nullptr; } if (ase_ != nullptr) { env_->DeleteGlobalRef(ase_); ase_ = nullptr; } if (sioobe_ != nullptr) { env_->DeleteGlobalRef(sioobe_); sioobe_ = nullptr; } } virtual void TearDown() { CleanUpJniEnv(); CommonCompilerTest::TearDown(); } jclass GetPrimitiveClass(char descriptor) { ScopedObjectAccess soa(env_); mirror::Class* c = class_linker_->FindPrimitiveClass(descriptor); CHECK(c != nullptr); return soa.AddLocalReference<jclass>(c); } void ReflectionTestMakeExecutable(ArtMethod** method, ObjPtr<mirror::Object>* receiver, bool is_static, const char* method_name, const char* method_signature) REQUIRES_SHARED(Locks::mutator_lock_) { const char* class_name = is_static ? "StaticLeafMethods" : "NonStaticLeafMethods"; jobject jclass_loader(LoadDex(class_name)); Thread* self = Thread::Current(); StackHandleScope<3> hs(self); Handle<mirror::ClassLoader> class_loader( hs.NewHandle( ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader>(jclass_loader))); if (!is_static) { MakeExecutable(nullptr, "java.lang.Class"); MakeExecutable(nullptr, "java.lang.Object"); } MakeExecutable(class_loader.Get(), class_name); ObjPtr<mirror::Class> c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(), class_loader); CHECK(c != nullptr); *method = is_static ? c->FindDirectMethod(method_name, method_signature, kRuntimePointerSize) : c->FindVirtualMethod(method_name, method_signature, kRuntimePointerSize); CHECK(method != nullptr); if (is_static) { *receiver = nullptr; } else { // Ensure class is initialized before allocating object { StackHandleScope<1> hs2(self); HandleWrapperObjPtr<mirror::Class> h_class(hs2.NewHandleWrapper(&c)); bool initialized = class_linker_->EnsureInitialized(self, h_class, true, true); CHECK(initialized); } *receiver = c->AllocObject(self); } // Start runtime. HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(receiver)); bool started = runtime_->Start(); CHECK(started); self->TransitionFromSuspendedToRunnable(); } void InvokeNopMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "nop", "()V"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), nullptr); } void InvokeIdentityByteMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[1]; args[0].b = 0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(0, result.GetB()); args[0].b = -1; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-1, result.GetB()); args[0].b = SCHAR_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(SCHAR_MAX, result.GetB()); static_assert(SCHAR_MIN == -128, "SCHAR_MIN unexpected"); args[0].b = SCHAR_MIN; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(SCHAR_MIN, result.GetB()); } void InvokeIdentityIntMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[1]; args[0].i = 0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(0, result.GetI()); args[0].i = -1; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-1, result.GetI()); args[0].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(INT_MAX, result.GetI()); args[0].i = INT_MIN; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(INT_MIN, result.GetI()); } void InvokeIdentityDoubleMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[1]; args[0].d = 0.0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(0.0, result.GetD()); args[0].d = -1.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(-1.0, result.GetD()); args[0].d = DBL_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(DBL_MAX, result.GetD()); args[0].d = DBL_MIN; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(DBL_MIN, result.GetD()); } void InvokeSumIntIntMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[2]; args[0].i = 1; args[1].i = 2; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(3, result.GetI()); args[0].i = -2; args[1].i = 5; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(3, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MIN; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-1, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-2, result.GetI()); } void InvokeSumIntIntIntMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[3]; args[0].i = 0; args[1].i = 0; args[2].i = 0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(0, result.GetI()); args[0].i = 1; args[1].i = 2; args[2].i = 3; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(6, result.GetI()); args[0].i = -1; args[1].i = 2; args[2].i = -3; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-2, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MIN; args[2].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(2147483646, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MAX; args[2].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(2147483645, result.GetI()); } void InvokeSumIntIntIntIntMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[4]; args[0].i = 0; args[1].i = 0; args[2].i = 0; args[3].i = 0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(0, result.GetI()); args[0].i = 1; args[1].i = 2; args[2].i = 3; args[3].i = 4; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(10, result.GetI()); args[0].i = -1; args[1].i = 2; args[2].i = -3; args[3].i = 4; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(2, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MIN; args[2].i = INT_MAX; args[3].i = INT_MIN; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-2, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MAX; args[2].i = INT_MAX; args[3].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-4, result.GetI()); } void InvokeSumIntIntIntIntIntMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[5]; args[0].i = 0; args[1].i = 0; args[2].i = 0; args[3].i = 0; args[4].i = 0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(0, result.GetI()); args[0].i = 1; args[1].i = 2; args[2].i = 3; args[3].i = 4; args[4].i = 5; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(15, result.GetI()); args[0].i = -1; args[1].i = 2; args[2].i = -3; args[3].i = 4; args[4].i = -5; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(-3, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MIN; args[2].i = INT_MAX; args[3].i = INT_MIN; args[4].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(2147483645, result.GetI()); args[0].i = INT_MAX; args[1].i = INT_MAX; args[2].i = INT_MAX; args[3].i = INT_MAX; args[4].i = INT_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_EQ(2147483643, result.GetI()); } void InvokeSumDoubleDoubleMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[2]; args[0].d = 0.0; args[1].d = 0.0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(0.0, result.GetD()); args[0].d = 1.0; args[1].d = 2.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(3.0, result.GetD()); args[0].d = 1.0; args[1].d = -2.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(-1.0, result.GetD()); args[0].d = DBL_MAX; args[1].d = DBL_MIN; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(1.7976931348623157e308, result.GetD()); args[0].d = DBL_MAX; args[1].d = DBL_MAX; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(INFINITY, result.GetD()); } void InvokeSumDoubleDoubleDoubleMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[3]; args[0].d = 0.0; args[1].d = 0.0; args[2].d = 0.0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(0.0, result.GetD()); args[0].d = 1.0; args[1].d = 2.0; args[2].d = 3.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(6.0, result.GetD()); args[0].d = 1.0; args[1].d = -2.0; args[2].d = 3.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(2.0, result.GetD()); } void InvokeSumDoubleDoubleDoubleDoubleMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[4]; args[0].d = 0.0; args[1].d = 0.0; args[2].d = 0.0; args[3].d = 0.0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(0.0, result.GetD()); args[0].d = 1.0; args[1].d = 2.0; args[2].d = 3.0; args[3].d = 4.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(10.0, result.GetD()); args[0].d = 1.0; args[1].d = -2.0; args[2].d = 3.0; args[3].d = -4.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(-2.0, result.GetD()); } void InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(bool is_static) { ScopedObjectAccess soa(env_); ArtMethod* method; ObjPtr<mirror::Object> receiver; ReflectionTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D"); ScopedLocalRef<jobject> receiver_ref(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jvalue args[5]; args[0].d = 0.0; args[1].d = 0.0; args[2].d = 0.0; args[3].d = 0.0; args[4].d = 0.0; JValue result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(0.0, result.GetD()); args[0].d = 1.0; args[1].d = 2.0; args[2].d = 3.0; args[3].d = 4.0; args[4].d = 5.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(15.0, result.GetD()); args[0].d = 1.0; args[1].d = -2.0; args[2].d = 3.0; args[3].d = -4.0; args[4].d = 5.0; result = InvokeWithJValues(soa, receiver_ref.get(), jni::EncodeArtMethod(method), args); EXPECT_DOUBLE_EQ(3.0, result.GetD()); } JavaVMExt* vm_; JNIEnv* env_; jclass aioobe_; jclass ase_; jclass sioobe_; }; TEST_F(ReflectionTest, StaticMainMethod) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("Main"); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V"); mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader); ASSERT_TRUE(klass != nullptr); ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V", kRuntimePointerSize); ASSERT_TRUE(method != nullptr); // Start runtime. bool started = runtime_->Start(); CHECK(started); soa.Self()->TransitionFromSuspendedToRunnable(); jvalue args[1]; args[0].l = nullptr; InvokeWithJValues(soa, nullptr, jni::EncodeArtMethod(method), args); } TEST_F(ReflectionTest, StaticNopMethod) { InvokeNopMethod(true); } TEST_F(ReflectionTest, NonStaticNopMethod) { InvokeNopMethod(false); } TEST_F(ReflectionTest, StaticIdentityByteMethod) { InvokeIdentityByteMethod(true); } TEST_F(ReflectionTest, NonStaticIdentityByteMethod) { InvokeIdentityByteMethod(false); } TEST_F(ReflectionTest, StaticIdentityIntMethod) { InvokeIdentityIntMethod(true); } TEST_F(ReflectionTest, NonStaticIdentityIntMethod) { InvokeIdentityIntMethod(false); } TEST_F(ReflectionTest, StaticIdentityDoubleMethod) { InvokeIdentityDoubleMethod(true); } TEST_F(ReflectionTest, NonStaticIdentityDoubleMethod) { InvokeIdentityDoubleMethod(false); } TEST_F(ReflectionTest, StaticSumIntIntMethod) { InvokeSumIntIntMethod(true); } TEST_F(ReflectionTest, NonStaticSumIntIntMethod) { InvokeSumIntIntMethod(false); } TEST_F(ReflectionTest, StaticSumIntIntIntMethod) { InvokeSumIntIntIntMethod(true); } TEST_F(ReflectionTest, NonStaticSumIntIntIntMethod) { InvokeSumIntIntIntMethod(false); } TEST_F(ReflectionTest, StaticSumIntIntIntIntMethod) { InvokeSumIntIntIntIntMethod(true); } TEST_F(ReflectionTest, NonStaticSumIntIntIntIntMethod) { InvokeSumIntIntIntIntMethod(false); } TEST_F(ReflectionTest, StaticSumIntIntIntIntIntMethod) { InvokeSumIntIntIntIntIntMethod(true); } TEST_F(ReflectionTest, NonStaticSumIntIntIntIntIntMethod) { InvokeSumIntIntIntIntIntMethod(false); } TEST_F(ReflectionTest, StaticSumDoubleDoubleMethod) { InvokeSumDoubleDoubleMethod(true); } TEST_F(ReflectionTest, NonStaticSumDoubleDoubleMethod) { InvokeSumDoubleDoubleMethod(false); } TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleMethod) { InvokeSumDoubleDoubleDoubleMethod(true); } TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleMethod) { InvokeSumDoubleDoubleDoubleMethod(false); } TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleMethod) { InvokeSumDoubleDoubleDoubleDoubleMethod(true); } TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleMethod) { InvokeSumDoubleDoubleDoubleDoubleMethod(false); } TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleDoubleMethod) { InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(true); } TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleDoubleMethod) { InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(false); } } // namespace art