summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/jni/jni_env_ext.cc11
-rw-r--r--runtime/jni/jni_env_ext.h12
-rw-r--r--runtime/jni/jni_id_manager.cc55
-rw-r--r--runtime/jni/jni_id_manager.h4
-rw-r--r--runtime/jni/jni_internal.cc160
-rw-r--r--runtime/jni/jni_internal.h21
-rw-r--r--runtime/jni_id_type.h5
-rw-r--r--runtime/mirror/class.cc7
-rw-r--r--runtime/parsed_options.cc13
-rw-r--r--runtime/runtime.cc17
-rw-r--r--runtime/runtime.h12
-rw-r--r--runtime/runtime_options.def2
-rw-r--r--runtime/well_known_classes.cc43
-rw-r--r--runtime/well_known_classes.h12
-rw-r--r--test/1972-jni-id-swap-indices/expected.txt7
-rw-r--r--test/1972-jni-id-swap-indices/info.txt3
-rw-r--r--test/1972-jni-id-swap-indices/jni_id.cc60
-rwxr-xr-xtest/1972-jni-id-swap-indices/run19
-rw-r--r--test/1972-jni-id-swap-indices/src/Main.java74
-rw-r--r--test/1973-jni-id-swap-pointer/expected.txt6
-rw-r--r--test/1973-jni-id-swap-pointer/info.txt1
-rwxr-xr-xtest/1973-jni-id-swap-pointer/run19
-rw-r--r--test/1973-jni-id-swap-pointer/src/Main.java65
-rw-r--r--test/Android.bp1
-rw-r--r--test/knownfailures.json5
25 files changed, 516 insertions, 118 deletions
diff --git a/runtime/jni/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc
index 411794c3a9..e2581dd4bb 100644
--- a/runtime/jni/jni_env_ext.cc
+++ b/runtime/jni/jni_env_ext.cc
@@ -291,11 +291,12 @@ void JNIEnvExt::CheckNoHeldMonitors() {
}
}
-static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
+void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
REQUIRES(Locks::jni_function_table_lock_) {
JNIEnvExt* env = thread->GetJniEnv();
bool check_jni = env->IsCheckJniEnabled();
env->functions = JNIEnvExt::GetFunctionTable(check_jni);
+ env->unchecked_functions_ = GetJniNativeInterface();
}
void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) {
@@ -323,4 +324,12 @@ const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
}
+void JNIEnvExt::ResetFunctionTable() {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_);
+ Runtime* runtime = Runtime::Current();
+ CHECK(runtime != nullptr);
+ runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr);
+}
+
} // namespace art
diff --git a/runtime/jni/jni_env_ext.h b/runtime/jni/jni_env_ext.h
index 924ff514e0..7aae92fa46 100644
--- a/runtime/jni/jni_env_ext.h
+++ b/runtime/jni/jni_env_ext.h
@@ -27,7 +27,10 @@
namespace art {
+class ArtMethod;
+class ArtField;
class JavaVMExt;
+class ScopedObjectAccessAlreadyRunnable;
namespace mirror {
class Object;
@@ -131,6 +134,9 @@ class JNIEnvExt : public JNIEnv {
// Set the functions to the runtime shutdown functions.
void SetFunctionsToRuntimeShutdownFunctions();
+ // Set the functions to the new JNI functions based on Runtime::GetJniIdType.
+ void UpdateJniFunctionsPointer();
+
// Set the function table override. This will install the override (or original table, if null)
// to all threads.
// Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI.
@@ -143,6 +149,9 @@ class JNIEnvExt : public JNIEnv {
static const JNINativeInterface* GetFunctionTable(bool check_jni)
REQUIRES(Locks::jni_function_table_lock_);
+ static void ResetFunctionTable()
+ REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
+
private:
// Checking "locals" requires the mutator lock, but at creation time we're
// really only interested in validity, which isn't changing. To avoid grabbing
@@ -179,7 +188,7 @@ class JNIEnvExt : public JNIEnv {
ReferenceTable monitors_;
// Used by -Xcheck:jni.
- const JNINativeInterface* unchecked_functions_;
+ JNINativeInterface const* unchecked_functions_;
// All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
// to ensure that only monitors locked in this native frame are being unlocked, and that at
@@ -202,6 +211,7 @@ class JNIEnvExt : public JNIEnv {
template<bool kEnableIndexIds> friend class JNI;
friend class ScopedJniEnvLocalRefState;
friend class Thread;
+ friend void ThreadResetFunctionTable(Thread* thread, void* arg);
ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets);
};
diff --git a/runtime/jni/jni_id_manager.cc b/runtime/jni/jni_id_manager.cc
index c28d813f18..88c6ba12d7 100644
--- a/runtime/jni/jni_id_manager.cc
+++ b/runtime/jni/jni_id_manager.cc
@@ -26,6 +26,7 @@
#include "gc/allocation_listener.h"
#include "gc/heap.h"
#include "jni/jni_internal.h"
+#include "jni_id_type.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
@@ -166,18 +167,28 @@ template <> ArtMethod* Canonicalize(ArtMethod* t) {
// We increment the id by 2 each time to allow us to use the LSB as a flag that the ID is an index
// and not a pointer. This gives us 2**31 unique methods that can be addressed on 32-bit art, which
// should be more than enough.
-template <> uintptr_t JniIdManager::GetNextId<ArtField>() {
- uintptr_t res = next_field_id_;
- next_field_id_ += 2;
- CHECK_GT(next_field_id_, res) << "jfieldID Overflow";
- return res;
+template <> uintptr_t JniIdManager::GetNextId<ArtField>(JniIdType type, ArtField* f) {
+ if (LIKELY(type == JniIdType::kIndices)) {
+ uintptr_t res = next_field_id_;
+ next_field_id_ += 2;
+ CHECK_GT(next_field_id_, res) << "jfieldID Overflow";
+ return res;
+ } else {
+ DCHECK_EQ(type, JniIdType::kSwapablePointer);
+ return reinterpret_cast<uintptr_t>(f);
+ }
}
-template <> uintptr_t JniIdManager::GetNextId<ArtMethod>() {
- uintptr_t res = next_method_id_;
- next_method_id_ += 2;
- CHECK_GT(next_method_id_, res) << "jmethodID Overflow";
- return res;
+template <> uintptr_t JniIdManager::GetNextId<ArtMethod>(JniIdType type, ArtMethod* m) {
+ if (LIKELY(type == JniIdType::kIndices)) {
+ uintptr_t res = next_method_id_;
+ next_method_id_ += 2;
+ CHECK_GT(next_method_id_, res) << "jmethodID Overflow";
+ return res;
+ } else {
+ DCHECK_EQ(type, JniIdType::kSwapablePointer);
+ return reinterpret_cast<uintptr_t>(m);
+ }
}
template <> std::vector<ArtField*>& JniIdManager::GetGenericMap<ArtField>() {
return field_id_map_;
@@ -199,7 +210,9 @@ template <> size_t JniIdManager::GetLinearSearchStartId<ArtMethod>(ArtMethod* m)
}
template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t) {
- if (!Runtime::Current()->JniIdsAreIndices() || t == nullptr) {
+ Runtime* runtime = Runtime::Current();
+ JniIdType id_type = runtime->GetJniIdType();
+ if (id_type == JniIdType::kPointer || t == nullptr) {
return reinterpret_cast<uintptr_t>(t);
}
Thread* self = Thread::Current();
@@ -257,12 +270,18 @@ template <typename ArtType> uintptr_t JniIdManager::EncodeGenericId(ArtType* t)
return IndexToId(index);
}
}
- cur_id = GetNextId<ArtType>();
- size_t cur_index = IdToIndex(cur_id);
- std::vector<ArtType*>& vec = GetGenericMap<ArtType>();
- vec.reserve(cur_index + 1);
- vec.resize(std::max(vec.size(), cur_index + 1), nullptr);
- vec[cur_index] = t;
+ cur_id = GetNextId(id_type, t);
+ if (UNLIKELY(id_type == JniIdType::kIndices)) {
+ DCHECK_EQ(cur_id % 2, 1u);
+ size_t cur_index = IdToIndex(cur_id);
+ std::vector<ArtType*>& vec = GetGenericMap<ArtType>();
+ vec.reserve(cur_index + 1);
+ vec.resize(std::max(vec.size(), cur_index + 1), nullptr);
+ vec[cur_index] = t;
+ } else {
+ DCHECK_EQ(cur_id % 2, 0u);
+ DCHECK_EQ(cur_id, reinterpret_cast<uintptr_t>(t));
+ }
if (ids.IsNull()) {
if (kIsDebugBuild && !IsObsolete(t)) {
CHECK_NE(deferred_allocation_refcount_, 0u)
@@ -291,7 +310,7 @@ jmethodID JniIdManager::EncodeMethodId(ArtMethod* method) {
}
template <typename ArtType> ArtType* JniIdManager::DecodeGenericId(uintptr_t t) {
- if (Runtime::Current()->JniIdsAreIndices() && (t % 2) == 1) {
+ if (Runtime::Current()->GetJniIdType() == JniIdType::kIndices && (t % 2) == 1) {
ReaderMutexLock mu(Thread::Current(), *Locks::jni_id_lock_);
size_t index = IdToIndex(t);
DCHECK_GT(GetGenericMap<ArtType>().size(), index);
diff --git a/runtime/jni/jni_id_manager.h b/runtime/jni/jni_id_manager.h
index 5e5c581be4..d2948152fb 100644
--- a/runtime/jni/jni_id_manager.h
+++ b/runtime/jni/jni_id_manager.h
@@ -24,6 +24,7 @@
#include "art_field.h"
#include "art_method.h"
#include "base/mutex.h"
+#include "jni_id_type.h"
namespace art {
namespace jni {
@@ -45,7 +46,8 @@ class JniIdManager {
template <typename ArtType>
ArtType* DecodeGenericId(uintptr_t input) REQUIRES(!Locks::jni_id_lock_);
template <typename ArtType> std::vector<ArtType*>& GetGenericMap() REQUIRES(Locks::jni_id_lock_);
- template <typename ArtType> uintptr_t GetNextId() REQUIRES(Locks::jni_id_lock_);
+ template <typename ArtType>
+ uintptr_t GetNextId(JniIdType id, ArtType* t) REQUIRES(Locks::jni_id_lock_);
template <typename ArtType>
size_t GetLinearSearchStartId(ArtType* t) REQUIRES(Locks::jni_id_lock_);
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 3d388815ac..882e10f12d 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -206,21 +206,6 @@ static std::string NormalizeJniClassDescriptor(const char* name) {
return result;
}
-static void ThrowNoSuchMethodError(ScopedObjectAccess& soa,
- ObjPtr<mirror::Class> c,
- const char* name,
- const char* sig,
- const char* kind)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::string temp;
- soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
- "no %s method \"%s.%s%s\"",
- kind,
- c->GetDescriptor(&temp),
- name,
- sig);
-}
-
static void ReportInvalidJNINativeMethod(const ScopedObjectAccess& soa,
ObjPtr<mirror::Class> c,
const char* kind,
@@ -236,42 +221,11 @@ static void ReportInvalidJNINativeMethod(const ScopedObjectAccess& soa,
idx);
}
-static ObjPtr<mirror::Class> EnsureInitialized(Thread* self, ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (LIKELY(klass->IsInitialized())) {
- return klass;
- }
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
- return nullptr;
- }
- return h_klass.Get();
-}
-
template<bool kEnableIndexIds>
static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
const char* name, const char* sig, bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class));
- if (c == nullptr) {
- return nullptr;
- }
- ArtMethod* method = nullptr;
- auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- if (c->IsInterface()) {
- method = c->FindInterfaceMethod(name, sig, pointer_size);
- } else {
- method = c->FindClassMethod(name, sig, pointer_size);
- }
- if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
- method = nullptr;
- }
- if (method == nullptr || method->IsStatic() != is_static) {
- ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
- return nullptr;
- }
- return jni::EncodeArtMethod<kEnableIndexIds>(method);
+ return jni::EncodeArtMethod<kEnableIndexIds>(FindMethodJNI(soa, jni_class, name, sig, is_static));
}
template<bool kEnableIndexIds>
@@ -310,6 +264,88 @@ template<bool kEnableIndexIds>
static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, const char* name,
const char* sig, bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ return jni::EncodeArtField<kEnableIndexIds>(FindFieldJNI(soa, jni_class, name, sig, is_static));
+}
+
+static void ThrowAIOOBE(ScopedObjectAccess& soa,
+ ObjPtr<mirror::Array> array,
+ jsize start,
+ jsize length,
+ const char* identifier)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string type(array->PrettyTypeOf());
+ soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "%s offset=%d length=%d %s.length=%d",
+ type.c_str(), start, length, identifier, array->GetLength());
+}
+
+static void ThrowSIOOBE(ScopedObjectAccess& soa, jsize start, jsize length,
+ jsize array_length)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
+ "offset=%d length=%d string.length()=%d", start, length,
+ array_length);
+}
+
+static void ThrowNoSuchMethodError(const ScopedObjectAccess& soa,
+ ObjPtr<mirror::Class> c,
+ const char* name,
+ const char* sig,
+ const char* kind)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string temp;
+ soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
+ "no %s method \"%s.%s%s\"",
+ kind,
+ c->GetDescriptor(&temp),
+ name,
+ sig);
+}
+
+static ObjPtr<mirror::Class> EnsureInitialized(Thread* self, ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (LIKELY(klass->IsInitialized())) {
+ return klass;
+ }
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_klass(hs.NewHandle(klass));
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
+ return nullptr;
+ }
+ return h_klass.Get();
+}
+
+ArtMethod* FindMethodJNI(const ScopedObjectAccess& soa,
+ jclass jni_class,
+ const char* name,
+ const char* sig,
+ bool is_static) {
+ ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class));
+ if (c == nullptr) {
+ return nullptr;
+ }
+ ArtMethod* method = nullptr;
+ auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ if (c->IsInterface()) {
+ method = c->FindInterfaceMethod(name, sig, pointer_size);
+ } else {
+ method = c->FindClassMethod(name, sig, pointer_size);
+ }
+ if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
+ method = nullptr;
+ }
+ if (method == nullptr || method->IsStatic() != is_static) {
+ ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
+ return nullptr;
+ }
+ return method;
+}
+
+ArtField* FindFieldJNI(const ScopedObjectAccess& soa,
+ jclass jni_class,
+ const char* name,
+ const char* sig,
+ bool is_static) {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> c(
hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class))));
@@ -359,27 +395,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
sig, name, c->GetDescriptor(&temp));
return nullptr;
}
- return jni::EncodeArtField<kEnableIndexIds>(field);
-}
-
-static void ThrowAIOOBE(ScopedObjectAccess& soa,
- ObjPtr<mirror::Array> array,
- jsize start,
- jsize length,
- const char* identifier)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::string type(array->PrettyTypeOf());
- soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
- "%s offset=%d length=%d %s.length=%d",
- type.c_str(), start, length, identifier, array->GetLength());
-}
-
-static void ThrowSIOOBE(ScopedObjectAccess& soa, jsize start, jsize length,
- jsize array_length)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
- "offset=%d length=%d string.length()=%d", start, length,
- array_length);
+ return field;
}
int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause)
@@ -2953,11 +2969,11 @@ struct JniNativeInterfaceFunctions {
const JNINativeInterface* GetJniNativeInterface() {
// The template argument is passed down through the Encode/DecodeArtMethod/Field calls so if
- // JniIdsAreIndices is false the calls will be a simple cast with no branches. This ensures that
+ // JniIdType is kPointer the calls will be a simple cast with no branches. This ensures that
// the normal case is still fast.
- return Runtime::Current()->JniIdsAreIndices()
- ? &JniNativeInterfaceFunctions<true>::gJniNativeInterface
- : &JniNativeInterfaceFunctions<false>::gJniNativeInterface;
+ return Runtime::Current()->GetJniIdType() == JniIdType::kPointer
+ ? &JniNativeInterfaceFunctions<false>::gJniNativeInterface
+ : &JniNativeInterfaceFunctions<true>::gJniNativeInterface;
}
void (*gJniSleepForeverStub[])() = {
diff --git a/runtime/jni/jni_internal.h b/runtime/jni/jni_internal.h
index b6e106cc88..da1792231b 100644
--- a/runtime/jni/jni_internal.h
+++ b/runtime/jni/jni_internal.h
@@ -28,6 +28,7 @@ namespace art {
class ArtField;
class ArtMethod;
+class ScopedObjectAccess;
const JNINativeInterface* GetJniNativeInterface();
const JNINativeInterface* GetRuntimeShutdownNativeInterface();
@@ -41,6 +42,22 @@ void JniInitializeNativeCallerCheck();
// Removes native stack checking state.
void JniShutdownNativeCallerCheck();
+// Finds the method using JNI semantics and initializes any classes. Does not encode the method in a
+// JNI id
+ArtMethod* FindMethodJNI(const ScopedObjectAccess& soa,
+ jclass java_class,
+ const char* name,
+ const char* sig,
+ bool is_static) REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Finds the field using JNI semantics and initializes any classes. Does not encode the method in a
+// JNI id.
+ArtField* FindFieldJNI(const ScopedObjectAccess& soa,
+ jclass java_class,
+ const char* name,
+ const char* sig,
+ bool is_static) REQUIRES_SHARED(Locks::mutator_lock_);
+
namespace jni {
// We want to maintain a branchless fast-path for performance reasons. The JniIdManager is the
@@ -72,7 +89,7 @@ static inline ArtField* DecodeArtField(jfieldID fid) {
template <bool kEnableIndexIds = true>
ALWAYS_INLINE
static inline jfieldID EncodeArtField(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kEnableIndexIds && Runtime::Current()->JniIdsAreIndices()) {
+ if (kEnableIndexIds && Runtime::Current()->GetJniIdType() != JniIdType::kPointer) {
return Runtime::Current()->GetJniIdManager()->EncodeFieldId(field);
} else {
return reinterpret_cast<jfieldID>(field);
@@ -83,7 +100,7 @@ template <bool kEnableIndexIds = true>
ALWAYS_INLINE
static inline jmethodID EncodeArtMethod(ArtMethod* art_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kEnableIndexIds && Runtime::Current()->JniIdsAreIndices()) {
+ if (kEnableIndexIds && Runtime::Current()->GetJniIdType() != JniIdType::kPointer) {
return Runtime::Current()->GetJniIdManager()->EncodeMethodId(art_method);
} else {
return reinterpret_cast<jmethodID>(art_method);
diff --git a/runtime/jni_id_type.h b/runtime/jni_id_type.h
index 7802ec6c37..3f952b638c 100644
--- a/runtime/jni_id_type.h
+++ b/runtime/jni_id_type.h
@@ -28,7 +28,10 @@ enum class JniIdType {
// All Jni method/field IDs are indices into a table.
kIndices,
- // The current default provider. Used if you run -XjdwpProvider:default
+ // All Jni method/field IDs are pointers to the corresponding Art{Field,Method} type but we
+ // keep around extra information support changing modes to either kPointer or kIndices later.
+ kSwapablePointer,
+
kDefault = kPointer,
};
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 846517b524..a299f34ada 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -39,6 +39,7 @@
#include "gc/heap-inl.h"
#include "handle_scope-inl.h"
#include "hidden_api.h"
+#include "jni_id_type.h"
#include "subtype_check.h"
#include "method.h"
#include "object-inl.h"
@@ -1595,7 +1596,7 @@ ObjPtr<PointerArray> Class::GetMethodIds() {
}
}
ObjPtr<PointerArray> Class::GetOrCreateMethodIds(Handle<Class> h_this) {
- DCHECK(Runtime::Current()->JniIdsAreIndices()) << "JNI Ids are pointers!";
+ DCHECK_NE(Runtime::Current()->GetJniIdType(), JniIdType::kPointer) << "JNI Ids are pointers!";
Thread* self = Thread::Current();
ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
if (ext.IsNull()) {
@@ -1614,7 +1615,7 @@ ObjPtr<PointerArray> Class::GetStaticFieldIds() {
}
}
ObjPtr<PointerArray> Class::GetOrCreateStaticFieldIds(Handle<Class> h_this) {
- DCHECK(Runtime::Current()->JniIdsAreIndices()) << "JNI Ids are pointers!";
+ DCHECK_NE(Runtime::Current()->GetJniIdType(), JniIdType::kPointer) << "JNI Ids are pointers!";
Thread* self = Thread::Current();
ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
if (ext.IsNull()) {
@@ -1632,7 +1633,7 @@ ObjPtr<PointerArray> Class::GetInstanceFieldIds() {
}
}
ObjPtr<PointerArray> Class::GetOrCreateInstanceFieldIds(Handle<Class> h_this) {
- DCHECK(Runtime::Current()->JniIdsAreIndices()) << "JNI Ids are pointers!";
+ DCHECK_NE(Runtime::Current()->GetJniIdType(), JniIdType::kPointer) << "JNI Ids are pointers!";
Thread* self = Thread::Current();
ObjPtr<ClassExt> ext(EnsureExtDataPresent(h_this, self));
if (ext.IsNull()) {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index fb1e6b7037..bfedfa9c1b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -27,6 +27,7 @@
#include "base/utils.h"
#include "debugger.h"
#include "gc/heap.h"
+#include "jni_id_type.h"
#include "monitor.h"
#include "runtime.h"
#include "ti/agent.h"
@@ -368,8 +369,13 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::FastClassNotFoundException)
.Define("-Xopaque-jni-ids:_")
- .WithType<bool>()
- .WithValueMap({{"true", true}, {"false", false}})
+ .WithType<JniIdType>()
+ .WithValueMap({{"true", JniIdType::kIndices},
+ {"false", JniIdType::kPointer},
+ {"swapable", JniIdType::kSwapablePointer},
+ {"pointer", JniIdType::kPointer},
+ {"indices", JniIdType::kIndices},
+ {"default", JniIdType::kDefault}})
.IntoKey(M::OpaqueJniIds)
.Define("-XX:VerifierMissingKThrowFatal=_")
.WithType<bool>()
@@ -792,7 +798,8 @@ void ParsedOptions::Usage(const char* fmt, ...) {
"(Enable new and experimental agent support)\n");
UsageMessage(stream, " -Xexperimental:agents"
"(Enable new and experimental agent support)\n");
- UsageMessage(stream, " -Xopaque-jni-ids:{true,false} (Use opauque integers for jni ids)\n");
+ UsageMessage(stream, " -Xopaque-jni-ids:{true,false,swapable}");
+ UsageMessage(stream, "(Use opauque integers for jni ids, yes, no or punt for later)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 534d69d785..1b12f6a4b4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1306,12 +1306,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess);
- if (!runtime_options.Exists(Opt::OpaqueJniIds)) {
- jni_ids_indirection_ = JniIdType::kDefault;
- } else {
- jni_ids_indirection_ = *runtime_options.Get(Opt::OpaqueJniIds) ? JniIdType::kIndices
- : JniIdType::kPointer;
- }
+ jni_ids_indirection_ = runtime_options.GetOrDefault(Opt::OpaqueJniIds);
plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
agent_specs_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
@@ -2927,4 +2922,14 @@ void Runtime::SetSignalHookDebuggable(bool value) {
SkipAddSignalHandler(value);
}
+void Runtime::SetJniIdType(JniIdType t) {
+ CHECK(CanSetJniIdType()) << "Not allowed to change id type!";
+ if (t == GetJniIdType()) {
+ return;
+ }
+ jni_ids_indirection_ = t;
+ JNIEnvExt::ResetFunctionTable();
+ WellKnownClasses::HandleJniIdTypeChange(Thread::Current()->GetJniEnv());
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index a276b8768b..ca017614fa 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -841,10 +841,18 @@ class Runtime {
return jdwp_provider_;
}
- bool JniIdsAreIndices() const {
- return jni_ids_indirection_ != JniIdType::kPointer;
+ JniIdType GetJniIdType() const {
+ return jni_ids_indirection_;
}
+ bool CanSetJniIdType() const {
+ return GetJniIdType() == JniIdType::kSwapablePointer;
+ }
+
+ // Changes the JniIdType to the given type. Only allowed if CanSetJniIdType(). All threads must be
+ // suspended to call this function.
+ void SetJniIdType(JniIdType t);
+
uint32_t GetVerifierLoggingThresholdMs() const {
return verifier_logging_threshold_ms_;
}
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index c384eda265..e9d1511ae9 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -77,7 +77,7 @@ RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, true)
RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false)
-RUNTIME_OPTIONS_KEY (bool, OpaqueJniIds, false) // -Xopaque-jni-ids:{true, false}
+RUNTIME_OPTIONS_KEY (JniIdType, OpaqueJniIds, JniIdType::kDefault) // -Xopaque-jni-ids:{true, false}
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold)
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 279b45b755..002bacb294 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -28,13 +28,16 @@
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "hidden_api.h"
+#include "hidden_api_jni.h"
#include "jni/jni_internal.h"
+#include "jni_id_type.h"
#include "mirror/class.h"
#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
+#include "scoped_thread_state_change.h"
#include "thread-current-inl.h"
namespace art {
@@ -172,8 +175,18 @@ static jclass CacheClass(JNIEnv* env, const char* jni_class_name) {
static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static,
const char* name, const char* signature) {
- jfieldID fid = is_static ? env->GetStaticFieldID(c, name, signature) :
- env->GetFieldID(c, name, signature);
+ jfieldID fid;
+ {
+ ScopedObjectAccess soa(env);
+ hiddenapi::ScopedCorePlatformApiCheck scpac;
+ if (Runtime::Current()->GetJniIdType() != JniIdType::kSwapablePointer) {
+ fid = jni::EncodeArtField</*kEnableIndexIds*/ true>(
+ FindFieldJNI(soa, c, name, signature, is_static));
+ } else {
+ fid = jni::EncodeArtField</*kEnableIndexIds*/ false>(
+ FindFieldJNI(soa, c, name, signature, is_static));
+ }
+ }
if (fid == nullptr) {
ScopedObjectAccess soa(env);
if (soa.Self()->IsExceptionPending()) {
@@ -189,8 +202,18 @@ static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static,
static jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
const char* name, const char* signature) {
- jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) :
- env->GetMethodID(c, name, signature);
+ jmethodID mid;
+ {
+ ScopedObjectAccess soa(env);
+ hiddenapi::ScopedCorePlatformApiCheck scpac;
+ if (Runtime::Current()->GetJniIdType() != JniIdType::kSwapablePointer) {
+ mid = jni::EncodeArtMethod</*kEnableIndexIds*/ true>(
+ FindMethodJNI(soa, c, name, signature, is_static));
+ } else {
+ mid = jni::EncodeArtMethod</*kEnableIndexIds*/ false>(
+ FindMethodJNI(soa, c, name, signature, is_static));
+ }
+ }
if (mid == nullptr) {
ScopedObjectAccess soa(env);
if (soa.Self()->IsExceptionPending()) {
@@ -348,6 +371,13 @@ void WellKnownClasses::Init(JNIEnv* env) {
org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
+ InitFieldsAndMethodsOnly(env);
+}
+
+void WellKnownClasses::InitFieldsAndMethodsOnly(JNIEnv* env) {
+ hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
+ hiddenapi::EnforcementPolicy::kDisabled);
+
dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;");
dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V");
dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(env, dalvik_system_VMRuntime, true, "hiddenApiUsed", "(ILjava/lang/String;Ljava/lang/String;IZ)V");
@@ -452,6 +482,11 @@ void WellKnownClasses::LateInit(JNIEnv* env) {
"[Ljava/lang/Object;)Ljava/lang/Object;");
}
+void WellKnownClasses::HandleJniIdTypeChange(JNIEnv* env) {
+ WellKnownClasses::InitFieldsAndMethodsOnly(env);
+ WellKnownClasses::LateInit(env);
+}
+
void WellKnownClasses::Clear() {
dalvik_annotation_optimization_CriticalNative = nullptr;
dalvik_annotation_optimization_FastNative = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index d7acd5789f..249dfa0724 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -35,11 +35,15 @@ class Class;
struct WellKnownClasses {
public:
- static void Init(JNIEnv* env); // Run before native methods are registered.
- static void LateInit(JNIEnv* env); // Run after native methods are registered.
+ // Run before native methods are registered.
+ static void Init(JNIEnv* env);
+ // Run after native methods are registered.
+ static void LateInit(JNIEnv* env);
static void Clear();
+ static void HandleJniIdTypeChange(JNIEnv* env);
+
static void InitStringInit(ObjPtr<mirror::Class> string_class,
ObjPtr<mirror::Class> string_builder_class)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -48,6 +52,10 @@ struct WellKnownClasses {
static ObjPtr<mirror::Class> ToClass(jclass global_jclass) REQUIRES_SHARED(Locks::mutator_lock_);
+ private:
+ static void InitFieldsAndMethodsOnly(JNIEnv* env);
+
+ public:
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_system_BaseDexClassLoader;
diff --git a/test/1972-jni-id-swap-indices/expected.txt b/test/1972-jni-id-swap-indices/expected.txt
new file mode 100644
index 0000000000..a22979ff8d
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/expected.txt
@@ -0,0 +1,7 @@
+JNI_OnLoad called
+JNI Type is: SwapablePointer
+pointer ID looks like a pointer!
+JNI Type is: Indices
+index ID looks like an index!
+pointer ID remains a pointer!
+index WKC ID looks like an index!
diff --git a/test/1972-jni-id-swap-indices/info.txt b/test/1972-jni-id-swap-indices/info.txt
new file mode 100644
index 0000000000..8b9c215940
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/info.txt
@@ -0,0 +1,3 @@
+Tests changing from SwapablePointer to indices for JniIdType
+
+Also tests that WellKnownClasses jmethodIDs are indices after swap. \ No newline at end of file
diff --git a/test/1972-jni-id-swap-indices/jni_id.cc b/test/1972-jni-id-swap-indices/jni_id.cc
new file mode 100644
index 0000000000..7de7131ca8
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/jni_id.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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 <jni.h>
+#include <sstream>
+#include <stdio.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include "jni/java_vm_ext.h"
+#include "runtime.h"
+
+namespace art {
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_GetMethodId(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ bool is_static,
+ jclass target,
+ jstring name,
+ jstring sig) {
+ auto get_id = is_static ? env->functions->GetStaticMethodID : env->functions->GetMethodID;
+ jboolean cpy;
+ const char* cname = env->GetStringUTFChars(name, &cpy);
+ const char* csig = env->GetStringUTFChars(sig, &cpy);
+ jlong res = static_cast<jlong>(reinterpret_cast<intptr_t>(get_id(env, target, cname, csig)));
+ env->ReleaseStringUTFChars(name, cname);
+ env->ReleaseStringUTFChars(sig, csig);
+ return res;
+}
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_GetJniType(JNIEnv* env, jclass k ATTRIBUTE_UNUSED) {
+ std::ostringstream oss;
+ oss << Runtime::Current()->GetJniIdType();
+ return env->NewStringUTF(oss.str().c_str());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_SetToPointerIds(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass k ATTRIBUTE_UNUSED) {
+ Runtime::Current()->SetJniIdType(JniIdType::kPointer);
+}
+extern "C" JNIEXPORT void JNICALL Java_Main_SetToIndexIds(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass k ATTRIBUTE_UNUSED) {
+ Runtime::Current()->SetJniIdType(JniIdType::kIndices);
+}
+
+} // namespace art
diff --git a/test/1972-jni-id-swap-indices/run b/test/1972-jni-id-swap-indices/run
new file mode 100755
index 0000000000..7d26b24902
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+args=$(echo "$@" | sed 's/--runtime-option -Xopaque-jni-ids\:true//g')
+
+./default-run $args --android-runtime-option -Xopaque-jni-ids:swapable \ No newline at end of file
diff --git a/test/1972-jni-id-swap-indices/src/Main.java b/test/1972-jni-id-swap-indices/src/Main.java
new file mode 100644
index 0000000000..f2bb1ab00c
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/src/Main.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+import java.util.function.Consumer;
+
+public class Main {
+ public static final boolean PRINT = false;
+
+ public static void doNothingPtr() {}
+
+ public static void doNothingIdx() {}
+
+ public static void DbgPrint(String str) {
+ if (PRINT) {
+ System.out.println(str);
+ }
+ }
+
+ public static long GetId(String name) {
+ return GetMethodId(true, Main.class, name, "()V");
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ System.out.println("JNI Type is: " + GetJniType());
+ long expect_ptr_id = GetId("doNothingPtr");
+ DbgPrint(String.format("expected_ptr_id is 0x%x", expect_ptr_id));
+ if (expect_ptr_id % 4 != 0) {
+ throw new Error("ID " + expect_ptr_id + " is not aligned!");
+ } else {
+ System.out.println("pointer ID looks like a pointer!");
+ }
+ SetToIndexIds();
+ System.out.println("JNI Type is: " + GetJniType());
+ long expect_idx_id = GetId("doNothingIdx");
+ DbgPrint(String.format("expected_idx_id is 0x%x", expect_idx_id));
+ if (expect_idx_id % 2 != 1) {
+ throw new Error("ID " + expect_ptr_id + " is not odd!");
+ } else {
+ System.out.println("index ID looks like an index!");
+ }
+ long again_ptr_id = GetId("doNothingPtr");
+ if (expect_ptr_id != again_ptr_id) {
+ throw new Error(
+ "Got different id values for same method. " + expect_ptr_id + " vs " + again_ptr_id);
+ } else {
+ System.out.println("pointer ID remains a pointer!");
+ }
+ long well_known_id = GetMethodId(false, Consumer.class, "accept", "(Ljava/lang/Object;)V");
+ DbgPrint(String.format("well_known_id is 0x%x", well_known_id));
+ if (well_known_id % 2 != 1) {
+ throw new Error("WKC ID " + well_known_id + " is not odd!");
+ } else {
+ System.out.println("index WKC ID looks like an index!");
+ }
+ }
+
+ private static native String GetJniType();
+ private static native void SetToIndexIds();
+ private static native long GetMethodId(boolean is_static, Class k, String name, String sig);
+}
diff --git a/test/1973-jni-id-swap-pointer/expected.txt b/test/1973-jni-id-swap-pointer/expected.txt
new file mode 100644
index 0000000000..5ea78580ae
--- /dev/null
+++ b/test/1973-jni-id-swap-pointer/expected.txt
@@ -0,0 +1,6 @@
+JNI_OnLoad called
+JNI Type is: SwapablePointer
+pointer ID looks like a pointer!
+JNI Type is: Pointer
+pointer2 ID looks like a pointer!
+pointer ID remains a pointer!
diff --git a/test/1973-jni-id-swap-pointer/info.txt b/test/1973-jni-id-swap-pointer/info.txt
new file mode 100644
index 0000000000..42f7ef2a60
--- /dev/null
+++ b/test/1973-jni-id-swap-pointer/info.txt
@@ -0,0 +1 @@
+Tests changing from SwapablePointer to indices for JniIdType \ No newline at end of file
diff --git a/test/1973-jni-id-swap-pointer/run b/test/1973-jni-id-swap-pointer/run
new file mode 100755
index 0000000000..7d26b24902
--- /dev/null
+++ b/test/1973-jni-id-swap-pointer/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+args=$(echo "$@" | sed 's/--runtime-option -Xopaque-jni-ids\:true//g')
+
+./default-run $args --android-runtime-option -Xopaque-jni-ids:swapable \ No newline at end of file
diff --git a/test/1973-jni-id-swap-pointer/src/Main.java b/test/1973-jni-id-swap-pointer/src/Main.java
new file mode 100644
index 0000000000..755fbd5d23
--- /dev/null
+++ b/test/1973-jni-id-swap-pointer/src/Main.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+public class Main {
+ public static final boolean PRINT = false;
+
+ public static void doNothingPtr() {}
+
+ public static void doNothingIdx() {}
+
+ public static void DbgPrint(String str) {
+ if (PRINT) {
+ System.out.println(str);
+ }
+ }
+
+ public static long GetId(String name) {
+ return GetMethodId(true, Main.class, name, "()V");
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ System.out.println("JNI Type is: " + GetJniType());
+ long expect_ptr_id = GetId("doNothingPtr");
+ DbgPrint(String.format("expected_ptr_id is 0x%x", expect_ptr_id));
+ if (expect_ptr_id % 4 != 0) {
+ throw new Error("ID " + expect_ptr_id + " is not aligned!");
+ } else {
+ System.out.println("pointer ID looks like a pointer!");
+ }
+ SetToPointerIds();
+ System.out.println("JNI Type is: " + GetJniType());
+ long expect_ptr_id2 = GetId("doNothingIdx");
+ DbgPrint(String.format("expected_ptr_id2 is 0x%x", expect_ptr_id2));
+ if (expect_ptr_id2 % 4 != 0) {
+ throw new Error("ID " + expect_ptr_id + " is not aligned!");
+ } else {
+ System.out.println("pointer2 ID looks like a pointer!");
+ }
+ long again_ptr_id = GetId("doNothingPtr");
+ if (expect_ptr_id != again_ptr_id) {
+ throw new Error(
+ "Got different id values for same method. " + expect_ptr_id + " vs " + again_ptr_id);
+ } else {
+ System.out.println("pointer ID remains a pointer!");
+ }
+ }
+
+ private static native String GetJniType();
+ private static native void SetToPointerIds();
+ private static native long GetMethodId(boolean is_static, Class k, String name, String sig);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 91d93935a3..d1382ab768 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -509,6 +509,7 @@ cc_defaults {
"1001-app-image-regions/app_image_regions.cc",
"1002-notify-startup/startup_interface.cc",
"1947-breakpoint-redefine-deopt/check_deopt.cc",
+ "1972-jni-id-swap-indices/jni_id.cc",
"common/runtime_state.cc",
"common/stack_inspect.cc",
],
diff --git a/test/knownfailures.json b/test/knownfailures.json
index bd32647633..85eec1b515 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1114,7 +1114,10 @@
"1001-app-image-regions",
"1339-dead-reference-safe",
"1951-monitor-enter-no-suspend",
- "1957-error-ext"],
+ "1957-error-ext",
+ "1972-jni-id-swap-indices",
+ "1973-jni-id-swap-pointer"
+ ],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},