summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorIan Rogers <irogers@google.com>2014-07-17 11:09:10 -0700
committerIan Rogers <irogers@google.com>2014-08-08 08:29:53 -0700
commit68d8b42ddec39ec0174162d90d4abaa004d1983e (patch)
tree0bb6ccf3b996bb8363c10d07aa39cde221980602 /runtime
parent9c522c2cbbf50dc687728747b37ad59985750b65 (diff)
downloadart-68d8b42ddec39ec0174162d90d4abaa004d1983e.zip
art-68d8b42ddec39ec0174162d90d4abaa004d1983e.tar.gz
art-68d8b42ddec39ec0174162d90d4abaa004d1983e.tar.bz2
Wire up check JNI force copy mode.
Increase check JNI checks. Break apart jni_internal.h in to jni_env_ext.h and java_vm_ext.h. Fix the abuse of ScopedObjectAccess/annotalysis by ScopedCheck in the case of VM routines. Make class loader override and shared library class loader JNI global references rather than mirror pointers. Clean-ups to native bridge. Change-Id: If7c6110b5aade7a402bfb67534af86a7b2cdeb55
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Android.mk2
-rw-r--r--runtime/base/mutex.cc6
-rw-r--r--runtime/base/mutex.h7
-rw-r--r--runtime/check_jni.cc3795
-rw-r--r--runtime/check_jni.h29
-rw-r--r--runtime/class_linker.cc3
-rw-r--r--runtime/common_runtime_test.cc17
-rw-r--r--runtime/common_runtime_test.h2
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc8
-rw-r--r--runtime/gc/heap.cc11
-rw-r--r--runtime/gc/heap.h1
-rw-r--r--runtime/gc/space/rosalloc_space.cc6
-rw-r--r--runtime/gc/space/rosalloc_space.h1
-rw-r--r--runtime/indirect_reference_table.cc3
-rw-r--r--runtime/indirect_reference_table.h4
-rw-r--r--runtime/java_vm_ext.cc829
-rw-r--r--runtime/java_vm_ext.h185
-rw-r--r--runtime/jni_env_ext-inl.h (renamed from runtime/jni_internal-inl.h)8
-rw-r--r--runtime/jni_env_ext.cc89
-rw-r--r--runtime/jni_env_ext.h115
-rw-r--r--runtime/jni_internal.cc906
-rw-r--r--runtime/jni_internal.h191
-rw-r--r--runtime/jni_internal_test.cc975
-rw-r--r--runtime/mirror/class.h8
-rw-r--r--runtime/mirror/object_test.cc7
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc2
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc5
-rw-r--r--runtime/native/java_lang_DexCache.cc1
-rw-r--r--runtime/native/java_lang_Runtime.cc15
-rw-r--r--runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc1
-rw-r--r--runtime/native_bridge.cc143
-rw-r--r--runtime/native_bridge.h38
-rw-r--r--runtime/parsed_options.cc4
-rw-r--r--runtime/parsed_options.h1
-rw-r--r--runtime/primitive.cc1
-rw-r--r--runtime/primitive.h8
-rw-r--r--runtime/reflection.cc46
-rw-r--r--runtime/reflection.h1
-rw-r--r--runtime/runtime.cc20
-rw-r--r--runtime/scoped_thread_state_change.h7
-rw-r--r--runtime/thread-inl.h2
-rw-r--r--runtime/thread.cc57
-rw-r--r--runtime/thread.h7
-rw-r--r--runtime/thread_list.cc12
-rw-r--r--runtime/utils.cc5
-rw-r--r--runtime/utils.h8
-rw-r--r--runtime/verifier/reg_type.h1
47 files changed, 4948 insertions, 2645 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 0b13a3f..1e037f5 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -81,6 +81,7 @@ LIBART_COMMON_SRC_FILES := \
interpreter/interpreter.cc \
interpreter/interpreter_common.cc \
interpreter/interpreter_switch_impl.cc \
+ java_vm_ext.cc \
jdwp/jdwp_event.cc \
jdwp/jdwp_expand_buf.cc \
jdwp/jdwp_handler.cc \
@@ -88,6 +89,7 @@ LIBART_COMMON_SRC_FILES := \
jdwp/jdwp_request.cc \
jdwp/jdwp_socket.cc \
jdwp/object_registry.cc \
+ jni_env_ext.cc \
jni_internal.cc \
jobject_comparator.cc \
mem_map.cc \
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index c0a865f..abe0aa0 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -35,6 +35,7 @@ Mutex* Locks::allocated_thread_ids_lock_ = nullptr;
Mutex* Locks::breakpoint_lock_ = nullptr;
ReaderWriterMutex* Locks::classlinker_classes_lock_ = nullptr;
ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
+Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
Mutex* Locks::mem_maps_lock_ = nullptr;
Mutex* Locks::modify_ldt_lock_ = nullptr;
@@ -834,6 +835,7 @@ void Locks::Init() {
DCHECK(breakpoint_lock_ != nullptr);
DCHECK(classlinker_classes_lock_ != nullptr);
DCHECK(heap_bitmap_lock_ != nullptr);
+ DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
DCHECK(thread_list_lock_ != nullptr);
@@ -878,6 +880,10 @@ void Locks::Init() {
DCHECK(thread_list_lock_ == nullptr);
thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock);
+ DCHECK(jni_libraries_lock_ == nullptr);
+ jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock);
DCHECK(breakpoint_lock_ == nullptr);
breakpoint_lock_ = new Mutex("breakpoint lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index d898e49..fd76629 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -74,7 +74,6 @@ enum LockLevel {
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
kPinTableLock,
- kLoadLibraryLock,
kJdwpObjectRegistryLock,
kModifyLdtLock,
kAllocatedThreadIdsLock,
@@ -83,6 +82,7 @@ enum LockLevel {
kBreakpointLock,
kMonitorLock,
kMonitorListLock,
+ kJniLoadLibraryLock,
kThreadListLock,
kBreakpointInvokeLock,
kDeoptimizationLock,
@@ -561,8 +561,11 @@ class Locks {
// attaching and detaching.
static Mutex* thread_list_lock_ ACQUIRED_AFTER(trace_lock_);
+ // Guards maintaining loading library data structures.
+ static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
+
// Guards breakpoints.
- static Mutex* breakpoint_lock_ ACQUIRED_AFTER(thread_list_lock_);
+ static Mutex* breakpoint_lock_ ACQUIRED_AFTER(jni_libraries_lock_);
// Guards lists of classes within the class linker.
static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_);
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index a530594..99277a0 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -25,6 +25,7 @@
#include "dex_file-inl.h"
#include "field_helper.h"
#include "gc/space/space.h"
+#include "java_vm_ext.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
@@ -35,62 +36,16 @@
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
+#include "well_known_classes.h"
namespace art {
-static void JniAbort(const char* jni_function_name, const char* msg) {
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
- mirror::ArtMethod* current_method = self->GetCurrentMethod(nullptr);
-
- std::ostringstream os;
- os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
-
- if (jni_function_name != nullptr) {
- os << "\n in call to " << jni_function_name;
- }
- // TODO: is this useful given that we're about to dump the calling thread's stack?
- if (current_method != nullptr) {
- os << "\n from " << PrettyMethod(current_method);
- }
- os << "\n";
- self->Dump(os);
-
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- if (vm->check_jni_abort_hook != nullptr) {
- vm->check_jni_abort_hook(vm->check_jni_abort_hook_data, os.str());
- } else {
- // Ensure that we get a native stack trace for this thread.
- self->TransitionFromRunnableToSuspended(kNative);
- LOG(FATAL) << os.str();
- self->TransitionFromSuspendedToRunnable(); // Unreachable, keep annotalysis happy.
- }
-}
-
-static void JniAbortV(const char* jni_function_name, const char* fmt, va_list ap) {
- std::string msg;
- StringAppendV(&msg, fmt, ap);
- JniAbort(jni_function_name, msg.c_str());
-}
-
-void JniAbortF(const char* jni_function_name, const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- JniAbortV(jni_function_name, fmt, args);
- va_end(args);
-}
-
/*
* ===========================================================================
* JNI function helpers
* ===========================================================================
*/
-static bool IsHandleScopeLocalRef(JNIEnv* env, jobject localRef) {
- return GetIndirectRefKind(localRef) == kHandleScopeOrInvalid &&
- reinterpret_cast<JNIEnvExt*>(env)->self->HandleScopeContains(localRef);
-}
-
// Flags passed into ScopedCheck.
#define kFlag_Default 0x0000
@@ -109,134 +64,88 @@ static bool IsHandleScopeLocalRef(JNIEnv* env, jobject localRef) {
#define kFlag_Invocation 0x8000 // Part of the invocation interface (JavaVM*).
#define kFlag_ForceTrace 0x80000000 // Add this to a JNI function's flags if you want to trace every call.
-
-static const char* gBuiltInPrefixes[] = {
- "Landroid/",
- "Lcom/android/",
- "Lcom/google/android/",
- "Ldalvik/",
- "Ljava/",
- "Ljavax/",
- "Llibcore/",
- "Lorg/apache/harmony/",
- nullptr
+/*
+ * Java primitive types:
+ * B - jbyte
+ * C - jchar
+ * D - jdouble
+ * F - jfloat
+ * I - jint
+ * J - jlong
+ * S - jshort
+ * Z - jboolean (shown as true and false)
+ * V - void
+ *
+ * Java reference types:
+ * L - jobject
+ * a - jarray
+ * c - jclass
+ * s - jstring
+ * t - jthrowable
+ *
+ * JNI types:
+ * b - jboolean (shown as JNI_TRUE and JNI_FALSE)
+ * f - jfieldID
+ * i - JNI error value (JNI_OK, JNI_ERR, JNI_EDETACHED, JNI_EVERSION)
+ * m - jmethodID
+ * p - void*
+ * r - jint (for release mode arguments)
+ * u - const char* (Modified UTF-8)
+ * z - jsize (for lengths; use i if negative values are okay)
+ * v - JavaVM*
+ * w - jobjectRefType
+ * E - JNIEnv*
+ * . - no argument; just print "..." (used for varargs JNI calls)
+ *
+ */
+union JniValueType {
+ jarray a;
+ jboolean b;
+ jclass c;
+ jfieldID f;
+ jint i;
+ jmethodID m;
+ const void* p; // Pointer.
+ jint r; // Release mode.
+ jstring s;
+ jthrowable t;
+ const char* u; // Modified UTF-8.
+ JavaVM* v;
+ jobjectRefType w;
+ jsize z;
+ jbyte B;
+ jchar C;
+ jdouble D;
+ JNIEnv* E;
+ jfloat F;
+ jint I;
+ jlong J;
+ jobject L;
+ jshort S;
+ const void* V; // void
+ jboolean Z;
};
-static bool ShouldTrace(JavaVMExt* vm, mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
- // when a native method that matches the -Xjnitrace argument calls a JNI function
- // such as NewByteArray.
- // If -verbose:third-party-jni is on, we want to log any JNI function calls
- // made by a third-party native method.
- std::string class_name(method->GetDeclaringClassDescriptor());
- if (!vm->trace.empty() && class_name.find(vm->trace) != std::string::npos) {
- return true;
- }
- if (VLOG_IS_ON(third_party_jni)) {
- // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
- // like part of Android.
- for (size_t i = 0; gBuiltInPrefixes[i] != nullptr; ++i) {
- if (StartsWith(class_name, gBuiltInPrefixes[i])) {
- return false;
- }
- }
- return true;
- }
- return false;
-}
-
class ScopedCheck {
public:
- // For JNIEnv* functions.
- explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
- : soa_(env) {
- Init(flags, functionName, true);
- CheckThread(flags);
+ explicit ScopedCheck(int flags, const char* functionName, bool has_method = true)
+ : function_name_(functionName), flags_(flags), indent_(0), has_method_(has_method) {
}
- // For JavaVM* functions.
- // TODO: it's not correct that this is a lock function, but making it so aids annotalysis.
- explicit ScopedCheck(JavaVM* vm, bool has_method, const char* functionName)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
- : soa_(vm) {
- Init(kFlag_Invocation, functionName, has_method);
- }
-
- ~ScopedCheck() UNLOCK_FUNCTION(Locks::mutator_lock_) {}
-
- const ScopedObjectAccess& soa() {
- return soa_;
- }
-
- bool ForceCopy() {
- return Runtime::Current()->GetJavaVM()->force_copy;
- }
+ ~ScopedCheck() {}
// Checks that 'class_name' is a valid "fully-qualified" JNI class name, like "java/lang/Thread"
// or "[Ljava/lang/Object;". A ClassLoader can actually normalize class names a couple of
// times, so using "java.lang.Thread" instead of "java/lang/Thread" might work in some
// circumstances, but this is incorrect.
- void CheckClassName(const char* class_name) {
+ bool CheckClassName(const char* class_name) {
if ((class_name == nullptr) || !IsValidJniClassName(class_name)) {
- JniAbortF(function_name_,
- "illegal class name '%s'\n"
- " (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')",
- class_name);
- }
- }
-
- /*
- * Verify that the field is of the appropriate type. If the field has an
- * object type, "java_object" is the object we're trying to assign into it.
- *
- * Works for both static and instance fields.
- */
- void CheckFieldType(jvalue value, jfieldID fid, char prim, bool isStatic)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::ArtField> f(hs.NewHandle(CheckFieldID(fid)));
- if (f.Get() == nullptr) {
- return;
- }
- mirror::Class* field_type = FieldHelper(f).GetType();
- if (!field_type->IsPrimitive()) {
- jobject java_object = value.l;
- if (java_object != nullptr) {
- mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object);
- // If java_object is a weak global ref whose referent has been cleared,
- // obj will be NULL. Otherwise, obj should always be non-NULL
- // and valid.
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
- Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "field operation on invalid %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
- return;
- } else {
- if (!obj->InstanceOf(field_type)) {
- JniAbortF(function_name_, "attempt to set field %s with value of wrong type: %s",
- PrettyField(f.Get()).c_str(), PrettyTypeOf(obj).c_str());
- return;
- }
- }
- }
- } else if (field_type != Runtime::Current()->GetClassLinker()->FindPrimitiveClass(prim)) {
- JniAbortF(function_name_, "attempt to set field %s with value of wrong type: %c",
- PrettyField(f.Get()).c_str(), prim);
- return;
- }
-
- if (isStatic != f.Get()->IsStatic()) {
- if (isStatic) {
- JniAbortF(function_name_, "accessing non-static field %s as static",
- PrettyField(f.Get()).c_str());
- } else {
- JniAbortF(function_name_, "accessing static field %s as non-static",
- PrettyField(f.Get()).c_str());
- }
- return;
+ AbortF("illegal class name '%s'\n"
+ " (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')",
+ class_name);
+ return false;
}
+ return true;
}
/*
@@ -244,59 +153,87 @@ class ScopedCheck {
*
* Assumes "jobj" has already been validated.
*/
- void CheckInstanceFieldID(jobject java_object, jfieldID fid)
+ bool CheckInstanceFieldID(ScopedObjectAccess& soa, jobject java_object, jfieldID fid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
- if (o == nullptr || !Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+ mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+ if (o == nullptr) {
+ AbortF("field operation on NULL object: %p", java_object);
+ return false;
+ }
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "field operation on invalid %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
- return;
+ AbortF("field operation on invalid %s: %p",
+ ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+ java_object);
+ return false;
}
- mirror::ArtField* f = CheckFieldID(fid);
+ mirror::ArtField* f = CheckFieldID(soa, fid);
if (f == nullptr) {
- return;
+ return false;
}
mirror::Class* c = o->GetClass();
if (c->FindInstanceField(f->GetName(), f->GetTypeDescriptor()) == nullptr) {
- JniAbortF(function_name_, "jfieldID %s not valid for an object of class %s",
- PrettyField(f).c_str(), PrettyTypeOf(o).c_str());
+ AbortF("jfieldID %s not valid for an object of class %s",
+ PrettyField(f).c_str(), PrettyTypeOf(o).c_str());
+ return false;
}
+ return true;
}
/*
* Verify that the pointer value is non-NULL.
*/
- void CheckNonNull(const void* ptr) {
- if (ptr == nullptr) {
- JniAbortF(function_name_, "non-nullable argument was NULL");
+ bool CheckNonNull(const void* ptr) {
+ if (UNLIKELY(ptr == nullptr)) {
+ AbortF("non-nullable argument was NULL");
+ return false;
}
+ return true;
}
/*
* Verify that the method's return type matches the type of call.
* 'expectedType' will be "L" for all objects, including arrays.
*/
- void CheckSig(jmethodID mid, const char* expectedType, bool isStatic)
+ bool CheckMethodAndSig(ScopedObjectAccess& soa, jobject jobj, jclass jc,
+ jmethodID mid, Primitive::Type type, InvokeType invoke)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = CheckMethodID(mid);
+ mirror::ArtMethod* m = CheckMethodID(soa, mid);
if (m == nullptr) {
- return;
+ return false;
}
- if (*expectedType != m->GetShorty()[0]) {
- JniAbortF(function_name_, "the return type of %s does not match %s",
- function_name_, PrettyMethod(m).c_str());
+ if (type != Primitive::GetType(m->GetShorty()[0])) {
+ AbortF("the return type of %s does not match %s", function_name_, PrettyMethod(m).c_str());
+ return false;
}
- if (isStatic != m->IsStatic()) {
- if (isStatic) {
- JniAbortF(function_name_, "calling non-static method %s with %s",
- PrettyMethod(m).c_str(), function_name_);
+ bool is_static = (invoke == kStatic);
+ if (is_static != m->IsStatic()) {
+ if (is_static) {
+ AbortF("calling non-static method %s with %s",
+ PrettyMethod(m).c_str(), function_name_);
} else {
- JniAbortF(function_name_, "calling static method %s with %s",
- PrettyMethod(m).c_str(), function_name_);
+ AbortF("calling static method %s with %s",
+ PrettyMethod(m).c_str(), function_name_);
}
+ return false;
}
+ if (invoke != kVirtual) {
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
+ AbortF("can't call %s %s with class %s", invoke == kStatic ? "static" : "nonvirtual",
+ PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+ return false;
+ }
+ }
+ if (invoke != kStatic) {
+ mirror::Object* o = soa.Decode<mirror::Object*>(jobj);
+ if (!o->InstanceOf(m->GetDeclaringClass())) {
+ AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+ return false;
+ }
+ }
+ return true;
}
/*
@@ -304,17 +241,18 @@ class ScopedCheck {
*
* Assumes "java_class" has already been validated.
*/
- void CheckStaticFieldID(jclass java_class, jfieldID fid)
+ bool CheckStaticFieldID(ScopedObjectAccess& soa, jclass java_class, jfieldID fid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
- mirror::ArtField* f = CheckFieldID(fid);
+ mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+ mirror::ArtField* f = CheckFieldID(soa, fid);
if (f == nullptr) {
- return;
+ return false;
}
if (f->GetDeclaringClass() != c) {
- JniAbortF(function_name_, "static jfieldID %p not valid for class %s",
- fid, PrettyClass(c).c_str());
+ AbortF("static jfieldID %p not valid for class %s", fid, PrettyClass(c).c_str());
+ return false;
}
+ return true;
}
/*
@@ -326,17 +264,18 @@ class ScopedCheck {
*
* Instances of "java_class" must be instances of the method's declaring class.
*/
- void CheckStaticMethod(jclass java_class, jmethodID mid)
+ bool CheckStaticMethod(ScopedObjectAccess& soa, jclass java_class, jmethodID mid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = CheckMethodID(mid);
+ mirror::ArtMethod* m = CheckMethodID(soa, mid);
if (m == nullptr) {
- return;
+ return false;
}
- mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
+ mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
- JniAbortF(function_name_, "can't call static %s on class %s",
- PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+ AbortF("can't call static %s on class %s", PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+ return false;
}
+ return true;
}
/*
@@ -346,17 +285,18 @@ class ScopedCheck {
* (Note the mid might point to a declaration in an interface; this
* will be handled automatically by the instanceof check.)
*/
- void CheckVirtualMethod(jobject java_object, jmethodID mid)
+ bool CheckVirtualMethod(ScopedObjectAccess& soa, jobject java_object, jmethodID mid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = CheckMethodID(mid);
+ mirror::ArtMethod* m = CheckMethodID(soa, mid);
if (m == nullptr) {
- return;
+ return false;
}
- mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
+ mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
if (!o->InstanceOf(m->GetDeclaringClass())) {
- JniAbortF(function_name_, "can't call %s on instance of %s",
- PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+ AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+ return false;
}
+ return true;
}
/**
@@ -395,11 +335,10 @@ class ScopedCheck {
*
* Use the kFlag_NullableUtf flag where 'u' field(s) are nullable.
*/
- void Check(bool entry, const char* fmt0, ...) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- va_list ap;
-
+ bool Check(ScopedObjectAccess& soa, bool entry, const char* fmt, JniValueType* args)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* traceMethod = nullptr;
- if (has_method_ && (!soa_.Vm()->trace.empty() || VLOG_IS_ON(third_party_jni))) {
+ if (has_method_ && soa.Vm()->IsTracingEnabled()) {
// We need to guard some of the invocation interface's calls: a bad caller might
// use DetachCurrentThread or GetEnv on a thread that's not yet attached.
Thread* self = Thread::Current();
@@ -409,129 +348,70 @@ class ScopedCheck {
}
if (((flags_ & kFlag_ForceTrace) != 0) ||
- (traceMethod != nullptr && ShouldTrace(soa_.Vm(), traceMethod))) {
- va_start(ap, fmt0);
+ (traceMethod != nullptr && soa.Vm()->ShouldTrace(traceMethod))) {
std::string msg;
- for (const char* fmt = fmt0; *fmt;) {
- char ch = *fmt++;
- if (ch == 'B') { // jbyte
- jbyte b = va_arg(ap, int);
- if (b >= 0 && b < 10) {
- StringAppendF(&msg, "%d", b);
- } else {
- StringAppendF(&msg, "%#x (%d)", b, b);
- }
- } else if (ch == 'C') { // jchar
- jchar c = va_arg(ap, int);
- if (c < 0x7f && c >= ' ') {
- StringAppendF(&msg, "U+%x ('%c')", c, c);
- } else {
- StringAppendF(&msg, "U+%x", c);
- }
- } else if (ch == 'F' || ch == 'D') { // jfloat, jdouble
- StringAppendF(&msg, "%g", va_arg(ap, double));
- } else if (ch == 'I' || ch == 'S') { // jint, jshort
- StringAppendF(&msg, "%d", va_arg(ap, int));
- } else if (ch == 'J') { // jlong
- StringAppendF(&msg, "%" PRId64, va_arg(ap, jlong));
- } else if (ch == 'Z') { // jboolean
- StringAppendF(&msg, "%s", va_arg(ap, int) ? "true" : "false");
- } else if (ch == 'V') { // void
- msg += "void";
- } else if (ch == 'v') { // JavaVM*
- JavaVM* vm = va_arg(ap, JavaVM*);
- StringAppendF(&msg, "(JavaVM*)%p", vm);
- } else if (ch == 'E') { // JNIEnv*
- JNIEnv* env = va_arg(ap, JNIEnv*);
- StringAppendF(&msg, "(JNIEnv*)%p", env);
- } else if (ch == 'L' || ch == 'a' || ch == 's') { // jobject, jarray, jstring
- // For logging purposes, these are identical.
- jobject o = va_arg(ap, jobject);
- if (o == nullptr) {
- msg += "NULL";
- } else {
- StringAppendF(&msg, "%p", o);
- }
- } else if (ch == 'b') { // jboolean (JNI-style)
- jboolean b = va_arg(ap, int);
- msg += (b ? "JNI_TRUE" : "JNI_FALSE");
- } else if (ch == 'c') { // jclass
- jclass jc = va_arg(ap, jclass);
- mirror::Class* c = reinterpret_cast<mirror::Class*>(Thread::Current()->DecodeJObject(jc));
- if (c == nullptr) {
- msg += "NULL";
- } else if (c == kInvalidIndirectRefObject ||
- !Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
- StringAppendF(&msg, "INVALID POINTER:%p", jc);
- } else if (!c->IsClass()) {
- msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
- } else {
- msg += PrettyClass(c);
- if (!entry) {
- StringAppendF(&msg, " (%p)", jc);
- }
- }
- } else if (ch == 'f') { // jfieldID
- jfieldID fid = va_arg(ap, jfieldID);
- mirror::ArtField* f = reinterpret_cast<mirror::ArtField*>(fid);
- msg += PrettyField(f);
- if (!entry) {
- StringAppendF(&msg, " (%p)", fid);
- }
- } else if (ch == 'z') { // non-negative jsize
- // You might expect jsize to be size_t, but it's not; it's the same as jint.
- // We only treat this specially so we can do the non-negative check.
- // TODO: maybe this wasn't worth it?
- jint i = va_arg(ap, jint);
- StringAppendF(&msg, "%d", i);
- } else if (ch == 'm') { // jmethodID
- jmethodID mid = va_arg(ap, jmethodID);
- mirror::ArtMethod* m = reinterpret_cast<mirror::ArtMethod*>(mid);
- msg += PrettyMethod(m);
- if (!entry) {
- StringAppendF(&msg, " (%p)", mid);
- }
- } else if (ch == 'p') { // void* ("pointer")
- void* p = va_arg(ap, void*);
- if (p == nullptr) {
- msg += "NULL";
- } else {
- StringAppendF(&msg, "(void*) %p", p);
- }
- } else if (ch == 'r') { // jint (release mode)
- jint releaseMode = va_arg(ap, jint);
- if (releaseMode == 0) {
- msg += "0";
- } else if (releaseMode == JNI_ABORT) {
- msg += "JNI_ABORT";
- } else if (releaseMode == JNI_COMMIT) {
- msg += "JNI_COMMIT";
- } else {
- StringAppendF(&msg, "invalid release mode %d", releaseMode);
- }
- } else if (ch == 'u') { // const char* (Modified UTF-8)
- const char* utf = va_arg(ap, const char*);
- if (utf == nullptr) {
- msg += "NULL";
- } else {
- StringAppendF(&msg, "\"%s\"", utf);
- }
- } else if (ch == '.') {
- msg += "...";
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ TracePossibleHeapValue(soa, entry, fmt[i], args[i], &msg);
+ if (fmt[i + 1] != '\0') {
+ StringAppendF(&msg, ", ");
+ }
+ }
+
+ if ((flags_ & kFlag_ForceTrace) != 0) {
+ LOG(INFO) << "JNI: call to " << function_name_ << "(" << msg << ")";
+ } else if (entry) {
+ if (has_method_) {
+ std::string methodName(PrettyMethod(traceMethod, false));
+ LOG(INFO) << "JNI: " << methodName << " -> " << function_name_ << "(" << msg << ")";
+ indent_ = methodName.size() + 1;
} else {
- JniAbortF(function_name_, "unknown trace format specifier: %c", ch);
- return;
+ LOG(INFO) << "JNI: -> " << function_name_ << "(" << msg << ")";
+ indent_ = 0;
+ }
+ } else {
+ LOG(INFO) << StringPrintf("JNI: %*s<- %s returned %s", indent_, "", function_name_, msg.c_str());
+ }
+ }
+
+ // We always do the thorough checks on entry, and never on exit...
+ if (entry) {
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ if (!CheckPossibleHeapValue(soa, fmt[i], args[i])) {
+ return false;
}
- if (*fmt) {
+ }
+ }
+ return true;
+ }
+
+ bool CheckNonHeap(JavaVMExt* vm, bool entry, const char* fmt, JniValueType* args) {
+ bool should_trace = (flags_ & kFlag_ForceTrace) != 0;
+ if (!should_trace && vm->IsTracingEnabled()) {
+ // We need to guard some of the invocation interface's calls: a bad caller might
+ // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
+ Thread* self = Thread::Current();
+ if ((flags_ & kFlag_Invocation) == 0 || self != nullptr) {
+ ScopedObjectAccess soa(self);
+ mirror::ArtMethod* traceMethod = self->GetCurrentMethod(nullptr);
+ should_trace = (traceMethod != nullptr && vm->ShouldTrace(traceMethod));
+ }
+ }
+ if (should_trace) {
+ std::string msg;
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ TraceNonHeapValue(fmt[i], args[i], &msg);
+ if (fmt[i + 1] != '\0') {
StringAppendF(&msg, ", ");
}
}
- va_end(ap);
if ((flags_ & kFlag_ForceTrace) != 0) {
LOG(INFO) << "JNI: call to " << function_name_ << "(" << msg << ")";
} else if (entry) {
if (has_method_) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ mirror::ArtMethod* traceMethod = self->GetCurrentMethod(nullptr);
std::string methodName(PrettyMethod(traceMethod, false));
LOG(INFO) << "JNI: " << methodName << " -> " << function_name_ << "(" << msg << ")";
indent_ = methodName.size() + 1;
@@ -546,43 +426,176 @@ class ScopedCheck {
// We always do the thorough checks on entry, and never on exit...
if (entry) {
- va_start(ap, fmt0);
- for (const char* fmt = fmt0; *fmt; ++fmt) {
- char ch = *fmt;
- if (ch == 'a') {
- CheckArray(va_arg(ap, jarray));
- } else if (ch == 'c') {
- CheckInstance(kClass, va_arg(ap, jclass));
- } else if (ch == 'L') {
- CheckObject(va_arg(ap, jobject));
- } else if (ch == 'r') {
- CheckReleaseMode(va_arg(ap, jint));
- } else if (ch == 's') {
- CheckInstance(kString, va_arg(ap, jstring));
- } else if (ch == 'u') {
- if ((flags_ & kFlag_Release) != 0) {
- CheckNonNull(va_arg(ap, const char*));
- } else {
- bool nullable = ((flags_ & kFlag_NullableUtf) != 0);
- CheckUtfString(va_arg(ap, const char*), nullable);
- }
- } else if (ch == 'z') {
- CheckLengthPositive(va_arg(ap, jsize));
- } else if (strchr("BCISZbfmpEv", ch) != nullptr) {
- va_arg(ap, uint32_t); // Skip this argument.
- } else if (ch == 'D' || ch == 'F') {
- va_arg(ap, double); // Skip this argument.
- } else if (ch == 'J') {
- va_arg(ap, uint64_t); // Skip this argument.
- } else if (ch == '.') {
- } else {
- LOG(FATAL) << "Unknown check format specifier: " << ch;
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ if (!CheckNonHeapValue(fmt[i], args[i])) {
+ return false;
}
}
- va_end(ap);
}
+ return true;
+ }
+
+ bool CheckReflectedMethod(ScopedObjectAccess& soa, jobject jmethod)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* method = soa.Decode<mirror::Object*>(jmethod);
+ if (method == nullptr) {
+ AbortF("expected non-null method");
+ return false;
+ }
+ mirror::Class* c = method->GetClass();
+ if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Method) != c &&
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Constructor) != c) {
+ AbortF("expected java.lang.reflect.Method or "
+ "java.lang.reflect.Constructor but got object of type %s: %p",
+ PrettyTypeOf(method).c_str(), jmethod);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckConstructor(ScopedObjectAccess& soa, jmethodID mid)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = soa.DecodeMethod(mid);
+ if (method == nullptr) {
+ AbortF("expected non-null constructor");
+ return false;
+ }
+ if (!method->IsConstructor() || method->IsStatic()) {
+ AbortF("expected a constructor but %s: %p", PrettyTypeOf(method).c_str(), mid);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckReflectedField(ScopedObjectAccess& soa, jobject jfield)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* field = soa.Decode<mirror::Object*>(jfield);
+ if (field == nullptr) {
+ AbortF("expected non-null java.lang.reflect.Field");
+ return false;
+ }
+ mirror::Class* c = field->GetClass();
+ if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Field) != c) {
+ AbortF("expected java.lang.reflect.Field but got object of type %s: %p",
+ PrettyTypeOf(field).c_str(), jfield);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckThrowable(ScopedObjectAccess& soa, jthrowable jobj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+ if (!obj->GetClass()->IsThrowableClass()) {
+ AbortF("expected java.lang.Throwable but got object of type "
+ "%s: %p", PrettyTypeOf(obj).c_str(), obj);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckThrowableClass(ScopedObjectAccess& soa, jclass jc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (!c->IsThrowableClass()) {
+ AbortF("expected java.lang.Throwable class but got object of "
+ "type %s: %p", PrettyDescriptor(c).c_str(), c);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckReferenceKind(IndirectRefKind expected_kind, JavaVMExt* vm, Thread* self, jobject obj) {
+ IndirectRefKind found_kind;
+ if (expected_kind == kLocal) {
+ found_kind = GetIndirectRefKind(obj);
+ if (found_kind == kHandleScopeOrInvalid && self->HandleScopeContains(obj)) {
+ found_kind = kLocal;
+ }
+ } else {
+ found_kind = GetIndirectRefKind(obj);
+ }
+ if (obj != nullptr && found_kind != expected_kind) {
+ AbortF("expected reference of kind %s but found %s: %p",
+ ToStr<IndirectRefKind>(expected_kind).c_str(),
+ ToStr<IndirectRefKind>(GetIndirectRefKind(obj)).c_str(),
+ obj);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckInstantiableNonArray(ScopedObjectAccess& soa, jclass jc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (!c->IsInstantiableNonArray()) {
+ AbortF("can't make objects of type %s: %p", PrettyDescriptor(c).c_str(), c);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckPrimitiveArrayType(ScopedObjectAccess& soa, jarray array, Primitive::Type type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (!CheckArray(soa, array)) {
+ return false;
+ }
+ mirror::Array* a = soa.Decode<mirror::Array*>(array);
+ if (a->GetClass()->GetComponentType()->GetPrimitiveType() != type) {
+ AbortF("incompatible array type %s expected %s[]: %p",
+ PrettyDescriptor(a->GetClass()).c_str(), PrettyDescriptor(type).c_str(), array);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckFieldAccess(ScopedObjectAccess& soa, jobject obj, jfieldID fid, bool is_static,
+ Primitive::Type type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (is_static && !CheckStaticFieldID(soa, down_cast<jclass>(obj), fid)) {
+ return false;
+ }
+ if (!is_static && !CheckInstanceFieldID(soa, obj, fid)) {
+ return false;
+ }
+ mirror::ArtField* field = soa.DecodeField(fid);
+ DCHECK(field != nullptr); // Already checked by Check.
+ if (is_static != field->IsStatic()) {
+ AbortF("attempt to access %s field %s: %p",
+ field->IsStatic() ? "static" : "non-static", PrettyField(field).c_str(), fid);
+ return false;
+ }
+ if (type != field->GetTypeAsPrimitiveType()) {
+ AbortF("attempt to access field %s of type %s with the wrong type %s: %p",
+ PrettyField(field).c_str(), PrettyDescriptor(field->GetTypeDescriptor()).c_str(),
+ PrettyDescriptor(type).c_str(), fid);
+ return false;
+ }
+ if (is_static) {
+ mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+ if (o == nullptr || !o->IsClass()) {
+ AbortF("attempt to access static field %s with a class argument of type %s: %p",
+ PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
+ return false;
+ }
+ mirror::Class* c = o->AsClass();
+ if (field->GetDeclaringClass() != c) {
+ AbortF("attempt to access static field %s with an incompatible class argument of %s: %p",
+ PrettyField(field).c_str(), PrettyDescriptor(c).c_str(), fid);
+ return false;
+ }
+ } else {
+ mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+ if (o == nullptr || !field->GetDeclaringClass()->IsAssignableFrom(o->GetClass())) {
+ AbortF("attempt to access field %s from an object argument of type %s: %p",
+ PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
+ return false;
+ }
+ }
+ return true;
}
+ private:
enum InstanceKind {
kClass,
kDirectByteBuffer,
@@ -598,7 +611,7 @@ class ScopedCheck {
* Because we're looking at an object on the GC heap, we have to switch
* to "running" mode before doing the checks.
*/
- bool CheckInstance(InstanceKind kind, jobject java_object)
+ bool CheckInstance(ScopedObjectAccess& soa, InstanceKind kind, jobject java_object, bool null_ok)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const char* what = nullptr;
switch (kind) {
@@ -622,15 +635,20 @@ class ScopedCheck {
}
if (java_object == nullptr) {
- JniAbortF(function_name_, "%s received null %s", function_name_, what);
- return false;
+ if (null_ok) {
+ return true;
+ } else {
+ AbortF("%s received NULL %s", function_name_, what);
+ return false;
+ }
}
- mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(java_object);
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "%s is an invalid %s: %p (%p)",
- what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object, obj);
+ AbortF("%s is an invalid %s: %p (%p)",
+ what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+ java_object, obj);
return false;
}
@@ -652,114 +670,333 @@ class ScopedCheck {
break;
}
if (!okay) {
- JniAbortF(function_name_, "%s has wrong type: %s", what, PrettyTypeOf(obj).c_str());
+ AbortF("%s has wrong type: %s", what, PrettyTypeOf(obj).c_str());
return false;
}
return true;
}
- private:
- // Set "has_method" to true if we have a valid thread with a method pointer.
- // We won't have one before attaching a thread, after detaching a thread, or
- // when shutting down the runtime.
- void Init(int flags, const char* functionName, bool has_method) {
- flags_ = flags;
- function_name_ = functionName;
- has_method_ = has_method;
+ /*
+ * Verify that the "mode" argument passed to a primitive array Release
+ * function is one of the valid values.
+ */
+ bool CheckReleaseMode(jint mode) {
+ if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+ AbortF("unknown value for release mode: %d", mode);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckPossibleHeapValue(ScopedObjectAccess& soa, char fmt, JniValueType arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ switch (fmt) {
+ case 'a': // jarray
+ return CheckArray(soa, arg.a);
+ case 'c': // jclass
+ return CheckInstance(soa, kClass, arg.c, false);
+ case 'f': // jfieldID
+ return CheckFieldID(soa, arg.f) != nullptr;
+ case 'm': // jmethodID
+ return CheckMethodID(soa, arg.m) != nullptr;
+ case 'r': // release int
+ return CheckReleaseMode(arg.r);
+ case 's': // jstring
+ return CheckInstance(soa, kString, arg.s, false);
+ case 't': // jthrowable
+ return CheckInstance(soa, kThrowable, arg.t, false);
+ case 'E': // JNIEnv*
+ return CheckThread(arg.E);
+ case 'L': // jobject
+ return CheckInstance(soa, kObject, arg.L, true);
+ default:
+ return CheckNonHeapValue(fmt, arg);
+ }
+ }
+
+ bool CheckNonHeapValue(char fmt, JniValueType arg) {
+ switch (fmt) {
+ case '.': // ...
+ case 'p': // TODO: pointer - null or readable?
+ case 'v': // JavaVM*
+ case 'B': // jbyte
+ case 'C': // jchar
+ case 'D': // jdouble
+ case 'F': // jfloat
+ case 'I': // jint
+ case 'J': // jlong
+ case 'S': // jshort
+ break; // Ignored.
+ case 'b': // jboolean, why two? Fall-through.
+ case 'Z':
+ return CheckBoolean(arg.Z);
+ case 'u': // utf8
+ if ((flags_ & kFlag_Release) != 0) {
+ return CheckNonNull(arg.u);
+ } else {
+ bool nullable = ((flags_ & kFlag_NullableUtf) != 0);
+ return CheckUtfString(arg.u, nullable);
+ }
+ case 'w': // jobjectRefType
+ switch (arg.w) {
+ case JNIInvalidRefType:
+ case JNILocalRefType:
+ case JNIGlobalRefType:
+ case JNIWeakGlobalRefType:
+ break;
+ default:
+ AbortF("Unknown reference type");
+ return false;
+ }
+ break;
+ case 'z': // jsize
+ return CheckLengthPositive(arg.z);
+ default:
+ AbortF("unknown format specifier: '%c'", fmt);
+ return false;
+ }
+ return true;
}
+ void TracePossibleHeapValue(ScopedObjectAccess& soa, bool entry, char fmt, JniValueType arg,
+ std::string* msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ switch (fmt) {
+ case 'L': // jobject fall-through.
+ case 'a': // jarray fall-through.
+ case 's': // jstring fall-through.
+ case 't': // jthrowable fall-through.
+ if (arg.L == nullptr) {
+ *msg += "NULL";
+ } else {
+ StringAppendF(msg, "%p", arg.L);
+ }
+ break;
+ case 'c': { // jclass
+ jclass jc = arg.c;
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (c == nullptr) {
+ *msg += "NULL";
+ } else if (c == kInvalidIndirectRefObject ||
+ !Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+ StringAppendF(msg, "INVALID POINTER:%p", jc);
+ } else if (!c->IsClass()) {
+ *msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
+ } else {
+ *msg += PrettyClass(c);
+ if (!entry) {
+ StringAppendF(msg, " (%p)", jc);
+ }
+ }
+ break;
+ }
+ case 'f': { // jfieldID
+ jfieldID fid = arg.f;
+ mirror::ArtField* f = soa.DecodeField(fid);
+ *msg += PrettyField(f);
+ if (!entry) {
+ StringAppendF(msg, " (%p)", fid);
+ }
+ break;
+ }
+ case 'm': { // jmethodID
+ jmethodID mid = arg.m;
+ mirror::ArtMethod* m = soa.DecodeMethod(mid);
+ *msg += PrettyMethod(m);
+ if (!entry) {
+ StringAppendF(msg, " (%p)", mid);
+ }
+ break;
+ }
+ default:
+ TraceNonHeapValue(fmt, arg, msg);
+ break;
+ }
+ }
+
+ void TraceNonHeapValue(char fmt, JniValueType arg, std::string* msg) {
+ switch (fmt) {
+ case 'B': // jbyte
+ if (arg.B >= 0 && arg.B < 10) {
+ StringAppendF(msg, "%d", arg.B);
+ } else {
+ StringAppendF(msg, "%#x (%d)", arg.B, arg.B);
+ }
+ break;
+ case 'C': // jchar
+ if (arg.C < 0x7f && arg.C >= ' ') {
+ StringAppendF(msg, "U+%x ('%c')", arg.C, arg.C);
+ } else {
+ StringAppendF(msg, "U+%x", arg.C);
+ }
+ break;
+ case 'F': // jfloat
+ StringAppendF(msg, "%g", arg.F);
+ break;
+ case 'D': // jdouble
+ StringAppendF(msg, "%g", arg.D);
+ break;
+ case 'S': // jshort
+ StringAppendF(msg, "%d", arg.S);
+ break;
+ case 'i': // jint - fall-through.
+ case 'I': // jint
+ StringAppendF(msg, "%d", arg.I);
+ break;
+ case 'J': // jlong
+ StringAppendF(msg, "%" PRId64, arg.J);
+ break;
+ case 'Z': // jboolean
+ case 'b': // jboolean (JNI-style)
+ *msg += arg.b == JNI_TRUE ? "true" : "false";
+ break;
+ case 'V': // void
+ DCHECK(arg.V == nullptr);
+ *msg += "void";
+ break;
+ case 'v': // JavaVM*
+ StringAppendF(msg, "(JavaVM*)%p", arg.v);
+ break;
+ case 'E':
+ StringAppendF(msg, "(JNIEnv*)%p", arg.E);
+ break;
+ case 'z': // non-negative jsize
+ // You might expect jsize to be size_t, but it's not; it's the same as jint.
+ // We only treat this specially so we can do the non-negative check.
+ // TODO: maybe this wasn't worth it?
+ StringAppendF(msg, "%d", arg.z);
+ break;
+ case 'p': // void* ("pointer")
+ if (arg.p == nullptr) {
+ *msg += "NULL";
+ } else {
+ StringAppendF(msg, "(void*) %p", arg.p);
+ }
+ break;
+ case 'r': { // jint (release mode)
+ jint releaseMode = arg.r;
+ if (releaseMode == 0) {
+ *msg += "0";
+ } else if (releaseMode == JNI_ABORT) {
+ *msg += "JNI_ABORT";
+ } else if (releaseMode == JNI_COMMIT) {
+ *msg += "JNI_COMMIT";
+ } else {
+ StringAppendF(msg, "invalid release mode %d", releaseMode);
+ }
+ break;
+ }
+ case 'u': // const char* (Modified UTF-8)
+ if (arg.u == nullptr) {
+ *msg += "NULL";
+ } else {
+ StringAppendF(msg, "\"%s\"", arg.u);
+ }
+ break;
+ case 'w': // jobjectRefType
+ switch (arg.w) {
+ case JNIInvalidRefType:
+ *msg += "invalid reference type";
+ break;
+ case JNILocalRefType:
+ *msg += "local ref type";
+ break;
+ case JNIGlobalRefType:
+ *msg += "global ref type";
+ break;
+ case JNIWeakGlobalRefType:
+ *msg += "weak global ref type";
+ break;
+ default:
+ *msg += "unknown ref type";
+ break;
+ }
+ break;
+ case '.':
+ *msg += "...";
+ break;
+ default:
+ LOG(FATAL) << function_name_ << ": unknown trace format specifier: '" << fmt << "'";
+ }
+ }
/*
* Verify that "array" is non-NULL and points to an Array object.
*
* Since we're dealing with objects, switch to "running" mode.
*/
- void CheckArray(jarray java_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (java_array == nullptr) {
- JniAbortF(function_name_, "jarray was NULL");
- return;
+ bool CheckArray(ScopedObjectAccess& soa, jarray java_array)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (UNLIKELY(java_array == nullptr)) {
+ AbortF("jarray was NULL");
+ return false;
}
- mirror::Array* a = soa_.Decode<mirror::Array*>(java_array);
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(a)) {
+ mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+ if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "jarray is an invalid %s: %p (%p)",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(), java_array, a);
+ AbortF("jarray is an invalid %s: %p (%p)",
+ ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(),
+ java_array, a);
+ return false;
} else if (!a->IsArrayInstance()) {
- JniAbortF(function_name_, "jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
+ AbortF("jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckBoolean(jboolean z) {
+ if (z != JNI_TRUE && z != JNI_FALSE) {
+ AbortF("unexpected jboolean value: %d", z);
+ return false;
}
+ return true;
}
- void CheckLengthPositive(jsize length) {
+ bool CheckLengthPositive(jsize length) {
if (length < 0) {
- JniAbortF(function_name_, "negative jsize: %d", length);
+ AbortF("negative jsize: %d", length);
+ return false;
}
+ return true;
}
- mirror::ArtField* CheckFieldID(jfieldID fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* CheckFieldID(ScopedObjectAccess& soa, jfieldID fid)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (fid == nullptr) {
- JniAbortF(function_name_, "jfieldID was NULL");
+ AbortF("jfieldID was NULL");
return nullptr;
}
- mirror::ArtField* f = soa_.DecodeField(fid);
+ mirror::ArtField* f = soa.DecodeField(fid);
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f) || !f->IsArtField()) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "invalid jfieldID: %p", fid);
+ AbortF("invalid jfieldID: %p", fid);
return nullptr;
}
return f;
}
- mirror::ArtMethod* CheckMethodID(jmethodID mid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* CheckMethodID(ScopedObjectAccess& soa, jmethodID mid)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (mid == nullptr) {
- JniAbortF(function_name_, "jmethodID was NULL");
+ AbortF("jmethodID was NULL");
return nullptr;
}
- mirror::ArtMethod* m = soa_.DecodeMethod(mid);
+ mirror::ArtMethod* m = soa.DecodeMethod(mid);
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m) || !m->IsArtMethod()) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "invalid jmethodID: %p", mid);
+ AbortF("invalid jmethodID: %p", mid);
return nullptr;
}
return m;
}
- /*
- * Verify that "jobj" is a valid object, and that it's an object that JNI
- * is allowed to know about. We allow NULL references.
- *
- * Switches to "running" mode before performing checks.
- */
- void CheckObject(jobject java_object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (java_object == nullptr) {
- return;
- }
-
- mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
- Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- // TODO: when we remove work_around_app_jni_bugs, this should be impossible.
- JniAbortF(function_name_, "native code passing in reference to invalid %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
- }
- }
-
- /*
- * Verify that the "mode" argument passed to a primitive array Release
- * function is one of the valid values.
- */
- void CheckReleaseMode(jint mode) {
- if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
- JniAbortF(function_name_, "unknown value for release mode: %d", mode);
- }
- }
-
- void CheckThread(int flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool CheckThread(JNIEnv* env) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Thread* self = Thread::Current();
if (self == nullptr) {
- JniAbortF(function_name_, "a thread (tid %d) is making JNI calls without being attached", GetTid());
- return;
+ AbortF("a thread (tid %d) is making JNI calls without being attached", GetTid());
+ return false;
}
// Get the *correct* JNIEnv by going through our TLS pointer.
@@ -767,21 +1004,22 @@ class ScopedCheck {
// Verify that the current thread is (a) attached and (b) associated with
// this particular instance of JNIEnv.
- if (soa_.Env() != threadEnv) {
- JniAbortF(function_name_, "thread %s using JNIEnv* from thread %s",
- ToStr<Thread>(*self).c_str(), ToStr<Thread>(*soa_.Self()).c_str());
- return;
+ if (env != threadEnv) {
+ AbortF("thread %s using JNIEnv* from thread %s",
+ ToStr<Thread>(*self).c_str(), ToStr<Thread>(*self).c_str());
+ return false;
}
// Verify that, if this thread previously made a critical "get" call, we
// do the corresponding "release" call before we try anything else.
- switch (flags & kFlag_CritMask) {
+ switch (flags_ & kFlag_CritMask) {
case kFlag_CritOkay: // okay to call this method
break;
case kFlag_CritBad: // not okay to call
if (threadEnv->critical) {
- JniAbortF(function_name_, "thread %s using JNI after critical get", ToStr<Thread>(*self).c_str());
- return;
+ AbortF("thread %s using JNI after critical get",
+ ToStr<Thread>(*self).c_str());
+ return false;
}
break;
case kFlag_CritGet: // this is a "get" call
@@ -791,44 +1029,46 @@ class ScopedCheck {
case kFlag_CritRelease: // this is a "release" call
threadEnv->critical--;
if (threadEnv->critical < 0) {
- JniAbortF(function_name_, "thread %s called too many critical releases", ToStr<Thread>(*self).c_str());
- return;
+ AbortF("thread %s called too many critical releases",
+ ToStr<Thread>(*self).c_str());
+ return false;
}
break;
default:
- LOG(FATAL) << "Bad flags (internal error): " << flags;
+ LOG(FATAL) << "Bad flags (internal error): " << flags_;
}
// Verify that, if an exception has been raised, the native code doesn't
// make any JNI calls other than the Exception* methods.
- if ((flags & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
+ if ((flags_ & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
ThrowLocation throw_location;
mirror::Throwable* exception = self->GetException(&throw_location);
std::string type(PrettyTypeOf(exception));
- JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s",
- function_name_, type.c_str(), throw_location.Dump().c_str());
- return;
+ AbortF("JNI %s called with pending exception '%s' thrown in %s",
+ function_name_, type.c_str(), throw_location.Dump().c_str());
+ return false;
}
+ return true;
}
// Verifies that "bytes" points to valid Modified UTF-8 data.
- void CheckUtfString(const char* bytes, bool nullable) {
+ bool CheckUtfString(const char* bytes, bool nullable) {
if (bytes == nullptr) {
if (!nullable) {
- JniAbortF(function_name_, "non-nullable const char* was NULL");
- return;
+ AbortF("non-nullable const char* was NULL");
+ return false;
}
- return;
+ return true;
}
const char* errorKind = nullptr;
uint8_t utf8 = CheckUtfBytes(bytes, &errorKind);
if (errorKind != nullptr) {
- JniAbortF(function_name_,
- "input is not valid Modified UTF-8: illegal %s byte %#x\n"
- " string: '%s'", errorKind, utf8, bytes);
- return;
+ AbortF("input is not valid Modified UTF-8: illegal %s byte %#x\n"
+ " string: '%s'", errorKind, utf8, bytes);
+ return false;
}
+ return true;
}
static uint8_t CheckUtfBytes(const char* bytes, const char** errorKind) {
@@ -880,92 +1120,120 @@ class ScopedCheck {
return 0;
}
- const ScopedObjectAccess soa_;
- const char* function_name_;
- int flags_;
- bool has_method_;
+ void AbortF(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+ va_list args;
+ va_start(args, fmt);
+ Runtime::Current()->GetJavaVM()->JniAbortV(function_name_, fmt, args);
+ va_end(args);
+ }
+
+ // The name of the JNI function being checked.
+ const char* const function_name_;
+
+ const int flags_;
int indent_;
+ const bool has_method_;
+
DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
};
-#define CHECK_JNI_ENTRY(flags, types, args...) \
- ScopedCheck sc(env, flags, __FUNCTION__); \
- sc.Check(true, types, ##args)
-
-#define CHECK_JNI_EXIT(type, exp) ({ \
- auto _rc = (exp); \
- sc.Check(false, type, _rc); \
- _rc; })
-#define CHECK_JNI_EXIT_VOID() \
- sc.Check(false, "V")
-
/*
* ===========================================================================
* Guarded arrays
* ===========================================================================
*/
-#define kGuardLen 512 /* must be multiple of 2 */
-#define kGuardPattern 0xd5e3 /* uncommon values; d5e3d5e3 invalid addr */
-#define kGuardMagic 0xffd5aa96
-
/* this gets tucked in at the start of the buffer; struct size must be even */
-struct GuardedCopy {
- uint32_t magic;
- uLong adler;
- size_t original_length;
- const void* original_ptr;
-
- /* find the GuardedCopy given the pointer into the "live" data */
- static inline const GuardedCopy* FromData(const void* dataBuf) {
- return reinterpret_cast<const GuardedCopy*>(ActualBuffer(dataBuf));
- }
-
+class GuardedCopy {
+ public:
/*
* Create an over-sized buffer to hold the contents of "buf". Copy it in,
* filling in the area around it with guard data.
- *
- * We use a 16-bit pattern to make a rogue memset less likely to elude us.
*/
- static void* Create(const void* buf, size_t len, bool modOkay) {
- size_t newLen = ActualLength(len);
- uint8_t* newBuf = DebugAlloc(newLen);
+ static void* Create(const void* original_buf, size_t len, bool mod_okay) {
+ const size_t new_len = LengthIncludingRedZones(len);
+ uint8_t* const new_buf = DebugAlloc(new_len);
- // Fill it in with a pattern.
- uint16_t* pat = reinterpret_cast<uint16_t*>(newBuf);
- for (size_t i = 0; i < newLen / 2; i++) {
- *pat++ = kGuardPattern;
+ // If modification is not expected, grab a checksum.
+ uLong adler = 0;
+ if (!mod_okay) {
+ adler = adler32(adler32(0L, Z_NULL, 0), reinterpret_cast<const Bytef*>(original_buf), len);
+ }
+
+ GuardedCopy* copy = new (new_buf) GuardedCopy(original_buf, len, adler);
+
+ // Fill begin region with canary pattern.
+ const size_t kStartCanaryLength = (GuardedCopy::kRedZoneSize / 2) - sizeof(GuardedCopy);
+ for (size_t i = 0, j = 0; i < kStartCanaryLength; ++i) {
+ const_cast<char*>(copy->StartRedZone())[i] = kCanary[j];
+ if (kCanary[j] == '\0') {
+ j = 0;
+ }
}
// Copy the data in; note "len" could be zero.
- memcpy(newBuf + kGuardLen / 2, buf, len);
+ memcpy(const_cast<uint8_t*>(copy->BufferWithinRedZones()), original_buf, len);
- // If modification is not expected, grab a checksum.
- uLong adler = 0;
- if (!modOkay) {
- adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, reinterpret_cast<const Bytef*>(buf), len);
- *reinterpret_cast<uLong*>(newBuf) = adler;
+ // Fill end region with canary pattern.
+ for (size_t i = 0, j = 0; i < kEndCanaryLength; ++i) {
+ const_cast<char*>(copy->EndRedZone())[i] = kCanary[j];
+ if (kCanary[j] == '\0') {
+ j = 0;
+ }
}
- GuardedCopy* pExtra = reinterpret_cast<GuardedCopy*>(newBuf);
- pExtra->magic = kGuardMagic;
- pExtra->adler = adler;
- pExtra->original_ptr = buf;
- pExtra->original_length = len;
+ return const_cast<uint8_t*>(copy->BufferWithinRedZones());
+ }
- return newBuf + kGuardLen / 2;
+ /*
+ * Create a guarded copy of a primitive array. Modifications to the copied
+ * data are allowed. Returns a pointer to the copied data.
+ */
+ static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy) {
+ ScopedObjectAccess soa(env);
+
+ mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+ size_t component_size = a->GetClass()->GetComponentSize();
+ size_t byte_count = a->GetLength() * component_size;
+ void* result = Create(a->GetRawData(component_size, 0), byte_count, true);
+ if (is_copy != nullptr) {
+ *is_copy = JNI_TRUE;
+ }
+ return result;
}
/*
+ * Perform the array "release" operation, which may or may not copy data
+ * back into the managed heap, and may or may not release the underlying storage.
+ */
+ static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env, jarray java_array,
+ void* embedded_buf, int mode) {
+ ScopedObjectAccess soa(env);
+ mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+
+ if (!GuardedCopy::Check(function_name, embedded_buf, true)) {
+ return nullptr;
+ }
+ if (mode != JNI_ABORT) {
+ size_t len = FromEmbedded(embedded_buf)->original_length_;
+ memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), embedded_buf, len);
+ }
+ if (mode != JNI_COMMIT) {
+ return Destroy(embedded_buf);
+ }
+ return embedded_buf;
+ }
+
+
+ /*
* Free up the guard buffer, scrub it, and return the original pointer.
*/
- static void* Destroy(void* dataBuf) {
- const GuardedCopy* pExtra = GuardedCopy::FromData(dataBuf);
- void* original_ptr = const_cast<void*>(pExtra->original_ptr);
- size_t len = pExtra->original_length;
- DebugFree(dataBuf, len);
+ static void* Destroy(void* embedded_buf) {
+ GuardedCopy* copy = FromEmbedded(embedded_buf);
+ void* original_ptr = const_cast<void*>(copy->original_ptr_);
+ size_t len = LengthIncludingRedZones(copy->original_length_);
+ DebugFree(copy, len);
return original_ptr;
}
@@ -975,137 +1243,144 @@ struct GuardedCopy {
*
* The caller has already checked that "dataBuf" is non-NULL.
*/
- static void Check(const char* functionName, const void* dataBuf, bool modOkay) {
+ static bool Check(const char* function_name, const void* embedded_buf, bool mod_okay) {
+ const GuardedCopy* copy = FromEmbedded(embedded_buf);
+ return copy->CheckHeader(function_name, mod_okay) && copy->CheckRedZones(function_name);
+ }
+
+ private:
+ GuardedCopy(const void* original_buf, size_t len, uLong adler) :
+ magic_(kGuardMagic), adler_(adler), original_ptr_(original_buf), original_length_(len) {
+ }
+
+ static uint8_t* DebugAlloc(size_t len) {
+ void* result = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (result == MAP_FAILED) {
+ PLOG(FATAL) << "GuardedCopy::create mmap(" << len << ") failed";
+ }
+ return reinterpret_cast<uint8_t*>(result);
+ }
+
+ static void DebugFree(void* buf, size_t len) {
+ if (munmap(buf, len) != 0) {
+ PLOG(FATAL) << "munmap(" << buf << ", " << len << ") failed";
+ }
+ }
+
+ static size_t LengthIncludingRedZones(size_t len) {
+ return len + kRedZoneSize;
+ }
+
+ // Get the GuardedCopy from the interior pointer.
+ static GuardedCopy* FromEmbedded(void* embedded_buf) {
+ return reinterpret_cast<GuardedCopy*>(
+ reinterpret_cast<uint8_t*>(embedded_buf) - (kRedZoneSize / 2));
+ }
+
+ static const GuardedCopy* FromEmbedded(const void* embedded_buf) {
+ return reinterpret_cast<const GuardedCopy*>(
+ reinterpret_cast<const uint8_t*>(embedded_buf) - (kRedZoneSize / 2));
+ }
+
+ static void AbortF(const char* jni_function_name, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ Runtime::Current()->GetJavaVM()->JniAbortV(jni_function_name, fmt, args);
+ va_end(args);
+ }
+
+ bool CheckHeader(const char* function_name, bool mod_okay) const {
static const uint32_t kMagicCmp = kGuardMagic;
- const uint8_t* fullBuf = ActualBuffer(dataBuf);
- const GuardedCopy* pExtra = GuardedCopy::FromData(dataBuf);
// Before we do anything with "pExtra", check the magic number. We
// do the check with memcmp rather than "==" in case the pointer is
// unaligned. If it points to completely bogus memory we're going
// to crash, but there's no easy way around that.
- if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
+ if (UNLIKELY(memcmp(&magic_, &kMagicCmp, 4) != 0)) {
uint8_t buf[4];
- memcpy(buf, &pExtra->magic, 4);
- JniAbortF(functionName,
- "guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
- buf[3], buf[2], buf[1], buf[0], dataBuf); // Assumes little-endian.
+ memcpy(buf, &magic_, 4);
+ AbortF(function_name,
+ "guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
+ buf[3], buf[2], buf[1], buf[0], this); // Assumes little-endian.
+ return false;
}
- size_t len = pExtra->original_length;
-
- // Check bottom half of guard; skip over optional checksum storage.
- const uint16_t* pat = reinterpret_cast<const uint16_t*>(fullBuf);
- for (size_t i = sizeof(GuardedCopy) / 2; i < (kGuardLen / 2 - sizeof(GuardedCopy)) / 2; i++) {
- if (pat[i] != kGuardPattern) {
- JniAbortF(functionName, "guard pattern(1) disturbed at %p +%zd", fullBuf, i*2);
+ // If modification is not expected, verify checksum. Strictly speaking this is wrong: if we
+ // told the client that we made a copy, there's no reason they can't alter the buffer.
+ if (!mod_okay) {
+ uLong computed_adler =
+ adler32(adler32(0L, Z_NULL, 0), BufferWithinRedZones(), original_length_);
+ if (computed_adler != adler_) {
+ AbortF(function_name, "buffer modified (0x%08lx vs 0x%08lx) at address %p",
+ computed_adler, adler_, this);
+ return false;
}
}
+ return true;
+ }
- int offset = kGuardLen / 2 + len;
- if (offset & 0x01) {
- // Odd byte; expected value depends on endian.
- const uint16_t patSample = kGuardPattern;
- uint8_t expected_byte = reinterpret_cast<const uint8_t*>(&patSample)[1];
- if (fullBuf[offset] != expected_byte) {
- JniAbortF(functionName, "guard pattern disturbed in odd byte after %p +%d 0x%02x 0x%02x",
- fullBuf, offset, fullBuf[offset], expected_byte);
+ bool CheckRedZones(const char* function_name) const {
+ // Check the begin red zone.
+ const size_t kStartCanaryLength = (GuardedCopy::kRedZoneSize / 2) - sizeof(GuardedCopy);
+ for (size_t i = 0, j = 0; i < kStartCanaryLength; ++i) {
+ if (UNLIKELY(StartRedZone()[i] != kCanary[j])) {
+ AbortF(function_name, "guard pattern before buffer disturbed at %p +%zd", this, i);
+ return false;
}
- offset++;
- }
-
- // Check top half of guard.
- pat = reinterpret_cast<const uint16_t*>(fullBuf + offset);
- for (size_t i = 0; i < kGuardLen / 4; i++) {
- if (pat[i] != kGuardPattern) {
- JniAbortF(functionName, "guard pattern(2) disturbed at %p +%zd", fullBuf, offset + i*2);
+ if (kCanary[j] == '\0') {
+ j = 0;
}
}
- // If modification is not expected, verify checksum. Strictly speaking
- // this is wrong: if we told the client that we made a copy, there's no
- // reason they can't alter the buffer.
- if (!modOkay) {
- uLong adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, (const Bytef*)dataBuf, len);
- if (pExtra->adler != adler) {
- JniAbortF(functionName, "buffer modified (0x%08lx vs 0x%08lx) at address %p",
- pExtra->adler, adler, dataBuf);
+ // Check end region.
+ for (size_t i = 0, j = 0; i < kEndCanaryLength; ++i) {
+ if (UNLIKELY(EndRedZone()[i] != kCanary[j])) {
+ size_t offset_from_buffer_start =
+ &(EndRedZone()[i]) - &(StartRedZone()[kStartCanaryLength]);
+ AbortF(function_name, "guard pattern after buffer disturbed at %p +%zd", this,
+ offset_from_buffer_start);
+ return false;
+ }
+ if (kCanary[j] == '\0') {
+ j = 0;
}
}
+ return true;
}
- private:
- static uint8_t* DebugAlloc(size_t len) {
- void* result = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
- if (result == MAP_FAILED) {
- PLOG(FATAL) << "GuardedCopy::create mmap(" << len << ") failed";
- }
- return reinterpret_cast<uint8_t*>(result);
- }
-
- static void DebugFree(void* dataBuf, size_t len) {
- uint8_t* fullBuf = ActualBuffer(dataBuf);
- size_t totalByteCount = ActualLength(len);
- // TODO: we could mprotect instead, and keep the allocation around for a while.
- // This would be even more expensive, but it might catch more errors.
- // if (mprotect(fullBuf, totalByteCount, PROT_NONE) != 0) {
- // PLOG(WARNING) << "mprotect(PROT_NONE) failed";
- // }
- if (munmap(fullBuf, totalByteCount) != 0) {
- PLOG(FATAL) << "munmap(" << reinterpret_cast<void*>(fullBuf) << ", " << totalByteCount << ") failed";
- }
+ // Location that canary value will be written before the guarded region.
+ const char* StartRedZone() const {
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(this);
+ return reinterpret_cast<const char*>(buf + sizeof(GuardedCopy));
}
- static const uint8_t* ActualBuffer(const void* dataBuf) {
- return reinterpret_cast<const uint8_t*>(dataBuf) - kGuardLen / 2;
+ // Return the interior embedded buffer.
+ const uint8_t* BufferWithinRedZones() const {
+ const uint8_t* embedded_buf = reinterpret_cast<const uint8_t*>(this) + (kRedZoneSize / 2);
+ return embedded_buf;
}
- static uint8_t* ActualBuffer(void* dataBuf) {
- return reinterpret_cast<uint8_t*>(dataBuf) - kGuardLen / 2;
+ // Location that canary value will be written after the guarded region.
+ const char* EndRedZone() const {
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(this);
+ size_t buf_len = LengthIncludingRedZones(original_length_);
+ return reinterpret_cast<const char*>(buf + (buf_len - (kRedZoneSize / 2)));
}
- // Underlying length of a user allocation of 'length' bytes.
- static size_t ActualLength(size_t length) {
- return (length + kGuardLen + 1) & ~0x01;
- }
-};
+ static constexpr size_t kRedZoneSize = 512;
+ static constexpr size_t kEndCanaryLength = kRedZoneSize / 2;
-/*
- * Create a guarded copy of a primitive array. Modifications to the copied
- * data are allowed. Returns a pointer to the copied data.
- */
-static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* isCopy) {
- ScopedObjectAccess soa(env);
-
- mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
- size_t component_size = a->GetClass()->GetComponentSize();
- size_t byte_count = a->GetLength() * component_size;
- void* result = GuardedCopy::Create(a->GetRawData(component_size, 0), byte_count, true);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
- }
- return result;
-}
+ // Value written before and after the guarded array.
+ static const char* const kCanary;
-/*
- * Perform the array "release" operation, which may or may not copy data
- * back into the managed heap, and may or may not release the underlying storage.
- */
-static void ReleaseGuardedPACopy(JNIEnv* env, jarray java_array, void* dataBuf, int mode) {
- ScopedObjectAccess soa(env);
- mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+ static constexpr uint32_t kGuardMagic = 0xffd5aa96;
- GuardedCopy::Check(__FUNCTION__, dataBuf, true);
-
- if (mode != JNI_ABORT) {
- size_t len = GuardedCopy::FromData(dataBuf)->original_length;
- memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), dataBuf, len);
- }
- if (mode != JNI_COMMIT) {
- GuardedCopy::Destroy(dataBuf);
- }
-}
+ const uint32_t magic_;
+ const uLong adler_;
+ const void* const original_ptr_;
+ const size_t original_length_;
+};
+const char* const GuardedCopy::kCanary = "JNI BUFFER RED ZONE";
/*
* ===========================================================================
@@ -1116,667 +1391,1953 @@ static void ReleaseGuardedPACopy(JNIEnv* env, jarray java_array, void* dataBuf,
class CheckJNI {
public:
static jint GetVersion(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_Default, "E", env);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetVersion(env));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[1] = {{.E = env }};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ result.I = baseEnv(env)->GetVersion(env);
+ if (sc.Check(soa, false, "I", &result)) {
+ return result.I;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env }, {.p = vm}};
+ if (sc.Check(soa, true, "Ep", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->GetJavaVM(env, vm);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env }, {.c = c}, {.p = methods}, {.I = nMethods}};
+ if (sc.Check(soa, true, "EcpI", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->RegisterNatives(env, c, methods, nMethods);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jint UnregisterNatives(JNIEnv* env, jclass c) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env }, {.c = c}};
+ if (sc.Check(soa, true, "Ec", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->UnregisterNatives(env, c);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
+ // Note: we use "Ep" rather than "EL" because this is the one JNI function that it's okay to
+ // pass an invalid reference to.
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env }, {.p = obj}};
+ if (sc.Check(soa, true, "Ep", args)) {
+ JniValueType result;
+ result.w = baseEnv(env)->GetObjectRefType(env, obj);
+ if (sc.Check(soa, false, "w", &result)) {
+ return result.w;
+ }
+ }
+ return JNIInvalidRefType;
}
- static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf, jsize bufLen) {
- CHECK_JNI_ENTRY(kFlag_Default, "EuLpz", env, name, loader, buf, bufLen);
- sc.CheckClassName(name);
- return CHECK_JNI_EXIT("c", baseEnv(env)->DefineClass(env, name, loader, buf, bufLen));
+ static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf,
+ jsize bufLen) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.u = name}, {.L = loader}, {.p = buf}, {.z = bufLen}};
+ if (sc.Check(soa, true, "EuLpz", args) && sc.CheckClassName(name)) {
+ JniValueType result;
+ result.c = baseEnv(env)->DefineClass(env, name, loader, buf, bufLen);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jclass FindClass(JNIEnv* env, const char* name) {
- CHECK_JNI_ENTRY(kFlag_Default, "Eu", env, name);
- sc.CheckClassName(name);
- return CHECK_JNI_EXIT("c", baseEnv(env)->FindClass(env, name));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.u = name}};
+ if (sc.Check(soa, true, "Eu", args) && sc.CheckClassName(name)) {
+ JniValueType result;
+ result.c = baseEnv(env)->FindClass(env, name);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jclass GetSuperclass(JNIEnv* env, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
- return CHECK_JNI_EXIT("c", baseEnv(env)->GetSuperclass(env, c));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.c = c}};
+ if (sc.Check(soa, true, "Ec", args)) {
+ JniValueType result;
+ result.c = baseEnv(env)->GetSuperclass(env, c);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jboolean IsAssignableFrom(JNIEnv* env, jclass c1, jclass c2) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecc", env, c1, c2);
- return CHECK_JNI_EXIT("b", baseEnv(env)->IsAssignableFrom(env, c1, c2));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.c = c1}, {.c = c2}};
+ if (sc.Check(soa, true, "Ecc", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->IsAssignableFrom(env, c1, c2);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
}
static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, method);
- // TODO: check that 'field' is a java.lang.reflect.Method.
- return CHECK_JNI_EXIT("m", baseEnv(env)->FromReflectedMethod(env, method));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = method}};
+ if (sc.Check(soa, true, "EL", args) && sc.CheckReflectedMethod(soa, method)) {
+ JniValueType result;
+ result.m = baseEnv(env)->FromReflectedMethod(env, method);
+ if (sc.Check(soa, false, "m", &result)) {
+ return result.m;
+ }
+ }
+ return nullptr;
}
static jfieldID FromReflectedField(JNIEnv* env, jobject field) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, field);
- // TODO: check that 'field' is a java.lang.reflect.Field.
- return CHECK_JNI_EXIT("f", baseEnv(env)->FromReflectedField(env, field));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = field}};
+ if (sc.Check(soa, true, "EL", args) && sc.CheckReflectedField(soa, field)) {
+ JniValueType result;
+ result.f = baseEnv(env)->FromReflectedField(env, field);
+ if (sc.Check(soa, false, "f", &result)) {
+ return result.f;
+ }
+ }
+ return nullptr;
}
static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecmb", env, cls, mid, isStatic);
- return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.b = isStatic}};
+ if (sc.Check(soa, true, "Ecmb", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic);
+ if (sc.Check(soa, false, "L", &result) && (result.L != nullptr)) {
+ DCHECK(sc.CheckReflectedMethod(soa, result.L));
+ return result.L;
+ }
+ }
+ return nullptr;
}
static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecfb", env, cls, fid, isStatic);
- return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedField(env, cls, fid, isStatic));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.b = isStatic}};
+ if (sc.Check(soa, true, "Ecfb", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->ToReflectedField(env, cls, fid, isStatic);
+ if (sc.Check(soa, false, "L", &result) && (result.L != nullptr)) {
+ DCHECK(sc.CheckReflectedField(soa, result.L));
+ return result.L;
+ }
+ }
+ return nullptr;
}
static jint Throw(JNIEnv* env, jthrowable obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- // TODO: check that 'obj' is a java.lang.Throwable.
- return CHECK_JNI_EXIT("I", baseEnv(env)->Throw(env, obj));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.t = obj}};
+ if (sc.Check(soa, true, "Et", args) && sc.CheckThrowable(soa, obj)) {
+ JniValueType result;
+ result.i = baseEnv(env)->Throw(env, obj);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jint ThrowNew(JNIEnv* env, jclass c, const char* message) {
- CHECK_JNI_ENTRY(kFlag_NullableUtf, "Ecu", env, c, message);
- return CHECK_JNI_EXIT("I", baseEnv(env)->ThrowNew(env, c, message));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.c = c}, {.u = message}};
+ if (sc.Check(soa, true, "Ecu", args) && sc.CheckThrowableClass(soa, c)) {
+ JniValueType result;
+ result.i = baseEnv(env)->ThrowNew(env, c, message);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jthrowable ExceptionOccurred(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
- return CHECK_JNI_EXIT("L", baseEnv(env)->ExceptionOccurred(env));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ result.t = baseEnv(env)->ExceptionOccurred(env);
+ if (sc.Check(soa, false, "t", &result)) {
+ return result.t;
+ }
+ }
+ return nullptr;
}
static void ExceptionDescribe(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
- baseEnv(env)->ExceptionDescribe(env);
- CHECK_JNI_EXIT_VOID();
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ baseEnv(env)->ExceptionDescribe(env);
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
static void ExceptionClear(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
- baseEnv(env)->ExceptionClear(env);
- CHECK_JNI_EXIT_VOID();
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ baseEnv(env)->ExceptionClear(env);
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static jboolean ExceptionCheck(JNIEnv* env) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay | kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->ExceptionCheck(env);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
}
static void FatalError(JNIEnv* env, const char* msg) {
// The JNI specification doesn't say it's okay to call FatalError with a pending exception,
// but you're about to abort anyway, and it's quite likely that you have a pending exception,
// and it's not unimaginable that you don't know that you do. So we allow it.
- CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_NullableUtf, "Eu", env, msg);
- baseEnv(env)->FatalError(env, msg);
- CHECK_JNI_EXIT_VOID();
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay | kFlag_NullableUtf, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.u = msg}};
+ if (sc.Check(soa, true, "Eu", args)) {
+ JniValueType result;
+ baseEnv(env)->FatalError(env, msg);
+ // Unreachable.
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
static jint PushLocalFrame(JNIEnv* env, jint capacity) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EI", env, capacity);
- return CHECK_JNI_EXIT("I", baseEnv(env)->PushLocalFrame(env, capacity));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.I = capacity}};
+ if (sc.Check(soa, true, "EI", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->PushLocalFrame(env, capacity);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jobject PopLocalFrame(JNIEnv* env, jobject res) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, res);
- return CHECK_JNI_EXIT("L", baseEnv(env)->PopLocalFrame(env, res));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = res}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->PopLocalFrame(env, res);
+ sc.Check(soa, false, "L", &result);
+ return result.L;
+ }
+ return nullptr;
}
static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewGlobalRef(env, obj));
+ return NewRef(__FUNCTION__, env, obj, kGlobal);
}
- static jobject NewLocalRef(JNIEnv* env, jobject ref) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, ref);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewLocalRef(env, ref));
+ static jobject NewLocalRef(JNIEnv* env, jobject obj) {
+ return NewRef(__FUNCTION__, env, obj, kLocal);
}
- static void DeleteGlobalRef(JNIEnv* env, jobject globalRef) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, globalRef);
- if (globalRef != nullptr && GetIndirectRefKind(globalRef) != kGlobal) {
- JniAbortF(__FUNCTION__, "DeleteGlobalRef on %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(globalRef)).c_str(), globalRef);
- } else {
- baseEnv(env)->DeleteGlobalRef(env, globalRef);
- CHECK_JNI_EXIT_VOID();
- }
+ static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
+ return NewRef(__FUNCTION__, env, obj, kWeakGlobal);
}
- static void DeleteWeakGlobalRef(JNIEnv* env, jweak weakGlobalRef) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, weakGlobalRef);
- if (weakGlobalRef != nullptr && GetIndirectRefKind(weakGlobalRef) != kWeakGlobal) {
- JniAbortF(__FUNCTION__, "DeleteWeakGlobalRef on %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(weakGlobalRef)).c_str(), weakGlobalRef);
- } else {
- baseEnv(env)->DeleteWeakGlobalRef(env, weakGlobalRef);
- CHECK_JNI_EXIT_VOID();
- }
+ static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
+ DeleteRef(__FUNCTION__, env, obj, kGlobal);
}
- static void DeleteLocalRef(JNIEnv* env, jobject localRef) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, localRef);
- if (localRef != nullptr && GetIndirectRefKind(localRef) != kLocal && !IsHandleScopeLocalRef(env, localRef)) {
- JniAbortF(__FUNCTION__, "DeleteLocalRef on %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(localRef)).c_str(), localRef);
- } else {
- baseEnv(env)->DeleteLocalRef(env, localRef);
- CHECK_JNI_EXIT_VOID();
- }
+ static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
+ DeleteRef(__FUNCTION__, env, obj, kWeakGlobal);
+ }
+
+ static void DeleteLocalRef(JNIEnv* env, jobject obj) {
+ DeleteRef(__FUNCTION__, env, obj, kLocal);
}
static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) {
- CHECK_JNI_ENTRY(kFlag_Default, "EI", env, capacity);
- return CHECK_JNI_EXIT("I", baseEnv(env)->EnsureLocalCapacity(env, capacity));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.I = capacity}};
+ if (sc.Check(soa, true, "EI", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->EnsureLocalCapacity(env, capacity);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
- CHECK_JNI_ENTRY(kFlag_Default, "ELL", env, ref1, ref2);
- return CHECK_JNI_EXIT("b", baseEnv(env)->IsSameObject(env, ref1, ref2));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.L = ref1}, {.L = ref2}};
+ if (sc.Check(soa, true, "ELL", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->IsSameObject(env, ref1, ref2);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
}
static jobject AllocObject(JNIEnv* env, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
- return CHECK_JNI_EXIT("L", baseEnv(env)->AllocObject(env, c));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.c = c}};
+ if (sc.Check(soa, true, "Ec", args) && sc.CheckInstantiableNonArray(soa, c)) {
+ JniValueType result;
+ result.L = baseEnv(env)->AllocObject(env, c);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
+ }
+
+ static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+ if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) &&
+ sc.CheckConstructor(soa, mid)) {
+ JniValueType result;
+ result.L = baseEnv(env)->NewObjectV(env, c, mid, vargs);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
}
static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
va_list args;
va_start(args, mid);
- jobject result = baseEnv(env)->NewObjectV(env, c, mid, args);
+ jobject result = NewObjectV(env, c, mid, args);
va_end(args);
- return CHECK_JNI_EXIT("L", result);
- }
-
- static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list args) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectV(env, c, mid, args));
+ return result;
}
- static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* args) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectA(env, c, mid, args));
+ static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+ if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) &&
+ sc.CheckConstructor(soa, mid)) {
+ JniValueType result;
+ result.L = baseEnv(env)->NewObjectA(env, c, mid, vargs);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
}
static jclass GetObjectClass(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- return CHECK_JNI_EXIT("c", baseEnv(env)->GetObjectClass(env, obj));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.c = baseEnv(env)->GetObjectClass(env, obj);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "ELc", env, obj, c);
- return CHECK_JNI_EXIT("b", baseEnv(env)->IsInstanceOf(env, obj, c));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.L = obj}, {.c = c}};
+ if (sc.Check(soa, true, "ELc", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->IsInstanceOf(env, obj, c);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
}
static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("m", baseEnv(env)->GetMethodID(env, c, name, sig));
+ return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, false);
}
- static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("f", baseEnv(env)->GetFieldID(env, c, name, sig));
+ static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, true);
}
- static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("m", baseEnv(env)->GetStaticMethodID(env, c, name, sig));
+ static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, false);
}
static jfieldID GetStaticFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("f", baseEnv(env)->GetStaticFieldID(env, c, name, sig));
- }
-
-#define FIELD_ACCESSORS(_ctype, _jname, _jvalue_type, _type) \
- static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass c, jfieldID fid) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecf", env, c, fid); \
- sc.CheckStaticFieldID(c, fid); \
- return CHECK_JNI_EXIT(_type, baseEnv(env)->GetStatic##_jname##Field(env, c, fid)); \
- } \
- static _ctype Get##_jname##Field(JNIEnv* env, jobject obj, jfieldID fid) { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELf", env, obj, fid); \
- sc.CheckInstanceFieldID(obj, fid); \
- return CHECK_JNI_EXIT(_type, baseEnv(env)->Get##_jname##Field(env, obj, fid)); \
- } \
- static void SetStatic##_jname##Field(JNIEnv* env, jclass c, jfieldID fid, _ctype value) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecf" _type, env, c, fid, value); \
- sc.CheckStaticFieldID(c, fid); \
- /* "value" arg only used when type == ref */ \
- jvalue java_type_value; \
- java_type_value._jvalue_type = value; \
- sc.CheckFieldType(java_type_value, fid, _type[0], true); \
- baseEnv(env)->SetStatic##_jname##Field(env, c, fid, value); \
- CHECK_JNI_EXIT_VOID(); \
- } \
- static void Set##_jname##Field(JNIEnv* env, jobject obj, jfieldID fid, _ctype value) { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELf" _type, env, obj, fid, value); \
- sc.CheckInstanceFieldID(obj, fid); \
- /* "value" arg only used when type == ref */ \
- jvalue java_type_value; \
- java_type_value._jvalue_type = value; \
- sc.CheckFieldType(java_type_value, fid, _type[0], false); \
- baseEnv(env)->Set##_jname##Field(env, obj, fid, value); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-FIELD_ACCESSORS(jobject, Object, l, "L");
-FIELD_ACCESSORS(jboolean, Boolean, z, "Z");
-FIELD_ACCESSORS(jbyte, Byte, b, "B");
-FIELD_ACCESSORS(jchar, Char, c, "C");
-FIELD_ACCESSORS(jshort, Short, s, "S");
-FIELD_ACCESSORS(jint, Int, i, "I");
-FIELD_ACCESSORS(jlong, Long, j, "J");
-FIELD_ACCESSORS(jfloat, Float, f, "F");
-FIELD_ACCESSORS(jdouble, Double, d, "D");
-
-#define CALL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
- /* Virtual... */ \
- static _ctype Call##_jname##Method(JNIEnv* env, jobject obj, \
- jmethodID mid, ...) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- va_list args; \
- va_start(args, mid); \
- _retasgn(baseEnv(env)->Call##_jname##MethodV(env, obj, mid, args)); \
- va_end(args); \
- _retok; \
- } \
- static _ctype Call##_jname##MethodV(JNIEnv* env, jobject obj, \
- jmethodID mid, va_list args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->Call##_jname##MethodV(env, obj, mid, args)); \
- _retok; \
- } \
- static _ctype Call##_jname##MethodA(JNIEnv* env, jobject obj, \
- jmethodID mid, jvalue* args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->Call##_jname##MethodA(env, obj, mid, args)); \
- _retok; \
- } \
- /* Non-virtual... */ \
- static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, \
- jobject obj, jclass c, jmethodID mid, ...) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- va_list args; \
- va_start(args, mid); \
- _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, c, mid, args)); \
- va_end(args); \
- _retok; \
- } \
- static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, \
- jobject obj, jclass c, jmethodID mid, va_list args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, c, mid, args)); \
- _retok; \
- } \
- static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, \
- jobject obj, jclass c, jmethodID mid, jvalue* args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodA(env, obj, c, mid, args)); \
- _retok; \
- } \
- /* Static... */ \
- static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass c, jmethodID mid, ...) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, true); \
- sc.CheckStaticMethod(c, mid); \
- _retdecl; \
- va_list args; \
- va_start(args, mid); \
- _retasgn(baseEnv(env)->CallStatic##_jname##MethodV(env, c, mid, args)); \
- va_end(args); \
- _retok; \
- } \
- static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass c, jmethodID mid, va_list args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, true); \
- sc.CheckStaticMethod(c, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallStatic##_jname##MethodV(env, c, mid, args)); \
- _retok; \
- } \
- static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, true); \
- sc.CheckStaticMethod(c, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallStatic##_jname##MethodA(env, c, mid, args)); \
- _retok; \
- }
-
-#define NON_VOID_RETURN(_retsig, _ctype) return CHECK_JNI_EXIT(_retsig, (_ctype) result)
-#define VOID_RETURN CHECK_JNI_EXIT_VOID()
-
-CALL(jobject, Object, mirror::Object* result, result = reinterpret_cast<mirror::Object*>, NON_VOID_RETURN("L", jobject), "L");
-CALL(jboolean, Boolean, jboolean result, result =, NON_VOID_RETURN("Z", jboolean), "Z");
-CALL(jbyte, Byte, jbyte result, result =, NON_VOID_RETURN("B", jbyte), "B");
-CALL(jchar, Char, jchar result, result =, NON_VOID_RETURN("C", jchar), "C");
-CALL(jshort, Short, jshort result, result =, NON_VOID_RETURN("S", jshort), "S");
-CALL(jint, Int, jint result, result =, NON_VOID_RETURN("I", jint), "I");
-CALL(jlong, Long, jlong result, result =, NON_VOID_RETURN("J", jlong), "J");
-CALL(jfloat, Float, jfloat result, result =, NON_VOID_RETURN("F", jfloat), "F");
-CALL(jdouble, Double, jdouble result, result =, NON_VOID_RETURN("D", jdouble), "D");
-CALL(void, Void, , , VOID_RETURN, "V");
-
- static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
- CHECK_JNI_ENTRY(kFlag_Default, "Epz", env, unicodeChars, len);
- return CHECK_JNI_EXIT("s", baseEnv(env)->NewString(env, unicodeChars, len));
+ return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, true);
+ }
+
+#define FIELD_ACCESSORS(jtype, name, ptype, shorty) \
+ static jtype GetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid) { \
+ return GetField(__FUNCTION__, env, c, fid, true, ptype).shorty; \
+ } \
+ \
+ static jtype Get##name##Field(JNIEnv* env, jobject obj, jfieldID fid) { \
+ return GetField(__FUNCTION__, env, obj, fid, false, ptype).shorty; \
+ } \
+ \
+ static void SetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid, jtype v) { \
+ JniValueType value; \
+ value.shorty = v; \
+ SetField(__FUNCTION__, env, c, fid, true, ptype, value); \
+ } \
+ \
+ static void Set##name##Field(JNIEnv* env, jobject obj, jfieldID fid, jtype v) { \
+ JniValueType value; \
+ value.shorty = v; \
+ SetField(__FUNCTION__, env, obj, fid, false, ptype, value); \
+ }
+
+ FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L)
+ FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z)
+ FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B)
+ FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C)
+ FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S)
+ FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I)
+ FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J)
+ FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F)
+ FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D)
+#undef FIELD_ACCESSORS
+
+ static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) {
+ CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+ }
+
+ static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid,
+ jvalue* vargs) {
+ CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+ }
+
+ static void CallStaticVoidMethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+ CallMethodA(__FUNCTION__, env, c, nullptr, mid, vargs, Primitive::kPrimVoid, kStatic);
+ }
+
+ static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list vargs) {
+ CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+ }
+
+ static void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass c, jmethodID mid,
+ va_list vargs) {
+ CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+ }
+
+ static void CallStaticVoidMethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+ CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+ }
+
+ static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
+ va_list vargs;
+ va_start(vargs, mid);
+ CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+ va_end(vargs);
+ }
+
+ static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass c, jmethodID mid, ...) {
+ va_list vargs;
+ va_start(vargs, mid);
+ CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+ va_end(vargs);
+ }
+
+ static void CallStaticVoidMethod(JNIEnv* env, jclass c, jmethodID mid, ...) {
+ va_list vargs;
+ va_start(vargs, mid);
+ CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+ va_end(vargs);
+ }
+
+#define CALL(rtype, name, ptype, shorty) \
+ static rtype Call##name##MethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) { \
+ return CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+ } \
+ \
+ static rtype CallNonvirtual##name##MethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+ jvalue* vargs) { \
+ return CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+ } \
+ \
+ static rtype CallStatic##name##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { \
+ return CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+ } \
+ \
+ static rtype Call##name##MethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list vargs) { \
+ return CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+ } \
+ \
+ static rtype CallNonvirtual##name##MethodV(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+ va_list vargs) { \
+ return CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+ } \
+ \
+ static rtype CallStatic##name##MethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) { \
+ return CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+ } \
+ \
+ static rtype Call##name##Method(JNIEnv* env, jobject obj, jmethodID mid, ...) { \
+ va_list vargs; \
+ va_start(vargs, mid); \
+ rtype result = \
+ CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+ va_end(vargs); \
+ return result; \
+ } \
+ \
+ static rtype CallNonvirtual##name##Method(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+ ...) { \
+ va_list vargs; \
+ va_start(vargs, mid); \
+ rtype result = \
+ CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+ va_end(vargs); \
+ return result; \
+ } \
+ \
+ static rtype CallStatic##name##Method(JNIEnv* env, jclass c, jmethodID mid, ...) { \
+ va_list vargs; \
+ va_start(vargs, mid); \
+ rtype result = \
+ CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+ va_end(vargs); \
+ return result; \
+ }
+
+ CALL(jobject, Object, Primitive::kPrimNot, L)
+ CALL(jboolean, Boolean, Primitive::kPrimBoolean, Z)
+ CALL(jbyte, Byte, Primitive::kPrimByte, B)
+ CALL(jchar, Char, Primitive::kPrimChar, C)
+ CALL(jshort, Short, Primitive::kPrimShort, S)
+ CALL(jint, Int, Primitive::kPrimInt, I)
+ CALL(jlong, Long, Primitive::kPrimLong, J)
+ CALL(jfloat, Float, Primitive::kPrimFloat, F)
+ CALL(jdouble, Double, Primitive::kPrimDouble, D)
+#undef CALL
+
+ static jstring NewString(JNIEnv* env, const jchar* unicode_chars, jsize len) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.p = unicode_chars}, {.z = len}};
+ if (sc.Check(soa, true, "Epz", args)) {
+ JniValueType result;
+ result.s = baseEnv(env)->NewString(env, unicode_chars, len);
+ if (sc.Check(soa, false, "s", &result)) {
+ return result.s;
+ }
+ }
+ return nullptr;
}
- static jsize GetStringLength(JNIEnv* env, jstring string) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringLength(env, string));
+ static jstring NewStringUTF(JNIEnv* env, const char* chars) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.u = chars}};
+ if (sc.Check(soa, true, "Eu", args)) {
+ JniValueType result;
+ // TODO: stale? show pointer and truncate string.
+ result.s = baseEnv(env)->NewStringUTF(env, chars);
+ if (sc.Check(soa, false, "s", &result)) {
+ return result.s;
+ }
+ }
+ return nullptr;
}
- static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, java_string, isCopy);
- const jchar* result = baseEnv(env)->GetStringChars(env, java_string, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- mirror::String* s = sc.soa().Decode<mirror::String*>(java_string);
- int byteCount = s->GetLength() * 2;
- result = (const jchar*) GuardedCopy::Create(result, byteCount, false);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
+ static jsize GetStringLength(JNIEnv* env, jstring string) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.s = string}};
+ if (sc.Check(soa, true, "Es", args)) {
+ JniValueType result;
+ result.z = baseEnv(env)->GetStringLength(env, string);
+ if (sc.Check(soa, false, "z", &result)) {
+ return result.z;
}
}
- return CHECK_JNI_EXIT("p", result);
+ return JNI_ERR;
}
- static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Esp", env, string, chars);
- sc.CheckNonNull(chars);
- if (sc.ForceCopy()) {
- GuardedCopy::Check(__FUNCTION__, chars, false);
- chars = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<jchar*>(chars)));
+ static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.s = string}};
+ if (sc.Check(soa, true, "Es", args)) {
+ JniValueType result;
+ result.z = baseEnv(env)->GetStringUTFLength(env, string);
+ if (sc.Check(soa, false, "z", &result)) {
+ return result.z;
+ }
}
- baseEnv(env)->ReleaseStringChars(env, string, chars);
- CHECK_JNI_EXIT_VOID();
+ return JNI_ERR;
}
- static jstring NewStringUTF(JNIEnv* env, const char* bytes) {
- CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, bytes); // TODO: show pointer and truncate string.
- return CHECK_JNI_EXIT("s", baseEnv(env)->NewStringUTF(env, bytes));
+ static const jchar* GetStringChars(JNIEnv* env, jstring string, jboolean* is_copy) {
+ return reinterpret_cast<const jchar*>(GetStringCharsInternal(__FUNCTION__, env, string,
+ is_copy, false, false));
}
- static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringUTFLength(env, string));
+ static const char* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* is_copy) {
+ return reinterpret_cast<const char*>(GetStringCharsInternal(__FUNCTION__, env, string,
+ is_copy, true, false));
}
- static const char* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
- const char* result = baseEnv(env)->GetStringUTFChars(env, string, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- result = (const char*) GuardedCopy::Create(result, strlen(result) + 1, false);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
- }
- }
- return CHECK_JNI_EXIT("u", result); // TODO: show pointer and truncate string.
+ static const jchar* GetStringCritical(JNIEnv* env, jstring string, jboolean* is_copy) {
+ return reinterpret_cast<const jchar*>(GetStringCharsInternal(__FUNCTION__, env, string,
+ is_copy, false, true));
+ }
+
+ static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
+ ReleaseStringCharsInternal(__FUNCTION__, env, string, chars, false, false);
}
static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_Release, "Esu", env, string, utf); // TODO: show pointer and truncate string.
- if (sc.ForceCopy()) {
- GuardedCopy::Check(__FUNCTION__, utf, false);
- utf = reinterpret_cast<const char*>(GuardedCopy::Destroy(const_cast<char*>(utf)));
+ ReleaseStringCharsInternal(__FUNCTION__, env, string, utf, true, false);
+ }
+
+ static void ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* chars) {
+ ReleaseStringCharsInternal(__FUNCTION__, env, string, chars, false, true);
+ }
+
+ static void GetStringRegion(JNIEnv* env, jstring string, jsize start, jsize len, jchar* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EsIIp", args)) {
+ baseEnv(env)->GetStringRegion(env, string, start, len, buf);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
}
- baseEnv(env)->ReleaseStringUTFChars(env, string, utf);
- CHECK_JNI_EXIT_VOID();
}
- static jsize GetArrayLength(JNIEnv* env, jarray array) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Ea", env, array);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetArrayLength(env, array));
+ static void GetStringUTFRegion(JNIEnv* env, jstring string, jsize start, jsize len, char* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EsIIp", args)) {
+ baseEnv(env)->GetStringUTFRegion(env, string, start, len, buf);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
- static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass elementClass, jobject initialElement) {
- CHECK_JNI_ENTRY(kFlag_Default, "EzcL", env, length, elementClass, initialElement);
- return CHECK_JNI_EXIT("a", baseEnv(env)->NewObjectArray(env, length, elementClass, initialElement));
+ static jsize GetArrayLength(JNIEnv* env, jarray array) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.a = array}};
+ if (sc.Check(soa, true, "Ea", args)) {
+ JniValueType result;
+ result.z = baseEnv(env)->GetArrayLength(env, array);
+ if (sc.Check(soa, false, "z", &result)) {
+ return result.z;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_class,
+ jobject initial_element) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] =
+ {{.E = env}, {.z = length}, {.c = element_class}, {.L = initial_element}};
+ if (sc.Check(soa, true, "EzcL", args)) {
+ JniValueType result;
+ // Note: assignability tests of initial_element are done in the base implementation.
+ result.a = baseEnv(env)->NewObjectArray(env, length, element_class, initial_element);
+ if (sc.Check(soa, false, "a", &result)) {
+ return down_cast<jobjectArray>(result.a);
+ }
+ }
+ return nullptr;
}
static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
- CHECK_JNI_ENTRY(kFlag_Default, "EaI", env, array, index);
- return CHECK_JNI_EXIT("L", baseEnv(env)->GetObjectArrayElement(env, array, index));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.a = array}, {.z = index}};
+ if (sc.Check(soa, true, "Eaz", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->GetObjectArrayElement(env, array, index);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
}
static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) {
- CHECK_JNI_ENTRY(kFlag_Default, "EaIL", env, array, index, value);
- baseEnv(env)->SetObjectArrayElement(env, array, index, value);
- CHECK_JNI_EXIT_VOID();
- }
-
-#define NEW_PRIMITIVE_ARRAY(_artype, _jname) \
- static _artype New##_jname##Array(JNIEnv* env, jsize length) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ez", env, length); \
- return CHECK_JNI_EXIT("a", baseEnv(env)->New##_jname##Array(env, length)); \
- }
-NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean);
-NEW_PRIMITIVE_ARRAY(jbyteArray, Byte);
-NEW_PRIMITIVE_ARRAY(jcharArray, Char);
-NEW_PRIMITIVE_ARRAY(jshortArray, Short);
-NEW_PRIMITIVE_ARRAY(jintArray, Int);
-NEW_PRIMITIVE_ARRAY(jlongArray, Long);
-NEW_PRIMITIVE_ARRAY(jfloatArray, Float);
-NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
-
-struct ForceCopyGetChecker {
- public:
- ForceCopyGetChecker(ScopedCheck& sc, jboolean* isCopy) {
- force_copy = sc.ForceCopy();
- no_copy = 0;
- if (force_copy && isCopy != nullptr) {
- // Capture this before the base call tramples on it.
- no_copy = *reinterpret_cast<uint32_t*>(isCopy);
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env}, {.a = array}, {.z = index}, {.L = value}};
+ // Note: the index arguments is checked as 'I' rather than 'z' as invalid indices result in
+ // ArrayIndexOutOfBoundsExceptions in the base implementation. Similarly invalid stores result
+ // in ArrayStoreExceptions.
+ if (sc.Check(soa, true, "EaIL", args)) {
+ baseEnv(env)->SetObjectArrayElement(env, array, index, value);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
}
}
- template<typename ResultT>
- ResultT Check(JNIEnv* env, jarray array, jboolean* isCopy, ResultT result) {
- if (force_copy && result != nullptr) {
- result = reinterpret_cast<ResultT>(CreateGuardedPACopy(env, array, isCopy));
- }
- return result;
+ static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
+ return down_cast<jbooleanArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimBoolean));
}
- uint32_t no_copy;
- bool force_copy;
-};
+ static jbyteArray NewByteArray(JNIEnv* env, jsize length) {
+ return down_cast<jbyteArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimByte));
+ }
-#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
- static _ctype* Get##_jname##ArrayElements(JNIEnv* env, _ctype##Array array, jboolean* isCopy) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Eap", env, array, isCopy); \
- _ctype* result = ForceCopyGetChecker(sc, isCopy).Check(env, array, isCopy, baseEnv(env)->Get##_jname##ArrayElements(env, array, isCopy)); \
- return CHECK_JNI_EXIT("p", result); \
- }
-
-#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
- static void Release##_jname##ArrayElements(JNIEnv* env, _ctype##Array array, _ctype* elems, jint mode) { \
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Eapr", env, array, elems, mode); \
- sc.CheckNonNull(elems); \
- if (sc.ForceCopy()) { \
- ReleaseGuardedPACopy(env, array, elems, mode); \
- } \
- baseEnv(env)->Release##_jname##ArrayElements(env, array, elems, mode); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
- static void Get##_jname##ArrayRegion(JNIEnv* env, _ctype##Array array, jsize start, jsize len, _ctype* buf) { \
- CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
- baseEnv(env)->Get##_jname##ArrayRegion(env, array, start, len, buf); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
- static void Set##_jname##ArrayRegion(JNIEnv* env, _ctype##Array array, jsize start, jsize len, const _ctype* buf) { \
- CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
- baseEnv(env)->Set##_jname##ArrayRegion(env, array, start, len, buf); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname, _typechar) \
- GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
- RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
- GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
- SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
-
-// TODO: verify primitive array type matches call type.
-PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, 'Z');
-PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, 'B');
-PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, 'C');
-PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, 'S');
-PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, 'I');
-PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, 'J');
-PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, 'F');
-PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D');
+ static jcharArray NewCharArray(JNIEnv* env, jsize length) {
+ return down_cast<jcharArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimChar));
+ }
- static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
- CHECK_JNI_ENTRY(kFlag_Default, "EcpI", env, c, methods, nMethods);
- return CHECK_JNI_EXIT("I", baseEnv(env)->RegisterNatives(env, c, methods, nMethods));
+ static jshortArray NewShortArray(JNIEnv* env, jsize length) {
+ return down_cast<jshortArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimShort));
}
- static jint UnregisterNatives(JNIEnv* env, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
- return CHECK_JNI_EXIT("I", baseEnv(env)->UnregisterNatives(env, c));
+ static jintArray NewIntArray(JNIEnv* env, jsize length) {
+ return down_cast<jintArray>(NewPrimitiveArray(__FUNCTION__, env, length, Primitive::kPrimInt));
}
+ static jlongArray NewLongArray(JNIEnv* env, jsize length) {
+ return down_cast<jlongArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimLong));
+ }
+
+ static jfloatArray NewFloatArray(JNIEnv* env, jsize length) {
+ return down_cast<jfloatArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimFloat));
+ }
+
+ static jdoubleArray NewDoubleArray(JNIEnv* env, jsize length) {
+ return down_cast<jdoubleArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimDouble));
+ }
+
+#define PRIMITIVE_ARRAY_FUNCTIONS(ctype, name, ptype) \
+ static ctype* Get##name##ArrayElements(JNIEnv* env, ctype##Array array, jboolean* is_copy) { \
+ return reinterpret_cast<ctype*>( \
+ GetPrimitiveArrayElements(__FUNCTION__, ptype, env, array, is_copy)); \
+ } \
+ \
+ static void Release##name##ArrayElements(JNIEnv* env, ctype##Array array, ctype* elems, \
+ jint mode) { \
+ ReleasePrimitiveArrayElements(__FUNCTION__, ptype, env, array, elems, mode); \
+ } \
+ \
+ static void Get##name##ArrayRegion(JNIEnv* env, ctype##Array array, jsize start, jsize len, \
+ ctype* buf) { \
+ GetPrimitiveArrayRegion(__FUNCTION__, ptype, env, array, start, len, buf); \
+ } \
+ \
+ static void Set##name##ArrayRegion(JNIEnv* env, ctype##Array array, jsize start, jsize len, \
+ const ctype* buf) { \
+ SetPrimitiveArrayRegion(__FUNCTION__, ptype, env, array, start, len, buf); \
+ }
+
+ PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, Primitive::kPrimBoolean)
+ PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, Primitive::kPrimByte)
+ PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, Primitive::kPrimChar)
+ PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, Primitive::kPrimShort)
+ PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, Primitive::kPrimInt)
+ PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, Primitive::kPrimLong)
+ PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, Primitive::kPrimFloat)
+ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, Primitive::kPrimDouble)
+#undef PRIMITIVE_ARRAY_FUNCTIONS
+
static jint MonitorEnter(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- if (!sc.CheckInstance(ScopedCheck::kObject, obj)) {
- return JNI_ERR; // Only for jni_internal_test. Real code will have aborted already.
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->MonitorEnter(env, obj);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
}
- return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorEnter(env, obj));
+ return JNI_ERR;
}
static jint MonitorExit(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
- if (!sc.CheckInstance(ScopedCheck::kObject, obj)) {
- return JNI_ERR; // Only for jni_internal_test. Real code will have aborted already.
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->MonitorExit(env, obj);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
}
- return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorExit(env, obj));
+ return JNI_ERR;
}
- static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, vm);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetJavaVM(env, vm));
+ static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* is_copy) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritGet, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
+ if (sc.Check(soa, true, "Eap", args)) {
+ JniValueType result;
+ result.p = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
+ if (result.p != nullptr && soa.ForceCopy()) {
+ result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+ }
+ if (sc.Check(soa, false, "p", &result)) {
+ return const_cast<void*>(result.p);
+ }
+ }
+ return nullptr;
}
- static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
- baseEnv(env)->GetStringRegion(env, str, start, len, buf);
- CHECK_JNI_EXIT_VOID();
+ static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritRelease | kFlag_ExcepOkay, __FUNCTION__);
+ sc.CheckNonNull(carray);
+ JniValueType args[4] = {{.E = env}, {.a = array}, {.p = carray}, {.r = mode}};
+ if (sc.Check(soa, true, "Eapr", args)) {
+ if (soa.ForceCopy()) {
+ GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
+ }
+ baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
- static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
- baseEnv(env)->GetStringUTFRegion(env, str, start, len, buf);
- CHECK_JNI_EXIT_VOID();
+ static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.p = address}, {.J = capacity}};
+ if (sc.Check(soa, true, "EpJ", args)) {
+ JniValueType result;
+ // Note: the validity of address and capacity are checked in the base implementation.
+ result.L = baseEnv(env)->NewDirectByteBuffer(env, address, capacity);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
}
- static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritGet, "Eap", env, array, isCopy);
- void* result = baseEnv(env)->GetPrimitiveArrayCritical(env, array, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- result = CreateGuardedPACopy(env, array, isCopy);
+ static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = buf}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ // Note: this is implemented in the base environment by a GetLongField which will sanity
+ // check the type of buf in GetLongField above.
+ result.p = baseEnv(env)->GetDirectBufferAddress(env, buf);
+ if (sc.Check(soa, false, "p", &result)) {
+ return const_cast<void*>(result.p);
+ }
}
- return CHECK_JNI_EXIT("p", result);
+ return nullptr;
}
- static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
- CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Eapr", env, array, carray, mode);
- sc.CheckNonNull(carray);
- if (sc.ForceCopy()) {
- ReleaseGuardedPACopy(env, array, carray, mode);
+ static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = buf}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ // Note: this is implemented in the base environment by a GetIntField which will sanity
+ // check the type of buf in GetIntField above.
+ result.J = baseEnv(env)->GetDirectBufferCapacity(env, buf);
+ if (sc.Check(soa, false, "J", &result)) {
+ return result.J;
+ }
}
- baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
- CHECK_JNI_EXIT_VOID();
+ return JNI_ERR;
}
- static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritGet, "Esp", env, java_string, isCopy);
- const jchar* result = baseEnv(env)->GetStringCritical(env, java_string, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- mirror::String* s = sc.soa().Decode<mirror::String*>(java_string);
- int byteCount = s->GetLength() * 2;
- result = (const jchar*) GuardedCopy::Create(result, byteCount, false);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
+ private:
+ static JavaVMExt* GetJavaVMExt(JNIEnv* env) {
+ return reinterpret_cast<JNIEnvExt*>(env)->vm;
+ }
+
+ static const JNINativeInterface* baseEnv(JNIEnv* env) {
+ return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions;
+ }
+
+ static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ switch (kind) {
+ case kGlobal:
+ result.L = baseEnv(env)->NewGlobalRef(env, obj);
+ break;
+ case kLocal:
+ result.L = baseEnv(env)->NewLocalRef(env, obj);
+ break;
+ case kWeakGlobal:
+ result.L = baseEnv(env)->NewWeakGlobalRef(env, obj);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected reference kind: " << kind;
+ }
+ if (sc.Check(soa, false, "L", &result)) {
+ DCHECK_EQ(IsSameObject(env, obj, result.L), JNI_TRUE);
+ DCHECK(sc.CheckReferenceKind(kind, soa.Vm(), soa.Self(), result.L));
+ return result.L;
}
}
- return CHECK_JNI_EXIT("p", result);
+ return nullptr;
+ }
+
+ static void DeleteRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, function_name);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ sc.Check(soa, true, "EL", args);
+ if (sc.CheckReferenceKind(kind, soa.Vm(), soa.Self(), obj)) {
+ JniValueType result;
+ switch (kind) {
+ case kGlobal:
+ baseEnv(env)->DeleteGlobalRef(env, obj);
+ break;
+ case kLocal:
+ baseEnv(env)->DeleteLocalRef(env, obj);
+ break;
+ case kWeakGlobal:
+ baseEnv(env)->DeleteWeakGlobalRef(env, obj);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected reference kind: " << kind;
+ }
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
- static void ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* carray) {
- CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Esp", env, string, carray);
- sc.CheckNonNull(carray);
- if (sc.ForceCopy()) {
- GuardedCopy::Check(__FUNCTION__, carray, false);
- carray = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<jchar*>(carray)));
+ static jmethodID GetMethodIDInternal(const char* function_name, JNIEnv* env, jclass c,
+ const char* name, const char* sig, bool is_static) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
+ if (sc.Check(soa, true, "Ecuu", args)) {
+ JniValueType result;
+ if (is_static) {
+ result.m = baseEnv(env)->GetStaticMethodID(env, c, name, sig);
+ } else {
+ result.m = baseEnv(env)->GetMethodID(env, c, name, sig);
+ }
+ if (sc.Check(soa, false, "m", &result)) {
+ return result.m;
+ }
}
- baseEnv(env)->ReleaseStringCritical(env, string, carray);
- CHECK_JNI_EXIT_VOID();
+ return nullptr;
}
- static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewWeakGlobalRef(env, obj));
+ static jfieldID GetFieldIDInternal(const char* function_name, JNIEnv* env, jclass c,
+ const char* name, const char* sig, bool is_static) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
+ if (sc.Check(soa, true, "Ecuu", args)) {
+ JniValueType result;
+ if (is_static) {
+ result.f = baseEnv(env)->GetStaticFieldID(env, c, name, sig);
+ } else {
+ result.f = baseEnv(env)->GetFieldID(env, c, name, sig);
+ }
+ if (sc.Check(soa, false, "f", &result)) {
+ return result.f;
+ }
+ }
+ return nullptr;
+ }
+
+ static JniValueType GetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
+ bool is_static, Primitive::Type type) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[3] = {{.E = env}, {.L = obj}, {.f = fid}};
+ JniValueType result;
+ if (sc.Check(soa, true, is_static ? "Ecf" : "ELf", args) &&
+ sc.CheckFieldAccess(soa, obj, fid, is_static, type)) {
+ const char* result_check = nullptr;
+ switch (type) {
+ case Primitive::kPrimNot:
+ if (is_static) {
+ result.L = baseEnv(env)->GetStaticObjectField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.L = baseEnv(env)->GetObjectField(env, obj, fid);
+ }
+ result_check = "L";
+ break;
+ case Primitive::kPrimBoolean:
+ if (is_static) {
+ result.Z = baseEnv(env)->GetStaticBooleanField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.Z = baseEnv(env)->GetBooleanField(env, obj, fid);
+ }
+ result_check = "Z";
+ break;
+ case Primitive::kPrimByte:
+ if (is_static) {
+ result.B = baseEnv(env)->GetStaticByteField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.B = baseEnv(env)->GetByteField(env, obj, fid);
+ }
+ result_check = "B";
+ break;
+ case Primitive::kPrimChar:
+ if (is_static) {
+ result.C = baseEnv(env)->GetStaticCharField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.C = baseEnv(env)->GetCharField(env, obj, fid);
+ }
+ result_check = "C";
+ break;
+ case Primitive::kPrimShort:
+ if (is_static) {
+ result.S = baseEnv(env)->GetStaticShortField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.S = baseEnv(env)->GetShortField(env, obj, fid);
+ }
+ result_check = "S";
+ break;
+ case Primitive::kPrimInt:
+ if (is_static) {
+ result.I = baseEnv(env)->GetStaticIntField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.I = baseEnv(env)->GetIntField(env, obj, fid);
+ }
+ result_check = "I";
+ break;
+ case Primitive::kPrimLong:
+ if (is_static) {
+ result.J = baseEnv(env)->GetStaticLongField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.J = baseEnv(env)->GetLongField(env, obj, fid);
+ }
+ result_check = "J";
+ break;
+ case Primitive::kPrimFloat:
+ if (is_static) {
+ result.F = baseEnv(env)->GetStaticFloatField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.F = baseEnv(env)->GetFloatField(env, obj, fid);
+ }
+ result_check = "F";
+ break;
+ case Primitive::kPrimDouble:
+ if (is_static) {
+ result.D = baseEnv(env)->GetStaticDoubleField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.D = baseEnv(env)->GetDoubleField(env, obj, fid);
+ }
+ result_check = "D";
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type: " << type;
+ break;
+ }
+ if (sc.Check(soa, false, result_check, &result)) {
+ return result;
+ }
+ }
+ result.J = 0;
+ return result;
}
- static jboolean ExceptionCheck(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_CritOkay | kFlag_ExcepOkay, "E", env);
- return CHECK_JNI_EXIT("b", baseEnv(env)->ExceptionCheck(env));
+ static void SetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
+ bool is_static, Primitive::Type type, JniValueType value) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[4] = {{.E = env}, {.L = obj}, {.f = fid}, value};
+ char sig[5] = { 'E', is_static ? 'c' : 'L', 'f',
+ type == Primitive::kPrimNot ? 'L' : Primitive::Descriptor(type)[0], '\0'};
+ if (sc.Check(soa, true, sig, args) &&
+ sc.CheckFieldAccess(soa, obj, fid, is_static, type)) {
+ switch (type) {
+ case Primitive::kPrimNot:
+ if (is_static) {
+ baseEnv(env)->SetStaticObjectField(env, down_cast<jclass>(obj), fid, value.L);
+ } else {
+ baseEnv(env)->SetObjectField(env, obj, fid, value.L);
+ }
+ break;
+ case Primitive::kPrimBoolean:
+ if (is_static) {
+ baseEnv(env)->SetStaticBooleanField(env, down_cast<jclass>(obj), fid, value.Z);
+ } else {
+ baseEnv(env)->SetBooleanField(env, obj, fid, value.Z);
+ }
+ break;
+ case Primitive::kPrimByte:
+ if (is_static) {
+ baseEnv(env)->SetStaticByteField(env, down_cast<jclass>(obj), fid, value.B);
+ } else {
+ baseEnv(env)->SetByteField(env, obj, fid, value.B);
+ }
+ break;
+ case Primitive::kPrimChar:
+ if (is_static) {
+ baseEnv(env)->SetStaticCharField(env, down_cast<jclass>(obj), fid, value.C);
+ } else {
+ baseEnv(env)->SetCharField(env, obj, fid, value.C);
+ }
+ break;
+ case Primitive::kPrimShort:
+ if (is_static) {
+ baseEnv(env)->SetStaticShortField(env, down_cast<jclass>(obj), fid, value.S);
+ } else {
+ baseEnv(env)->SetShortField(env, obj, fid, value.S);
+ }
+ break;
+ case Primitive::kPrimInt:
+ if (is_static) {
+ baseEnv(env)->SetStaticIntField(env, down_cast<jclass>(obj), fid, value.I);
+ } else {
+ baseEnv(env)->SetIntField(env, obj, fid, value.I);
+ }
+ break;
+ case Primitive::kPrimLong:
+ if (is_static) {
+ baseEnv(env)->SetStaticLongField(env, down_cast<jclass>(obj), fid, value.J);
+ } else {
+ baseEnv(env)->SetLongField(env, obj, fid, value.J);
+ }
+ break;
+ case Primitive::kPrimFloat:
+ if (is_static) {
+ baseEnv(env)->SetStaticFloatField(env, down_cast<jclass>(obj), fid, value.F);
+ } else {
+ baseEnv(env)->SetFloatField(env, obj, fid, value.F);
+ }
+ break;
+ case Primitive::kPrimDouble:
+ if (is_static) {
+ baseEnv(env)->SetStaticDoubleField(env, down_cast<jclass>(obj), fid, value.D);
+ } else {
+ baseEnv(env)->SetDoubleField(env, obj, fid, value.D);
+ }
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type: " << type;
+ break;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
- static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
- // Note: we use "Ep" rather than "EL" because this is the one JNI function
- // that it's okay to pass an invalid reference to.
- CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, obj);
- // TODO: proper decoding of jobjectRefType!
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetObjectRefType(env, obj));
+ static bool CheckCallArgs(ScopedObjectAccess& soa, ScopedCheck& sc, JNIEnv* env, jobject obj,
+ jclass c, jmethodID mid, InvokeType invoke)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool checked;
+ switch (invoke) {
+ case kVirtual: {
+ DCHECK(c == nullptr);
+ JniValueType args[3] = {{.E = env}, {.L = obj}, {.m = mid}};
+ checked = sc.Check(soa, true, "ELm.", args);
+ break;
+ }
+ case kDirect: {
+ JniValueType args[4] = {{.E = env}, {.L = obj}, {.c = c}, {.m = mid}};
+ checked = sc.Check(soa, true, "ELcm.", args);
+ break;
+ }
+ case kStatic: {
+ DCHECK(obj == nullptr);
+ JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+ checked = sc.Check(soa, true, "Ecm.", args);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ checked = false;
+ break;
+ }
+ return checked;
+ }
+
+ static JniValueType CallMethodA(const char* function_name, JNIEnv* env, jobject obj, jclass c,
+ jmethodID mid, jvalue* vargs, Primitive::Type type,
+ InvokeType invoke) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType result;
+ if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) &&
+ sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) {
+ const char* result_check;
+ switch (type) {
+ case Primitive::kPrimNot:
+ result_check = "L";
+ switch (invoke) {
+ case kVirtual:
+ result.L = baseEnv(env)->CallObjectMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.L = baseEnv(env)->CallNonvirtualObjectMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.L = baseEnv(env)->CallStaticObjectMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimBoolean:
+ result_check = "Z";
+ switch (invoke) {
+ case kVirtual:
+ result.Z = baseEnv(env)->CallBooleanMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.Z = baseEnv(env)->CallNonvirtualBooleanMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.Z = baseEnv(env)->CallStaticBooleanMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimByte:
+ result_check = "B";
+ switch (invoke) {
+ case kVirtual:
+ result.B = baseEnv(env)->CallByteMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.B = baseEnv(env)->CallNonvirtualByteMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.B = baseEnv(env)->CallStaticByteMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimChar:
+ result_check = "C";
+ switch (invoke) {
+ case kVirtual:
+ result.C = baseEnv(env)->CallCharMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.C = baseEnv(env)->CallNonvirtualCharMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.C = baseEnv(env)->CallStaticCharMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimShort:
+ result_check = "S";
+ switch (invoke) {
+ case kVirtual:
+ result.S = baseEnv(env)->CallShortMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.S = baseEnv(env)->CallNonvirtualShortMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.S = baseEnv(env)->CallStaticShortMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimInt:
+ result_check = "I";
+ switch (invoke) {
+ case kVirtual:
+ result.I = baseEnv(env)->CallIntMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.I = baseEnv(env)->CallNonvirtualIntMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.I = baseEnv(env)->CallStaticIntMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimLong:
+ result_check = "J";
+ switch (invoke) {
+ case kVirtual:
+ result.J = baseEnv(env)->CallLongMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.J = baseEnv(env)->CallNonvirtualLongMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.J = baseEnv(env)->CallStaticLongMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimFloat:
+ result_check = "F";
+ switch (invoke) {
+ case kVirtual:
+ result.F = baseEnv(env)->CallFloatMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.F = baseEnv(env)->CallNonvirtualFloatMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.F = baseEnv(env)->CallStaticFloatMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimDouble:
+ result_check = "D";
+ switch (invoke) {
+ case kVirtual:
+ result.D = baseEnv(env)->CallDoubleMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.D = baseEnv(env)->CallNonvirtualDoubleMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.D = baseEnv(env)->CallStaticDoubleMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimVoid:
+ result_check = "V";
+ result.V = nullptr;
+ switch (invoke) {
+ case kVirtual:
+ baseEnv(env)->CallVoidMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ baseEnv(env)->CallNonvirtualVoidMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ baseEnv(env)->CallStaticVoidMethodA(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected return type: " << type;
+ result_check = nullptr;
+ }
+ if (sc.Check(soa, false, result_check, &result)) {
+ return result;
+ }
+ }
+ result.J = 0;
+ return result;
}
- static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
- CHECK_JNI_ENTRY(kFlag_Default, "EpJ", env, address, capacity);
- if (address == nullptr) {
- JniAbortF(__FUNCTION__, "non-nullable address is NULL");
- return nullptr;
+ static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c,
+ jmethodID mid, va_list vargs, Primitive::Type type,
+ InvokeType invoke) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType result;
+ if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) &&
+ sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) {
+ const char* result_check;
+ switch (type) {
+ case Primitive::kPrimNot:
+ result_check = "L";
+ switch (invoke) {
+ case kVirtual:
+ result.L = baseEnv(env)->CallObjectMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.L = baseEnv(env)->CallNonvirtualObjectMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.L = baseEnv(env)->CallStaticObjectMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimBoolean:
+ result_check = "Z";
+ switch (invoke) {
+ case kVirtual:
+ result.Z = baseEnv(env)->CallBooleanMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.Z = baseEnv(env)->CallNonvirtualBooleanMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.Z = baseEnv(env)->CallStaticBooleanMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimByte:
+ result_check = "B";
+ switch (invoke) {
+ case kVirtual:
+ result.B = baseEnv(env)->CallByteMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.B = baseEnv(env)->CallNonvirtualByteMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.B = baseEnv(env)->CallStaticByteMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimChar:
+ result_check = "C";
+ switch (invoke) {
+ case kVirtual:
+ result.C = baseEnv(env)->CallCharMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.C = baseEnv(env)->CallNonvirtualCharMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.C = baseEnv(env)->CallStaticCharMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimShort:
+ result_check = "S";
+ switch (invoke) {
+ case kVirtual:
+ result.S = baseEnv(env)->CallShortMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.S = baseEnv(env)->CallNonvirtualShortMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.S = baseEnv(env)->CallStaticShortMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimInt:
+ result_check = "I";
+ switch (invoke) {
+ case kVirtual:
+ result.I = baseEnv(env)->CallIntMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.I = baseEnv(env)->CallNonvirtualIntMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.I = baseEnv(env)->CallStaticIntMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimLong:
+ result_check = "J";
+ switch (invoke) {
+ case kVirtual:
+ result.J = baseEnv(env)->CallLongMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.J = baseEnv(env)->CallNonvirtualLongMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.J = baseEnv(env)->CallStaticLongMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimFloat:
+ result_check = "F";
+ switch (invoke) {
+ case kVirtual:
+ result.F = baseEnv(env)->CallFloatMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.F = baseEnv(env)->CallNonvirtualFloatMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.F = baseEnv(env)->CallStaticFloatMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimDouble:
+ result_check = "D";
+ switch (invoke) {
+ case kVirtual:
+ result.D = baseEnv(env)->CallDoubleMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.D = baseEnv(env)->CallNonvirtualDoubleMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.D = baseEnv(env)->CallStaticDoubleMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimVoid:
+ result_check = "V";
+ result.V = nullptr;
+ switch (invoke) {
+ case kVirtual:
+ baseEnv(env)->CallVoidMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ baseEnv(env)->CallNonvirtualVoidMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ baseEnv(env)->CallStaticVoidMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected return type: " << type;
+ result_check = nullptr;
+ }
+ if (sc.Check(soa, false, result_check, &result)) {
+ return result;
+ }
}
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity));
+ result.J = 0;
+ return result;
}
- static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
- // TODO: check that 'buf' is a java.nio.Buffer.
- return CHECK_JNI_EXIT("p", baseEnv(env)->GetDirectBufferAddress(env, buf));
+ static const void* GetStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
+ jboolean* is_copy, bool utf, bool critical) {
+ ScopedObjectAccess soa(env);
+ int flags = critical ? kFlag_CritGet : kFlag_CritOkay;
+ ScopedCheck sc(flags, function_name);
+ JniValueType args[3] = {{.E = env}, {.s = string}, {.p = is_copy}};
+ if (sc.Check(soa, true, "Esp", args)) {
+ JniValueType result;
+ if (utf) {
+ CHECK(!critical);
+ result.u = baseEnv(env)->GetStringUTFChars(env, string, is_copy);
+ } else {
+ if (critical) {
+ result.p = baseEnv(env)->GetStringCritical(env, string, is_copy);
+ } else {
+ result.p = baseEnv(env)->GetStringChars(env, string, is_copy);
+ }
+ }
+ // TODO: could we be smarter about not copying when local_is_copy?
+ if (result.p != nullptr && soa.ForceCopy()) {
+ if (utf) {
+ size_t length_in_bytes = strlen(result.u) + 1;
+ result.u =
+ reinterpret_cast<const char*>(GuardedCopy::Create(result.u, length_in_bytes, false));
+ } else {
+ size_t length_in_bytes = baseEnv(env)->GetStringLength(env, string) * 2;
+ result.p =
+ reinterpret_cast<const jchar*>(GuardedCopy::Create(result.p, length_in_bytes, false));
+ }
+ if (is_copy != nullptr) {
+ *is_copy = JNI_TRUE;
+ }
+ }
+ if (sc.Check(soa, false, utf ? "u" : "p", &result)) {
+ return utf ? result.u : result.p;
+ }
+ }
+ return nullptr;
}
- static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
- // TODO: check that 'buf' is a java.nio.Buffer.
- return CHECK_JNI_EXIT("J", baseEnv(env)->GetDirectBufferCapacity(env, buf));
+ static void ReleaseStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
+ const void* chars, bool utf, bool critical) {
+ ScopedObjectAccess soa(env);
+ int flags = kFlag_ExcepOkay | kFlag_Release;
+ if (critical) {
+ flags |= kFlag_CritRelease;
+ }
+ ScopedCheck sc(flags, function_name);
+ sc.CheckNonNull(chars);
+ bool force_copy_ok = !soa.ForceCopy() || GuardedCopy::Check(function_name, chars, false);
+ if (force_copy_ok && soa.ForceCopy()) {
+ chars = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<void*>(chars)));
+ }
+ if (force_copy_ok) {
+ JniValueType args[3] = {{.E = env}, {.s = string}, {.p = chars}};
+ if (sc.Check(soa, true, utf ? "Esu" : "Esp", args)) {
+ if (utf) {
+ CHECK(!critical);
+ baseEnv(env)->ReleaseStringUTFChars(env, string, reinterpret_cast<const char*>(chars));
+ } else {
+ if (critical) {
+ baseEnv(env)->ReleaseStringCritical(env, string, reinterpret_cast<const jchar*>(chars));
+ } else {
+ baseEnv(env)->ReleaseStringChars(env, string, reinterpret_cast<const jchar*>(chars));
+ }
+ }
+ JniValueType result;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
}
- private:
- static inline const JNINativeInterface* baseEnv(JNIEnv* env) {
- return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions;
+ static jarray NewPrimitiveArray(const char* function_name, JNIEnv* env, jsize length,
+ Primitive::Type type) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.z = length}};
+ if (sc.Check(soa, true, "Ez", args)) {
+ JniValueType result;
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ result.a = baseEnv(env)->NewBooleanArray(env, length);
+ break;
+ case Primitive::kPrimByte:
+ result.a = baseEnv(env)->NewByteArray(env, length);
+ break;
+ case Primitive::kPrimChar:
+ result.a = baseEnv(env)->NewCharArray(env, length);
+ break;
+ case Primitive::kPrimShort:
+ result.a = baseEnv(env)->NewShortArray(env, length);
+ break;
+ case Primitive::kPrimInt:
+ result.a = baseEnv(env)->NewIntArray(env, length);
+ break;
+ case Primitive::kPrimLong:
+ result.a = baseEnv(env)->NewLongArray(env, length);
+ break;
+ case Primitive::kPrimFloat:
+ result.a = baseEnv(env)->NewFloatArray(env, length);
+ break;
+ case Primitive::kPrimDouble:
+ result.a = baseEnv(env)->NewDoubleArray(env, length);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ if (sc.Check(soa, false, "a", &result)) {
+ return result.a;
+ }
+ }
+ return nullptr;
+ }
+
+ static void* GetPrimitiveArrayElements(const char* function_name, Primitive::Type type,
+ JNIEnv* env, jarray array, jboolean* is_copy) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
+ if (sc.Check(soa, true, "Eap", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ JniValueType result;
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ result.p = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimByte:
+ result.p = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimChar:
+ result.p = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimShort:
+ result.p = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimInt:
+ result.p = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
+ break;
+ case Primitive::kPrimLong:
+ result.p = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimFloat:
+ result.p = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimDouble:
+ result.p = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array),
+ is_copy);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ if (result.p != nullptr && soa.ForceCopy()) {
+ result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+ if (is_copy != nullptr) {
+ *is_copy = JNI_TRUE;
+ }
+ }
+ if (sc.Check(soa, false, "p", &result)) {
+ return const_cast<void*>(result.p);
+ }
+ }
+ return nullptr;
+ }
+
+ static void ReleasePrimitiveArrayElements(const char* function_name, Primitive::Type type,
+ JNIEnv* env, jarray array, void* elems, jint mode) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, function_name);
+ if (sc.CheckNonNull(elems) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ if (soa.ForceCopy()) {
+ elems = GuardedCopy::ReleaseGuardedPACopy(function_name, env, array, elems, mode);
+ }
+ if (!soa.ForceCopy() || elems != nullptr) {
+ JniValueType args[4] = {{.E = env}, {.a = array}, {.p = elems}, {.r = mode}};
+ if (sc.Check(soa, true, "Eapr", args)) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ baseEnv(env)->ReleaseBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+ reinterpret_cast<jboolean*>(elems), mode);
+ break;
+ case Primitive::kPrimByte:
+ baseEnv(env)->ReleaseByteArrayElements(env, down_cast<jbyteArray>(array),
+ reinterpret_cast<jbyte*>(elems), mode);
+ break;
+ case Primitive::kPrimChar:
+ baseEnv(env)->ReleaseCharArrayElements(env, down_cast<jcharArray>(array),
+ reinterpret_cast<jchar*>(elems), mode);
+ break;
+ case Primitive::kPrimShort:
+ baseEnv(env)->ReleaseShortArrayElements(env, down_cast<jshortArray>(array),
+ reinterpret_cast<jshort*>(elems), mode);
+ break;
+ case Primitive::kPrimInt:
+ baseEnv(env)->ReleaseIntArrayElements(env, down_cast<jintArray>(array),
+ reinterpret_cast<jint*>(elems), mode);
+ break;
+ case Primitive::kPrimLong:
+ baseEnv(env)->ReleaseLongArrayElements(env, down_cast<jlongArray>(array),
+ reinterpret_cast<jlong*>(elems), mode);
+ break;
+ case Primitive::kPrimFloat:
+ baseEnv(env)->ReleaseFloatArrayElements(env, down_cast<jfloatArray>(array),
+ reinterpret_cast<jfloat*>(elems), mode);
+ break;
+ case Primitive::kPrimDouble:
+ baseEnv(env)->ReleaseDoubleArrayElements(env, down_cast<jdoubleArray>(array),
+ reinterpret_cast<jdouble*>(elems), mode);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+ }
+ }
+
+ static void GetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
+ jarray array, jsize start, jsize len, void* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EaIIp", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ baseEnv(env)->GetBooleanArrayRegion(env, down_cast<jbooleanArray>(array), start, len,
+ reinterpret_cast<jboolean*>(buf));
+ break;
+ case Primitive::kPrimByte:
+ baseEnv(env)->GetByteArrayRegion(env, down_cast<jbyteArray>(array), start, len,
+ reinterpret_cast<jbyte*>(buf));
+ break;
+ case Primitive::kPrimChar:
+ baseEnv(env)->GetCharArrayRegion(env, down_cast<jcharArray>(array), start, len,
+ reinterpret_cast<jchar*>(buf));
+ break;
+ case Primitive::kPrimShort:
+ baseEnv(env)->GetShortArrayRegion(env, down_cast<jshortArray>(array), start, len,
+ reinterpret_cast<jshort*>(buf));
+ break;
+ case Primitive::kPrimInt:
+ baseEnv(env)->GetIntArrayRegion(env, down_cast<jintArray>(array), start, len,
+ reinterpret_cast<jint*>(buf));
+ break;
+ case Primitive::kPrimLong:
+ baseEnv(env)->GetLongArrayRegion(env, down_cast<jlongArray>(array), start, len,
+ reinterpret_cast<jlong*>(buf));
+ break;
+ case Primitive::kPrimFloat:
+ baseEnv(env)->GetFloatArrayRegion(env, down_cast<jfloatArray>(array), start, len,
+ reinterpret_cast<jfloat*>(buf));
+ break;
+ case Primitive::kPrimDouble:
+ baseEnv(env)->GetDoubleArrayRegion(env, down_cast<jdoubleArray>(array), start, len,
+ reinterpret_cast<jdouble*>(buf));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static void SetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
+ jarray array, jsize start, jsize len, const void* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EaIIp", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ baseEnv(env)->SetBooleanArrayRegion(env, down_cast<jbooleanArray>(array), start, len,
+ reinterpret_cast<const jboolean*>(buf));
+ break;
+ case Primitive::kPrimByte:
+ baseEnv(env)->SetByteArrayRegion(env, down_cast<jbyteArray>(array), start, len,
+ reinterpret_cast<const jbyte*>(buf));
+ break;
+ case Primitive::kPrimChar:
+ baseEnv(env)->SetCharArrayRegion(env, down_cast<jcharArray>(array), start, len,
+ reinterpret_cast<const jchar*>(buf));
+ break;
+ case Primitive::kPrimShort:
+ baseEnv(env)->SetShortArrayRegion(env, down_cast<jshortArray>(array), start, len,
+ reinterpret_cast<const jshort*>(buf));
+ break;
+ case Primitive::kPrimInt:
+ baseEnv(env)->SetIntArrayRegion(env, down_cast<jintArray>(array), start, len,
+ reinterpret_cast<const jint*>(buf));
+ break;
+ case Primitive::kPrimLong:
+ baseEnv(env)->SetLongArrayRegion(env, down_cast<jlongArray>(array), start, len,
+ reinterpret_cast<const jlong*>(buf));
+ break;
+ case Primitive::kPrimFloat:
+ baseEnv(env)->SetFloatArrayRegion(env, down_cast<jfloatArray>(array), start, len,
+ reinterpret_cast<const jfloat*>(buf));
+ break;
+ case Primitive::kPrimDouble:
+ baseEnv(env)->SetDoubleArrayRegion(env, down_cast<jdoubleArray>(array), start, len,
+ reinterpret_cast<const jdouble*>(buf));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
};
@@ -2023,38 +3584,58 @@ const JNINativeInterface* GetCheckJniNativeInterface() {
class CheckJII {
public:
static jint DestroyJavaVM(JavaVM* vm) {
- ScopedCheck sc(vm, false, __FUNCTION__);
- sc.Check(true, "v", vm);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->DestroyJavaVM(vm));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__, false);
+ JniValueType args[1] = {{.v = vm}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "v", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->DestroyJavaVM(vm);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- ScopedCheck sc(vm, false, __FUNCTION__);
- sc.Check(true, "vpp", vm, p_env, thr_args);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- ScopedCheck sc(vm, false, __FUNCTION__);
- sc.Check(true, "vpp", vm, p_env, thr_args);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
static jint DetachCurrentThread(JavaVM* vm) {
- ScopedCheck sc(vm, true, __FUNCTION__);
- sc.Check(true, "v", vm);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->DetachCurrentThread(vm));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[1] = {{.v = vm}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "v", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->DetachCurrentThread(vm);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
- static jint GetEnv(JavaVM* vm, void** env, jint version) {
- ScopedCheck sc(vm, true, __FUNCTION__);
- sc.Check(true, "vpI", vm);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->GetEnv(vm, env, version));
+ static jint GetEnv(JavaVM* vm, void** p_env, jint version) {
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.I = version}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpI", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->GetEnv(vm, p_env, version);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
private:
- static inline const JNIInvokeInterface* BaseVm(JavaVM* vm) {
- return reinterpret_cast<JavaVMExt*>(vm)->unchecked_functions;
+ static const JNIInvokeInterface* BaseVm(JavaVM* vm) {
+ return reinterpret_cast<JavaVMExt*>(vm)->GetUncheckedFunctions();
}
};
diff --git a/runtime/check_jni.h b/runtime/check_jni.h
new file mode 100644
index 0000000..f41abf8
--- /dev/null
+++ b/runtime/check_jni.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_CHECK_JNI_H_
+#define ART_RUNTIME_CHECK_JNI_H_
+
+#include <jni.h>
+
+namespace art {
+
+const JNINativeInterface* GetCheckJniNativeInterface();
+const JNIInvokeInterface* GetCheckJniInvokeInterface();
+
+} // namespace art
+
+#endif // ART_RUNTIME_CHECK_JNI_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 72572a0..3b4976f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,9 +16,6 @@
#include "class_linker.h"
-#include <fcntl.h>
-#include <sys/file.h>
-#include <sys/stat.h>
#include <deque>
#include <memory>
#include <string>
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6f3b3a3..ab4a2bb 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -354,23 +354,22 @@ jobject CommonRuntimeTest::LoadDex(const char* dex_name) {
for (const DexFile* dex_file : dex_files) {
class_linker_->RegisterDexFile(*dex_file);
}
- ScopedObjectAccessUnchecked soa(Thread::Current());
- ScopedLocalRef<jobject> class_loader_local(soa.Env(),
- soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
- jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
- soa.Self()->SetClassLoaderOverride(soa.Decode<mirror::ClassLoader*>(class_loader_local.get()));
+ Thread* self = Thread::Current();
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedLocalRef<jobject> class_loader_local(env,
+ env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+ jobject class_loader = env->NewGlobalRef(class_loader_local.get());
+ self->SetClassLoaderOverride(class_loader_local.get());
Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files);
return class_loader;
}
CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
- vm_->check_jni_abort_hook = Hook;
- vm_->check_jni_abort_hook_data = &actual_;
+ vm_->SetCheckJniAbortHook(Hook, &actual_);
}
CheckJniAbortCatcher::~CheckJniAbortCatcher() {
- vm_->check_jni_abort_hook = nullptr;
- vm_->check_jni_abort_hook_data = nullptr;
+ vm_->SetCheckJniAbortHook(nullptr, nullptr);
EXPECT_TRUE(actual_.empty()) << actual_;
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 0fefc21..12c1241 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -138,7 +138,7 @@ class CheckJniAbortCatcher {
private:
static void Hook(void* data, const std::string& reason);
- JavaVMExt* vm_;
+ JavaVMExt* const vm_;
std::string actual_;
DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index be3895a..4755b9e 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -220,7 +220,8 @@ void CheckReferenceResult(mirror::Object* o, Thread* self) {
}
mirror::ArtMethod* m = self->GetCurrentMethod(NULL);
if (o == kInvalidIndirectRefObject) {
- JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str());
+ Runtime::Current()->GetJavaVM()->JniAbortF(NULL, "invalid reference returned from %s",
+ PrettyMethod(m).c_str());
}
// Make sure that the result is an instance of the type this method was expected to return.
StackHandleScope<1> hs(self);
@@ -228,8 +229,9 @@ void CheckReferenceResult(mirror::Object* o, Thread* self) {
mirror::Class* return_type = MethodHelper(h_m).GetReturnType();
if (!o->InstanceOf(return_type)) {
- JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(),
- PrettyMethod(h_m.Get()).c_str());
+ Runtime::Current()->GetJavaVM()->JniAbortF(NULL, "attempt to return an instance of %s from %s",
+ PrettyTypeOf(o).c_str(),
+ PrettyMethod(h_m.Get()).c_str());
}
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index b61105f..6d8190e 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2514,6 +2514,17 @@ void Heap::RevokeAllThreadLocalAllocationStacks(Thread* self) {
}
}
+void Heap::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+ if (kIsDebugBuild) {
+ if (rosalloc_space_ != nullptr) {
+ rosalloc_space_->AssertThreadLocalBuffersAreRevoked(thread);
+ }
+ if (bump_pointer_space_ != nullptr) {
+ bump_pointer_space_->AssertThreadLocalBuffersAreRevoked(thread);
+ }
+ }
+}
+
void Heap::AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked() {
if (kIsDebugBuild) {
if (bump_pointer_space_ != nullptr) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index a82392a..1851662 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -455,6 +455,7 @@ class Heap {
void RevokeThreadLocalBuffers(Thread* thread);
void RevokeRosAllocThreadLocalBuffers(Thread* thread);
void RevokeAllThreadLocalBuffers();
+ void AssertThreadLocalBuffersAreRevoked(Thread* thread);
void AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked();
void RosAllocVerification(TimingLogger* timings, const char* name)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 92c6f53..3f39c77 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -338,6 +338,12 @@ void RosAllocSpace::RevokeAllThreadLocalBuffers() {
rosalloc_->RevokeAllThreadLocalRuns();
}
+void RosAllocSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+ if (kIsDebugBuild) {
+ rosalloc_->AssertThreadLocalRunsAreRevoked(thread);
+ }
+}
+
void RosAllocSpace::AssertAllThreadLocalBuffersAreRevoked() {
if (kIsDebugBuild) {
rosalloc_->AssertAllThreadLocalRunsAreRevoked();
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index f505305..f1ce115 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -101,6 +101,7 @@ class RosAllocSpace : public MallocSpace {
void RevokeThreadLocalBuffers(Thread* thread);
void RevokeAllThreadLocalBuffers();
+ void AssertThreadLocalBuffersAreRevoked(Thread* thread);
void AssertAllThreadLocalBuffersAreRevoked();
// Returns the class of a recently freed object.
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 9b2b82e..1ba2291 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -56,7 +56,8 @@ std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
void IndirectReferenceTable::AbortIfNoCheckJNI() {
// If -Xcheck:jni is on, it'll give a more detailed error before aborting.
- if (!Runtime::Current()->GetJavaVM()->check_jni) {
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ if (!vm->IsCheckJniEnabled()) {
// Otherwise, we want to abort rather than hand back a bad reference.
LOG(FATAL) << "JNI ERROR (app bug): see above.";
}
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fb910e2..d25bc42 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -25,16 +25,18 @@
#include "base/logging.h"
#include "base/mutex.h"
#include "gc_root.h"
-#include "mem_map.h"
#include "object_callbacks.h"
#include "offsets.h"
#include "read_barrier_option.h"
namespace art {
+
namespace mirror {
class Object;
} // namespace mirror
+class MemMap;
+
/*
* Maintain a table of indirect references. Used for local/global JNI
* references.
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
new file mode 100644
index 0000000..9eab3fd
--- /dev/null
+++ b/runtime/java_vm_ext.cc
@@ -0,0 +1,829 @@
+/*
+ * 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 "jni_internal.h"
+
+#include <dlfcn.h>
+
+#include "base/mutex.h"
+#include "base/stl_util.h"
+#include "check_jni.h"
+#include "indirect_reference_table-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "native_bridge.h"
+#include "java_vm_ext.h"
+#include "parsed_options.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace art {
+
+static const size_t kPinTableInitial = 16; // Arbitrary.
+static const size_t kPinTableMax = 1024; // Arbitrary sanity check.
+
+static size_t gGlobalsInitial = 512; // Arbitrary.
+static size_t gGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
+
+static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
+static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
+
+static bool IsBadJniVersion(int version) {
+ // We don't support JNI_VERSION_1_1. These are the only other valid versions.
+ return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
+}
+
+class SharedLibrary {
+ public:
+ SharedLibrary(JNIEnv* env, Thread* self, const std::string& path, void* handle,
+ jobject class_loader)
+ : path_(path),
+ handle_(handle),
+ needs_native_bridge_(false),
+ class_loader_(env->NewGlobalRef(class_loader)),
+ jni_on_load_lock_("JNI_OnLoad lock"),
+ jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
+ jni_on_load_thread_id_(self->GetThreadId()),
+ jni_on_load_result_(kPending) {
+ }
+
+ ~SharedLibrary() {
+ Thread* self = Thread::Current();
+ if (self != nullptr) {
+ self->GetJniEnv()->DeleteGlobalRef(class_loader_);
+ }
+ }
+
+ jobject GetClassLoader() const {
+ return class_loader_;
+ }
+
+ const std::string& GetPath() const {
+ return path_;
+ }
+
+ /*
+ * Check the result of an earlier call to JNI_OnLoad on this library.
+ * If the call has not yet finished in another thread, wait for it.
+ */
+ bool CheckOnLoadResult()
+ LOCKS_EXCLUDED(jni_on_load_lock_) {
+ Thread* self = Thread::Current();
+ bool okay;
+ {
+ MutexLock mu(self, jni_on_load_lock_);
+
+ if (jni_on_load_thread_id_ == self->GetThreadId()) {
+ // Check this so we don't end up waiting for ourselves. We need to return "true" so the
+ // caller can continue.
+ LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
+ okay = true;
+ } else {
+ while (jni_on_load_result_ == kPending) {
+ VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
+ jni_on_load_cond_.Wait(self);
+ }
+
+ okay = (jni_on_load_result_ == kOkay);
+ VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
+ << (okay ? "succeeded" : "failed") << "]";
+ }
+ }
+ return okay;
+ }
+
+ void SetResult(bool result) LOCKS_EXCLUDED(jni_on_load_lock_) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, jni_on_load_lock_);
+
+ jni_on_load_result_ = result ? kOkay : kFailed;
+ jni_on_load_thread_id_ = 0;
+
+ // Broadcast a wakeup to anybody sleeping on the condition variable.
+ jni_on_load_cond_.Broadcast(self);
+ }
+
+ void SetNeedsNativeBridge() {
+ needs_native_bridge_ = true;
+ }
+
+ bool NeedsNativeBridge() const {
+ return needs_native_bridge_;
+ }
+
+ void* FindSymbol(const std::string& symbol_name) {
+ return dlsym(handle_, symbol_name.c_str());
+ }
+
+ void* FindSymbolWithNativeBridge(const std::string& symbol_name, const char* shorty) {
+ CHECK(NeedsNativeBridge());
+
+ uint32_t len = 0;
+ return NativeBridgeGetTrampoline(handle_, symbol_name.c_str(), shorty, len);
+ }
+
+ private:
+ enum JNI_OnLoadState {
+ kPending,
+ kFailed,
+ kOkay,
+ };
+
+ // Path to library "/system/lib/libjni.so".
+ const std::string path_;
+
+ // The void* returned by dlopen(3).
+ void* const handle_;
+
+ // True if a native bridge is required.
+ bool needs_native_bridge_;
+
+ // The ClassLoader this library is associated with, a global JNI reference that is
+ // created/deleted with the scope of the library.
+ const jobject class_loader_;
+
+ // Guards remaining items.
+ Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Wait for JNI_OnLoad in other thread.
+ ConditionVariable jni_on_load_cond_ GUARDED_BY(jni_on_load_lock_);
+ // Recursive invocation guard.
+ uint32_t jni_on_load_thread_id_ GUARDED_BY(jni_on_load_lock_);
+ // Result of earlier JNI_OnLoad call.
+ JNI_OnLoadState jni_on_load_result_ GUARDED_BY(jni_on_load_lock_);
+};
+
+// This exists mainly to keep implementation details out of the header file.
+class Libraries {
+ public:
+ Libraries() {
+ }
+
+ ~Libraries() {
+ STLDeleteValues(&libraries_);
+ }
+
+ void Dump(std::ostream& os) const {
+ bool first = true;
+ for (const auto& library : libraries_) {
+ if (!first) {
+ os << ' ';
+ }
+ first = false;
+ os << library.first;
+ }
+ }
+
+ size_t size() const {
+ return libraries_.size();
+ }
+
+ SharedLibrary* Get(const std::string& path) {
+ auto it = libraries_.find(path);
+ return (it == libraries_.end()) ? nullptr : it->second;
+ }
+
+ void Put(const std::string& path, SharedLibrary* library) {
+ libraries_.Put(path, library);
+ }
+
+ // See section 11.3 "Linking Native Methods" of the JNI spec.
+ void* FindNativeMethod(mirror::ArtMethod* m, std::string& detail)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::jni_libraries_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::string jni_short_name(JniShortName(m));
+ std::string jni_long_name(JniLongName(m));
+ const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ for (const auto& lib : libraries_) {
+ SharedLibrary* library = lib.second;
+ if (soa.Decode<mirror::ClassLoader*>(library->GetClassLoader()) != declaring_class_loader) {
+ // We only search libraries loaded by the appropriate ClassLoader.
+ continue;
+ }
+ // Try the short name then the long name...
+ void* fn;
+ if (library->NeedsNativeBridge()) {
+ const char* shorty = m->GetShorty();
+ fn = library->FindSymbolWithNativeBridge(jni_short_name, shorty);
+ if (fn == nullptr) {
+ fn = library->FindSymbolWithNativeBridge(jni_long_name, shorty);
+ }
+ } else {
+ fn = library->FindSymbol(jni_short_name);
+ if (fn == nullptr) {
+ fn = library->FindSymbol(jni_long_name);
+ }
+ }
+ if (fn == nullptr) {
+ fn = library->FindSymbol(jni_long_name);
+ }
+ if (fn != nullptr) {
+ VLOG(jni) << "[Found native code for " << PrettyMethod(m)
+ << " in \"" << library->GetPath() << "\"]";
+ return fn;
+ }
+ }
+ detail += "No implementation found for ";
+ detail += PrettyMethod(m);
+ detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
+ LOG(ERROR) << detail;
+ return nullptr;
+ }
+
+ private:
+ SafeMap<std::string, SharedLibrary*> libraries_;
+};
+
+
+class JII {
+ public:
+ static jint DestroyJavaVM(JavaVM* vm) {
+ if (vm == nullptr) {
+ return JNI_ERR;
+ }
+ JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+ delete raw_vm->GetRuntime();
+ return JNI_OK;
+ }
+
+ static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+ return AttachCurrentThreadInternal(vm, p_env, thr_args, false);
+ }
+
+ static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+ return AttachCurrentThreadInternal(vm, p_env, thr_args, true);
+ }
+
+ static jint DetachCurrentThread(JavaVM* vm) {
+ if (vm == nullptr || Thread::Current() == nullptr) {
+ return JNI_ERR;
+ }
+ JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+ Runtime* runtime = raw_vm->GetRuntime();
+ runtime->DetachCurrentThread();
+ return JNI_OK;
+ }
+
+ static jint GetEnv(JavaVM* vm, void** env, jint version) {
+ // GetEnv always returns a JNIEnv* for the most current supported JNI version,
+ // and unlike other calls that take a JNI version doesn't care if you supply
+ // JNI_VERSION_1_1, which we don't otherwise support.
+ if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+ LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
+ return JNI_EVERSION;
+ }
+ if (vm == nullptr || env == nullptr) {
+ return JNI_ERR;
+ }
+ Thread* thread = Thread::Current();
+ if (thread == nullptr) {
+ *env = nullptr;
+ return JNI_EDETACHED;
+ }
+ *env = thread->GetJniEnv();
+ return JNI_OK;
+ }
+
+ private:
+ static jint AttachCurrentThreadInternal(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
+ if (vm == nullptr || p_env == nullptr) {
+ return JNI_ERR;
+ }
+
+ // Return immediately if we're already attached.
+ Thread* self = Thread::Current();
+ if (self != nullptr) {
+ *p_env = self->GetJniEnv();
+ return JNI_OK;
+ }
+
+ Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->GetRuntime();
+
+ // No threads allowed in zygote mode.
+ if (runtime->IsZygote()) {
+ LOG(ERROR) << "Attempt to attach a thread in the zygote";
+ return JNI_ERR;
+ }
+
+ JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
+ const char* thread_name = nullptr;
+ jobject thread_group = nullptr;
+ if (args != nullptr) {
+ if (IsBadJniVersion(args->version)) {
+ LOG(ERROR) << "Bad JNI version passed to "
+ << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
+ << args->version;
+ return JNI_EVERSION;
+ }
+ thread_name = args->name;
+ thread_group = args->group;
+ }
+
+ if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
+ *p_env = nullptr;
+ return JNI_ERR;
+ } else {
+ *p_env = Thread::Current()->GetJniEnv();
+ return JNI_OK;
+ }
+ }
+};
+
+const JNIInvokeInterface gJniInvokeInterface = {
+ nullptr, // reserved0
+ nullptr, // reserved1
+ nullptr, // reserved2
+ JII::DestroyJavaVM,
+ JII::AttachCurrentThread,
+ JII::DetachCurrentThread,
+ JII::GetEnv,
+ JII::AttachCurrentThreadAsDaemon
+};
+
+JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
+ : runtime_(runtime),
+ check_jni_abort_hook_(nullptr),
+ check_jni_abort_hook_data_(nullptr),
+ check_jni_(false), // Initialized properly in the constructor body below.
+ force_copy_(options->force_copy_),
+ tracing_enabled_(!options->jni_trace_.empty() || VLOG_IS_ON(third_party_jni)),
+ trace_(options->jni_trace_),
+ pins_lock_("JNI pin table lock", kPinTableLock),
+ pin_table_("pin table", kPinTableInitial, kPinTableMax),
+ globals_lock_("JNI global reference table lock"),
+ globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
+ libraries_(new Libraries),
+ unchecked_functions_(&gJniInvokeInterface),
+ weak_globals_lock_("JNI weak global reference table lock"),
+ weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
+ allow_new_weak_globals_(true),
+ weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
+ functions = unchecked_functions_;
+ if (options->check_jni_) {
+ SetCheckJniEnabled(true);
+ }
+}
+
+JavaVMExt::~JavaVMExt() {
+}
+
+void JavaVMExt::JniAbort(const char* jni_function_name, const char* msg) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ mirror::ArtMethod* current_method = self->GetCurrentMethod(nullptr);
+
+ std::ostringstream os;
+ os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
+
+ if (jni_function_name != nullptr) {
+ os << "\n in call to " << jni_function_name;
+ }
+ // TODO: is this useful given that we're about to dump the calling thread's stack?
+ if (current_method != nullptr) {
+ os << "\n from " << PrettyMethod(current_method);
+ }
+ os << "\n";
+ self->Dump(os);
+
+ if (check_jni_abort_hook_ != nullptr) {
+ check_jni_abort_hook_(check_jni_abort_hook_data_, os.str());
+ } else {
+ // Ensure that we get a native stack trace for this thread.
+ self->TransitionFromRunnableToSuspended(kNative);
+ LOG(FATAL) << os.str();
+ self->TransitionFromSuspendedToRunnable(); // Unreachable, keep annotalysis happy.
+ }
+}
+
+void JavaVMExt::JniAbortV(const char* jni_function_name, const char* fmt, va_list ap) {
+ std::string msg;
+ StringAppendV(&msg, fmt, ap);
+ JniAbort(jni_function_name, msg.c_str());
+}
+
+void JavaVMExt::JniAbortF(const char* jni_function_name, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ JniAbortV(jni_function_name, fmt, args);
+ va_end(args);
+}
+
+bool JavaVMExt::ShouldTrace(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Fast where no tracing is enabled.
+ if (trace_.empty() && !VLOG_IS_ON(third_party_jni)) {
+ return false;
+ }
+ // Perform checks based on class name.
+ StringPiece class_name(method->GetDeclaringClassDescriptor());
+ if (!trace_.empty() && class_name.find(trace_) != std::string::npos) {
+ return true;
+ }
+ if (!VLOG_IS_ON(third_party_jni)) {
+ return false;
+ }
+ // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
+ // like part of Android.
+ static const char* gBuiltInPrefixes[] = {
+ "Landroid/",
+ "Lcom/android/",
+ "Lcom/google/android/",
+ "Ldalvik/",
+ "Ljava/",
+ "Ljavax/",
+ "Llibcore/",
+ "Lorg/apache/harmony/",
+ };
+ for (size_t i = 0; i < arraysize(gBuiltInPrefixes); ++i) {
+ if (class_name.starts_with(gBuiltInPrefixes[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+jobject JavaVMExt::AddGlobalRef(Thread* self, mirror::Object* obj) {
+ // Check for null after decoding the object to handle cleared weak globals.
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ WriterMutexLock mu(self, globals_lock_);
+ IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj);
+ return reinterpret_cast<jobject>(ref);
+}
+
+jweak JavaVMExt::AddWeakGlobalRef(Thread* self, mirror::Object* obj) {
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ MutexLock mu(self, weak_globals_lock_);
+ while (UNLIKELY(!allow_new_weak_globals_)) {
+ weak_globals_add_condition_.WaitHoldingLocks(self);
+ }
+ IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
+ return reinterpret_cast<jweak>(ref);
+}
+
+void JavaVMExt::DeleteGlobalRef(Thread* self, jobject obj) {
+ if (obj == nullptr) {
+ return;
+ }
+ WriterMutexLock mu(self, globals_lock_);
+ if (!globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
+ LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
+ << "failed to find entry";
+ }
+}
+
+void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
+ if (obj == nullptr) {
+ return;
+ }
+ MutexLock mu(self, weak_globals_lock_);
+ if (!weak_globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
+ LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
+ << "failed to find entry";
+ }
+}
+
+static void ThreadEnableCheckJni(Thread* thread, void* arg) {
+ bool* check_jni = reinterpret_cast<bool*>(arg);
+ thread->GetJniEnv()->SetCheckJniEnabled(*check_jni);
+}
+
+bool JavaVMExt::SetCheckJniEnabled(bool enabled) {
+ bool old_check_jni = check_jni_;
+ check_jni_ = enabled;
+ functions = enabled ? GetCheckJniInvokeInterface() : unchecked_functions_;
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ runtime_->GetThreadList()->ForEach(ThreadEnableCheckJni, &check_jni_);
+ return old_check_jni;
+}
+
+void JavaVMExt::DumpForSigQuit(std::ostream& os) {
+ os << "JNI: CheckJNI is " << (check_jni_ ? "on" : "off");
+ if (force_copy_) {
+ os << " (with forcecopy)";
+ }
+ Thread* self = Thread::Current();
+ {
+ MutexLock mu(self, pins_lock_);
+ os << "; pins=" << pin_table_.Size();
+ }
+ {
+ ReaderMutexLock mu(self, globals_lock_);
+ os << "; globals=" << globals_.Capacity();
+ }
+ {
+ MutexLock mu(self, weak_globals_lock_);
+ if (weak_globals_.Capacity() > 0) {
+ os << " (plus " << weak_globals_.Capacity() << " weak)";
+ }
+ }
+ os << '\n';
+
+ {
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ os << "Libraries: " << Dumpable<Libraries>(*libraries_) << " (" << libraries_->size() << ")\n";
+ }
+}
+
+void JavaVMExt::DisallowNewWeakGlobals() {
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ allow_new_weak_globals_ = false;
+}
+
+void JavaVMExt::AllowNewWeakGlobals() {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, weak_globals_lock_);
+ allow_new_weak_globals_ = true;
+ weak_globals_add_condition_.Broadcast(self);
+}
+
+mirror::Object* JavaVMExt::DecodeGlobal(Thread* self, IndirectRef ref) {
+ return globals_.SynchronizedGet(self, &globals_lock_, ref);
+}
+
+mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
+ MutexLock mu(self, weak_globals_lock_);
+ while (UNLIKELY(!allow_new_weak_globals_)) {
+ weak_globals_add_condition_.WaitHoldingLocks(self);
+ }
+ return weak_globals_.Get(ref);
+}
+
+void JavaVMExt::PinPrimitiveArray(Thread* self, mirror::Array* array) {
+ MutexLock mu(self, pins_lock_);
+ pin_table_.Add(array);
+}
+
+void JavaVMExt::UnpinPrimitiveArray(Thread* self, mirror::Array* array) {
+ MutexLock mu(self, pins_lock_);
+ pin_table_.Remove(array);
+}
+
+void JavaVMExt::DumpReferenceTables(std::ostream& os) {
+ Thread* self = Thread::Current();
+ {
+ ReaderMutexLock mu(self, globals_lock_);
+ globals_.Dump(os);
+ }
+ {
+ MutexLock mu(self, weak_globals_lock_);
+ weak_globals_.Dump(os);
+ }
+ {
+ MutexLock mu(self, pins_lock_);
+ pin_table_.Dump(os);
+ }
+}
+
+bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
+ std::string* error_msg) {
+ error_msg->clear();
+
+ // See if we've already loaded this library. If we have, and the class loader
+ // matches, return successfully without doing anything.
+ // TODO: for better results we should canonicalize the pathname (or even compare
+ // inodes). This implementation is fine if everybody is using System.loadLibrary.
+ SharedLibrary* library;
+ Thread* self = Thread::Current();
+ {
+ // TODO: move the locking (and more of this logic) into Libraries.
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ library = libraries_->Get(path);
+ }
+ if (library != nullptr) {
+ if (env->IsSameObject(library->GetClassLoader(), class_loader) == JNI_FALSE) {
+ // The library will be associated with class_loader. The JNI
+ // spec says we can't load the same library into more than one
+ // class loader.
+ StringAppendF(error_msg, "Shared library \"%s\" already opened by "
+ "ClassLoader %p; can't open in ClassLoader %p",
+ path.c_str(), library->GetClassLoader(), class_loader);
+ LOG(WARNING) << error_msg;
+ return false;
+ }
+ VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
+ << " ClassLoader " << class_loader << "]";
+ if (!library->CheckOnLoadResult()) {
+ StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
+ "to load \"%s\"", path.c_str());
+ return false;
+ }
+ return true;
+ }
+
+ // Open the shared library. Because we're using a full path, the system
+ // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
+ // resolve this library's dependencies though.)
+
+ // Failures here are expected when java.library.path has several entries
+ // and we have to hunt for the lib.
+
+ // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
+ // class unloading. Libraries will only be unloaded when the reference count (incremented by
+ // dlopen) becomes zero from dlclose.
+
+ Locks::mutator_lock_->AssertNotHeld(self);
+ const char* path_str = path.empty() ? nullptr : path.c_str();
+ void* handle = dlopen(path_str, RTLD_LAZY);
+ bool needs_native_bridge = false;
+ if (handle == nullptr) {
+ if (NativeBridgeIsSupported(path_str)) {
+ handle = NativeBridgeLoadLibrary(path_str, RTLD_LAZY);
+ needs_native_bridge = true;
+ }
+ }
+
+ VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
+
+ if (handle == nullptr) {
+ *error_msg = dlerror();
+ LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << *error_msg;
+ return false;
+ }
+
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ LOG(ERROR) << "Unexpected exception:";
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ // Create a new entry.
+ // TODO: move the locking (and more of this logic) into Libraries.
+ bool created_library = false;
+ {
+ // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
+ std::unique_ptr<SharedLibrary> new_library(
+ new SharedLibrary(env, self, path, handle, class_loader));
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ library = libraries_->Get(path);
+ if (library == nullptr) { // We won race to get libraries_lock.
+ library = new_library.release();
+ libraries_->Put(path, library);
+ created_library = true;
+ }
+ }
+ if (!created_library) {
+ LOG(INFO) << "WOW: we lost a race to add shared library: "
+ << "\"" << path << "\" ClassLoader=" << class_loader;
+ return library->CheckOnLoadResult();
+ }
+ VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+
+ bool was_successful = false;
+ void* sym;
+ if (needs_native_bridge) {
+ library->SetNeedsNativeBridge();
+ sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
+ } else {
+ sym = dlsym(handle, "JNI_OnLoad");
+ }
+ if (sym == nullptr) {
+ VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
+ was_successful = true;
+ } else {
+ // Call JNI_OnLoad. We have to override the current class
+ // loader, which will always be "null" since the stuff at the
+ // top of the stack is around Runtime.loadLibrary(). (See
+ // the comments in the JNI FindClass function.)
+ ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
+ self->SetClassLoaderOverride(class_loader);
+
+ VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+ typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+ JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
+ int version = (*jni_on_load)(this, nullptr);
+
+ self->SetClassLoaderOverride(old_class_loader.get());
+
+ if (version == JNI_ERR) {
+ StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
+ } else if (IsBadJniVersion(version)) {
+ StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
+ path.c_str(), version);
+ // It's unwise to call dlclose() here, but we can mark it
+ // as bad and ensure that future load attempts will fail.
+ // We don't know how far JNI_OnLoad got, so there could
+ // be some partially-initialized stuff accessible through
+ // newly-registered native method calls. We could try to
+ // unregister them, but that doesn't seem worthwhile.
+ } else {
+ was_successful = true;
+ }
+ VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
+ << " from JNI_OnLoad in \"" << path << "\"]";
+ }
+
+ library->SetResult(was_successful);
+ return was_successful;
+}
+
+void* JavaVMExt::FindCodeForNativeMethod(mirror::ArtMethod* m) {
+ CHECK(m->IsNative());
+ mirror::Class* c = m->GetDeclaringClass();
+ // If this is a static method, it could be called before the class has been initialized.
+ CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
+ std::string detail;
+ void* native_method;
+ Thread* self = Thread::Current();
+ {
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ native_method = libraries_->FindNativeMethod(m, detail);
+ }
+ // Throwing can cause libraries_lock to be reacquired.
+ if (native_method == nullptr) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
+ }
+ return native_method;
+}
+
+void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ for (mirror::Object** entry : weak_globals_) {
+ // Since this is called by the GC, we don't need a read barrier.
+ mirror::Object* obj = *entry;
+ mirror::Object* new_obj = callback(obj, arg);
+ if (new_obj == nullptr) {
+ new_obj = kClearedJniWeakGlobal;
+ }
+ *entry = new_obj;
+ }
+}
+
+void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
+ Thread* self = Thread::Current();
+ {
+ ReaderMutexLock mu(self, globals_lock_);
+ globals_.VisitRoots(callback, arg, 0, kRootJNIGlobal);
+ }
+ {
+ MutexLock mu(self, pins_lock_);
+ pin_table_.VisitRoots(callback, arg, 0, kRootVMInternal);
+ }
+ // The weak_globals table is visited by the GC itself (because it mutates the table).
+}
+
+// JNI Invocation interface.
+
+extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+ const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
+ if (IsBadJniVersion(args->version)) {
+ LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
+ return JNI_EVERSION;
+ }
+ RuntimeOptions options;
+ for (int i = 0; i < args->nOptions; ++i) {
+ JavaVMOption* option = &args->options[i];
+ options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
+ }
+ bool ignore_unrecognized = args->ignoreUnrecognized;
+ if (!Runtime::Create(options, ignore_unrecognized)) {
+ return JNI_ERR;
+ }
+ Runtime* runtime = Runtime::Current();
+ bool started = runtime->Start();
+ if (!started) {
+ delete Thread::Current()->GetJniEnv();
+ delete runtime->GetJavaVM();
+ LOG(WARNING) << "CreateJavaVM failed";
+ return JNI_ERR;
+ }
+ *p_env = Thread::Current()->GetJniEnv();
+ *p_vm = runtime->GetJavaVM();
+ return JNI_OK;
+}
+
+extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime == nullptr) {
+ *vm_count = 0;
+ } else {
+ *vm_count = 1;
+ vms[0] = runtime->GetJavaVM();
+ }
+ return JNI_OK;
+}
+
+// Historically unsupported.
+extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* /*vm_args*/) {
+ return JNI_ERR;
+}
+
+} // namespace art
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
new file mode 100644
index 0000000..da0b8e3
--- /dev/null
+++ b/runtime/java_vm_ext.h
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_JAVA_VM_EXT_H_
+#define ART_RUNTIME_JAVA_VM_EXT_H_
+
+#include "jni.h"
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "indirect_reference_table.h"
+#include "reference_table.h"
+
+namespace art {
+
+namespace mirror {
+ class ArtMethod;
+ class Array;
+} // namespace mirror
+
+class Libraries;
+class ParsedOptions;
+class Runtime;
+
+class JavaVMExt : public JavaVM {
+ public:
+ JavaVMExt(Runtime* runtime, ParsedOptions* options);
+ ~JavaVMExt();
+
+ bool ForceCopy() const {
+ return force_copy_;
+ }
+
+ bool IsCheckJniEnabled() const {
+ return check_jni_;
+ }
+
+ bool IsTracingEnabled() const {
+ return tracing_enabled_;
+ }
+
+ Runtime* GetRuntime() const {
+ return runtime_;
+ }
+
+ void SetCheckJniAbortHook(void (*hook)(void*, const std::string&), void* data) {
+ check_jni_abort_hook_ = hook;
+ check_jni_abort_hook_data_ = data;
+ }
+
+ // Aborts execution unless there is an abort handler installed in which case it will return. Its
+ // therefore important that callers return after aborting as otherwise code following the abort
+ // will be executed in the abort handler case.
+ void JniAbort(const char* jni_function_name, const char* msg);
+
+ void JniAbortV(const char* jni_function_name, const char* fmt, va_list ap);
+
+ void JniAbortF(const char* jni_function_name, const char* fmt, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+ // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
+ // when a native method that matches the -Xjnitrace argument calls a JNI function
+ // such as NewByteArray.
+ // If -verbose:third-party-jni is on, we want to log any JNI function calls
+ // made by a third-party native method.
+ bool ShouldTrace(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /**
+ * Loads the given shared library. 'path' is an absolute pathname.
+ *
+ * Returns 'true' on success. On failure, sets 'detail' to a
+ * human-readable description of the error.
+ */
+ bool LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject javaLoader,
+ std::string* error_msg);
+
+ /**
+ * Returns a pointer to the code for the native method 'm', found
+ * using dlsym(3) on every native library that's been loaded so far.
+ */
+ void* FindCodeForNativeMethod(mirror::ArtMethod* m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DumpForSigQuit(std::ostream& os)
+ LOCKS_EXCLUDED(Locks::jni_libraries_lock_, globals_lock_, weak_globals_lock_, pins_lock_);
+
+ void DumpReferenceTables(std::ostream& os)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool SetCheckJniEnabled(bool enabled);
+
+ void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ jobject AddGlobalRef(Thread* self, mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ jweak AddWeakGlobalRef(Thread* self, mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DeleteGlobalRef(Thread* self, jobject obj);
+
+ void DeleteWeakGlobalRef(Thread* self, jweak obj);
+
+ void SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Object* DecodeGlobal(Thread* self, IndirectRef ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void PinPrimitiveArray(Thread* self, mirror::Array* array)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(pins_lock_);
+
+ void UnpinPrimitiveArray(Thread* self, mirror::Array* array)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(pins_lock_);
+
+ const JNIInvokeInterface* GetUncheckedFunctions() const {
+ return unchecked_functions_;
+ }
+
+ private:
+ Runtime* const runtime_;
+
+ // Used for testing. By default, we'll LOG(FATAL) the reason.
+ void (*check_jni_abort_hook_)(void* data, const std::string& reason);
+ void* check_jni_abort_hook_data_;
+
+ // Extra checking.
+ bool check_jni_;
+ bool force_copy_;
+ const bool tracing_enabled_;
+
+ // Extra diagnostics.
+ const std::string trace_;
+
+ // Used to hold references to pinned primitive arrays.
+ Mutex pins_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ ReferenceTable pin_table_ GUARDED_BY(pins_lock_);
+
+ // JNI global references.
+ ReaderWriterMutex globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
+ IndirectReferenceTable globals_;
+
+ std::unique_ptr<Libraries> libraries_ GUARDED_BY(Locks::jni_libraries_lock_);
+
+ // Used by -Xcheck:jni.
+ const JNIInvokeInterface* const unchecked_functions_;
+
+ // JNI weak global references.
+ Mutex weak_globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Since weak_globals_ contain weak roots, be careful not to
+ // directly access the object references in it. Use Get() with the
+ // read barrier enabled.
+ IndirectReferenceTable weak_globals_ GUARDED_BY(weak_globals_lock_);
+ bool allow_new_weak_globals_ GUARDED_BY(weak_globals_lock_);
+ ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(JavaVMExt);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JAVA_VM_EXT_H_
diff --git a/runtime/jni_internal-inl.h b/runtime/jni_env_ext-inl.h
index 6cf9a61..dc6a3e8 100644
--- a/runtime/jni_internal-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_JNI_INTERNAL_INL_H_
-#define ART_RUNTIME_JNI_INTERNAL_INL_H_
+#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_
+#define ART_RUNTIME_JNI_ENV_EXT_INL_H_
-#include "jni_internal.h"
+#include "jni_env_ext.h"
#include "utils.h"
@@ -44,4 +44,4 @@ inline T JNIEnvExt::AddLocalReference(mirror::Object* obj) {
} // namespace art
-#endif // ART_RUNTIME_JNI_INTERNAL_INL_H_
+#endif // ART_RUNTIME_JNI_ENV_EXT_INL_H_
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
new file mode 100644
index 0000000..180e3d7
--- /dev/null
+++ b/runtime/jni_env_ext.cc
@@ -0,0 +1,89 @@
+/*
+ * 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 "jni_env_ext.h"
+
+#include "check_jni.h"
+#include "indirect_reference_table.h"
+#include "java_vm_ext.h"
+#include "jni_internal.h"
+
+namespace art {
+
+static constexpr size_t kMonitorsInitial = 32; // Arbitrary.
+static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check.
+
+static constexpr size_t kLocalsInitial = 64; // Arbitrary.
+
+JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
+ : self(self),
+ vm(vm),
+ local_ref_cookie(IRT_FIRST_SEGMENT),
+ locals(kLocalsInitial, kLocalsMax, kLocal),
+ check_jni(false),
+ critical(0),
+ monitors("monitors", kMonitorsInitial, kMonitorsMax) {
+ functions = unchecked_functions = GetJniNativeInterface();
+ if (vm->IsCheckJniEnabled()) {
+ SetCheckJniEnabled(true);
+ }
+}
+
+JNIEnvExt::~JNIEnvExt() {
+}
+
+jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
+}
+
+void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (obj != nullptr) {
+ locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
+ }
+}
+
+void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
+ check_jni = enabled;
+ functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
+void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
+ locals.Dump(os);
+ monitors.Dump(os);
+}
+
+void JNIEnvExt::PushFrame(int capacity) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UNUSED(capacity); // cpplint gets confused with (int) and thinks its a cast.
+ // TODO: take 'capacity' into account.
+ stacked_local_ref_cookies.push_back(local_ref_cookie);
+ local_ref_cookie = locals.GetSegmentState();
+}
+
+void JNIEnvExt::PopFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ locals.SetSegmentState(local_ref_cookie);
+ local_ref_cookie = stacked_local_ref_cookies.back();
+ stacked_local_ref_cookies.pop_back();
+}
+
+Offset JNIEnvExt::SegmentStateOffset() {
+ return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
+ IndirectReferenceTable::SegmentStateOffset().Int32Value());
+}
+
+} // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
new file mode 100644
index 0000000..af87cb4
--- /dev/null
+++ b/runtime/jni_env_ext.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_JNI_ENV_EXT_H_
+#define ART_RUNTIME_JNI_ENV_EXT_H_
+
+#include <jni.h>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "indirect_reference_table.h"
+#include "object_callbacks.h"
+#include "reference_table.h"
+
+namespace art {
+
+class JavaVMExt;
+
+// Maximum number of local references in the indirect reference table. The value is arbitrary but
+// low enough that it forces sanity checks.
+static constexpr size_t kLocalsMax = 512;
+
+struct JNIEnvExt : public JNIEnv {
+ JNIEnvExt(Thread* self, JavaVMExt* vm);
+ ~JNIEnvExt();
+
+ void DumpReferenceTables(std::ostream& os)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetCheckJniEnabled(bool enabled);
+
+ void PushFrame(int capacity);
+ void PopFrame();
+
+ template<typename T>
+ T AddLocalReference(mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static Offset SegmentStateOffset();
+
+ static Offset LocalRefCookieOffset() {
+ return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
+ }
+
+ static Offset SelfOffset() {
+ return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
+ }
+
+ jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Thread* const self;
+ JavaVMExt* const vm;
+
+ // Cookie used when using the local indirect reference table.
+ uint32_t local_ref_cookie;
+
+ // JNI local references.
+ IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
+
+ // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
+ // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
+ // to a native method.
+ std::vector<uint32_t> stacked_local_ref_cookies;
+
+ // Frequently-accessed fields cached from JavaVM.
+ bool check_jni;
+
+ // How many nested "critical" JNI calls are we in?
+ int critical;
+
+ // Entered JNI monitors, for bulk exit on thread detach.
+ ReferenceTable monitors;
+
+ // Used by -Xcheck:jni.
+ const JNINativeInterface* unchecked_functions;
+};
+
+// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
+// compiler.
+class ScopedJniEnvLocalRefState {
+ public:
+ explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
+ saved_local_ref_cookie_ = env->local_ref_cookie;
+ env->local_ref_cookie = env->locals.GetSegmentState();
+ }
+
+ ~ScopedJniEnvLocalRefState() {
+ env_->locals.SetSegmentState(env_->local_ref_cookie);
+ env_->local_ref_cookie = saved_local_ref_cookie_;
+ }
+
+ private:
+ JNIEnvExt* const env_;
+ uint32_t saved_local_ref_cookie_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JNI_ENV_EXT_H_
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 43b9912..d5e92a4 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -33,7 +33,8 @@
#include "gc/accounting/card_table-inl.h"
#include "indirect_reference_table-inl.h"
#include "interpreter/interpreter.h"
-#include "jni.h"
+#include "jni_env_ext.h"
+#include "java_vm_ext.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
@@ -55,31 +56,6 @@
namespace art {
-static const size_t kMonitorsInitial = 32; // Arbitrary.
-static const size_t kMonitorsMax = 4096; // Arbitrary sanity check.
-
-static const size_t kLocalsInitial = 64; // Arbitrary.
-static const size_t kLocalsMax = 512; // Arbitrary sanity check.
-
-static const size_t kPinTableInitial = 16; // Arbitrary.
-static const size_t kPinTableMax = 1024; // Arbitrary sanity check.
-
-static size_t gGlobalsInitial = 512; // Arbitrary.
-static size_t gGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
-
-static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
-static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
-
-static jweak AddWeakGlobalReference(ScopedObjectAccess& soa, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return soa.Vm()->AddWeakGlobalReference(soa.Self(), obj);
-}
-
-static bool IsBadJniVersion(int version) {
- // We don't support JNI_VERSION_1_1. These are the only other valid versions.
- return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
-}
-
// Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
// separated with slashes but aren't wrapped with "L;" like regular descriptors
// (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an
@@ -169,7 +145,7 @@ static mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa)
mirror::ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
// If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
if (method == soa.DecodeMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
- return soa.Self()->GetClassLoaderOverride();
+ return soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
}
// If we have a method, use its ClassLoader for context.
if (method != nullptr) {
@@ -182,7 +158,7 @@ static mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa)
return class_loader;
}
// See if the override ClassLoader is set for gtests.
- class_loader = soa.Self()->GetClassLoaderOverride();
+ class_loader = soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
if (class_loader != nullptr) {
// If so, CommonCompilerTest should have set UseCompileTimeClassPath.
CHECK(Runtime::Current()->UseCompileTimeClassPath());
@@ -240,20 +216,6 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
return soa.EncodeField(field);
}
-static void PinPrimitiveArray(const ScopedObjectAccess& soa, mirror::Array* array)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- JavaVMExt* vm = soa.Vm();
- MutexLock mu(soa.Self(), vm->pins_lock);
- vm->pin_table.Add(array);
-}
-
-static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, mirror::Array* array)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- JavaVMExt* vm = soa.Vm();
- MutexLock mu(soa.Self(), vm->pins_lock);
- vm->pin_table.Remove(array);
-}
-
static void ThrowAIOOBE(ScopedObjectAccess& soa, mirror::Array* array, jsize start,
jsize length, const char* identifier)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -316,255 +278,10 @@ int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobj
return JNI_OK;
}
-static jint JII_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
- if (vm == nullptr || p_env == nullptr) {
- return JNI_ERR;
- }
-
- // Return immediately if we're already attached.
- Thread* self = Thread::Current();
- if (self != nullptr) {
- *p_env = self->GetJniEnv();
- return JNI_OK;
- }
-
- Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
-
- // No threads allowed in zygote mode.
- if (runtime->IsZygote()) {
- LOG(ERROR) << "Attempt to attach a thread in the zygote";
- return JNI_ERR;
- }
-
- JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
- const char* thread_name = nullptr;
- jobject thread_group = nullptr;
- if (args != nullptr) {
- if (IsBadJniVersion(args->version)) {
- LOG(ERROR) << "Bad JNI version passed to "
- << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
- << args->version;
- return JNI_EVERSION;
- }
- thread_name = args->name;
- thread_group = args->group;
- }
-
- if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
- *p_env = nullptr;
- return JNI_ERR;
- } else {
- *p_env = Thread::Current()->GetJniEnv();
- return JNI_OK;
- }
+static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) {
+ return reinterpret_cast<JNIEnvExt*>(env)->vm;
}
-class SharedLibrary {
- public:
- SharedLibrary(const std::string& path, void* handle, mirror::Object* class_loader)
- : path_(path),
- handle_(handle),
- needs_native_bridge_(false),
- class_loader_(GcRoot<mirror::Object>(class_loader)),
- jni_on_load_lock_("JNI_OnLoad lock"),
- jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
- jni_on_load_thread_id_(Thread::Current()->GetThreadId()),
- jni_on_load_result_(kPending) {
- }
-
- mirror::Object* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return class_loader_.Read();
- }
-
- std::string GetPath() {
- return path_;
- }
-
- /*
- * Check the result of an earlier call to JNI_OnLoad on this library.
- * If the call has not yet finished in another thread, wait for it.
- */
- bool CheckOnLoadResult()
- LOCKS_EXCLUDED(jni_on_load_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Thread* self = Thread::Current();
- self->TransitionFromRunnableToSuspended(kWaitingForJniOnLoad);
- bool okay;
- {
- MutexLock mu(self, jni_on_load_lock_);
-
- if (jni_on_load_thread_id_ == self->GetThreadId()) {
- // Check this so we don't end up waiting for ourselves. We need to return "true" so the
- // caller can continue.
- LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
- okay = true;
- } else {
- while (jni_on_load_result_ == kPending) {
- VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
- jni_on_load_cond_.Wait(self);
- }
-
- okay = (jni_on_load_result_ == kOkay);
- VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
- << (okay ? "succeeded" : "failed") << "]";
- }
- }
- self->TransitionFromSuspendedToRunnable();
- return okay;
- }
-
- void SetResult(bool result) LOCKS_EXCLUDED(jni_on_load_lock_) {
- Thread* self = Thread::Current();
- MutexLock mu(self, jni_on_load_lock_);
-
- jni_on_load_result_ = result ? kOkay : kFailed;
- jni_on_load_thread_id_ = 0;
-
- // Broadcast a wakeup to anybody sleeping on the condition variable.
- jni_on_load_cond_.Broadcast(self);
- }
-
- void SetNeedsNativeBridge() {
- needs_native_bridge_ = true;
- }
-
- bool NeedsNativeBridge() const {
- return needs_native_bridge_;
- }
-
- void* FindSymbol(const std::string& symbol_name) {
- return dlsym(handle_, symbol_name.c_str());
- }
-
- void* FindSymbolWithNativeBridge(const std::string& symbol_name, mirror::ArtMethod* m)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK(NeedsNativeBridge());
-
- uint32_t len = 0;
- const char* shorty = nullptr;
- if (m != nullptr) {
- shorty = m->GetShorty(&len);
- }
- return NativeBridge::GetTrampoline(handle_, symbol_name.c_str(), shorty, len);
- }
-
- void VisitRoots(RootCallback* visitor, void* arg) {
- if (!class_loader_.IsNull()) {
- class_loader_.VisitRoot(visitor, arg, 0, kRootVMInternal);
- }
- }
-
- private:
- enum JNI_OnLoadState {
- kPending,
- kFailed,
- kOkay,
- };
-
- // Path to library "/system/lib/libjni.so".
- std::string path_;
-
- // The void* returned by dlopen(3).
- void* handle_;
-
- // True if a native bridge is required.
- bool needs_native_bridge_;
-
- // The ClassLoader this library is associated with.
- GcRoot<mirror::Object> class_loader_;
-
- // Guards remaining items.
- Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- // Wait for JNI_OnLoad in other thread.
- ConditionVariable jni_on_load_cond_ GUARDED_BY(jni_on_load_lock_);
- // Recursive invocation guard.
- uint32_t jni_on_load_thread_id_ GUARDED_BY(jni_on_load_lock_);
- // Result of earlier JNI_OnLoad call.
- JNI_OnLoadState jni_on_load_result_ GUARDED_BY(jni_on_load_lock_);
-};
-
-// This exists mainly to keep implementation details out of the header file.
-class Libraries {
- public:
- Libraries() {
- }
-
- ~Libraries() {
- STLDeleteValues(&libraries_);
- }
-
- void Dump(std::ostream& os) const {
- bool first = true;
- for (const auto& library : libraries_) {
- if (!first) {
- os << ' ';
- }
- first = false;
- os << library.first;
- }
- }
-
- size_t size() const {
- return libraries_.size();
- }
-
- SharedLibrary* Get(const std::string& path) {
- auto it = libraries_.find(path);
- return (it == libraries_.end()) ? nullptr : it->second;
- }
-
- void Put(const std::string& path, SharedLibrary* library) {
- libraries_.Put(path, library);
- }
-
- // See section 11.3 "Linking Native Methods" of the JNI spec.
- void* FindNativeMethod(mirror::ArtMethod* m, std::string& detail)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::string jni_short_name(JniShortName(m));
- std::string jni_long_name(JniLongName(m));
- const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
- for (const auto& lib : libraries_) {
- SharedLibrary* library = lib.second;
- if (library->GetClassLoader() != declaring_class_loader) {
- // We only search libraries loaded by the appropriate ClassLoader.
- continue;
- }
- // Try the short name then the long name...
- void* fn = nullptr;
- if (UNLIKELY(library->NeedsNativeBridge())) {
- fn = library->FindSymbolWithNativeBridge(jni_short_name, m);
- if (fn == nullptr) {
- fn = library->FindSymbolWithNativeBridge(jni_long_name, m);
- }
- } else {
- fn = library->FindSymbol(jni_short_name);
- if (fn == nullptr) {
- fn = library->FindSymbol(jni_long_name);
- }
- }
- if (fn != nullptr) {
- VLOG(jni) << "[Found native code for " << PrettyMethod(m)
- << " in \"" << library->GetPath() << "\"]";
- return fn;
- }
- }
- detail += "No implementation found for ";
- detail += PrettyMethod(m);
- detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
- LOG(ERROR) << detail;
- return nullptr;
- }
-
- void VisitRoots(RootCallback* callback, void* arg) {
- for (auto& lib_pair : libraries_) {
- lib_pair.second->VisitRoots(callback, arg);
- }
- }
-
- private:
- SafeMap<std::string, SharedLibrary*> libraries_;
-};
-
#define CHECK_NON_NULL_ARGUMENT(value) \
CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, nullptr)
@@ -579,13 +296,13 @@ class Libraries {
#define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value, return_val) \
if (UNLIKELY(value == nullptr)) { \
- JniAbortF(name, #value " == null"); \
+ JavaVmExtFromEnv(env)->JniAbortF(name, #value " == null"); \
return return_val; \
}
#define CHECK_NON_NULL_MEMCPY_ARGUMENT(length, value) \
if (UNLIKELY(length != 0 && value == nullptr)) { \
- JniAbortF(__FUNCTION__, #value " == null"); \
+ JavaVmExtFromEnv(env)->JniAbortF(__FUNCTION__, #value " == null"); \
return; \
}
@@ -786,10 +503,10 @@ class JNI {
static jint PushLocalFrame(JNIEnv* env, jint capacity) {
// TODO: SOA may not be necessary but I do it to please lock annotations.
ScopedObjectAccess soa(env);
- if (EnsureLocalCapacity(soa, capacity, "PushLocalFrame") != JNI_OK) {
+ if (EnsureLocalCapacityInternal(soa, capacity, "PushLocalFrame") != JNI_OK) {
return JNI_ERR;
}
- static_cast<JNIEnvExt*>(env)->PushFrame(capacity);
+ down_cast<JNIEnvExt*>(env)->PushFrame(capacity);
return JNI_OK;
}
@@ -803,48 +520,31 @@ class JNI {
static jint EnsureLocalCapacity(JNIEnv* env, jint desired_capacity) {
// TODO: SOA may not be necessary but I do it to please lock annotations.
ScopedObjectAccess soa(env);
- return EnsureLocalCapacity(soa, desired_capacity, "EnsureLocalCapacity");
+ return EnsureLocalCapacityInternal(soa, desired_capacity, "EnsureLocalCapacity");
}
static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
ScopedObjectAccess soa(env);
mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
- // Check for null after decoding the object to handle cleared weak globals.
- if (decoded_obj == nullptr) {
- return nullptr;
- }
- JavaVMExt* vm = soa.Vm();
- IndirectReferenceTable& globals = vm->globals;
- WriterMutexLock mu(soa.Self(), vm->globals_lock);
- IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, decoded_obj);
- return reinterpret_cast<jobject>(ref);
+ return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj);
}
static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
- if (obj == nullptr) {
- return;
- }
- JavaVMExt* vm = reinterpret_cast<JNIEnvExt*>(env)->vm;
- IndirectReferenceTable& globals = vm->globals;
- Thread* self = reinterpret_cast<JNIEnvExt*>(env)->self;
- WriterMutexLock mu(self, vm->globals_lock);
-
- if (!globals.Remove(IRT_FIRST_SEGMENT, obj)) {
- LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
- << "failed to find entry";
- }
+ JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
+ Thread* self = down_cast<JNIEnvExt*>(env)->self;
+ vm->DeleteGlobalRef(self, obj);
}
static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
ScopedObjectAccess soa(env);
- return AddWeakGlobalReference(soa, soa.Decode<mirror::Object*>(obj));
+ mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
+ return soa.Vm()->AddWeakGlobalRef(soa.Self(), decoded_obj);
}
static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
- if (obj != nullptr) {
- ScopedObjectAccess soa(env);
- soa.Vm()->DeleteWeakGlobalRef(soa.Self(), obj);
- }
+ JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
+ Thread* self = down_cast<JNIEnvExt*>(env)->self;
+ vm->DeleteWeakGlobalRef(self, obj);
}
static jobject NewLocalRef(JNIEnv* env, jobject obj) {
@@ -861,7 +561,6 @@ class JNI {
if (obj == nullptr) {
return;
}
- ScopedObjectAccess soa(env);
IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env)->locals;
uint32_t cookie = reinterpret_cast<JNIEnvExt*>(env)->local_ref_cookie;
@@ -1927,11 +1626,11 @@ class JNI {
static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) {
if (UNLIKELY(char_count < 0)) {
- JniAbortF("NewString", "char_count < 0: %d", char_count);
+ JavaVmExtFromEnv(env)->JniAbortF("NewString", "char_count < 0: %d", char_count);
return nullptr;
}
if (UNLIKELY(chars == nullptr && char_count > 0)) {
- JniAbortF("NewString", "chars == null && char_count > 0");
+ JavaVmExtFromEnv(env)->JniAbortF("NewString", "chars == null && char_count > 0");
return nullptr;
}
ScopedObjectAccess soa(env);
@@ -1993,7 +1692,7 @@ class JNI {
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
mirror::CharArray* chars = s->GetCharArray();
- PinPrimitiveArray(soa, chars);
+ soa.Vm()->PinPrimitiveArray(soa.Self(), chars);
gc::Heap* heap = Runtime::Current()->GetHeap();
if (heap->IsMovableObject(chars)) {
if (is_copy != nullptr) {
@@ -2022,7 +1721,7 @@ class JNI {
if (chars != (s_chars->GetData() + s->GetOffset())) {
delete[] chars;
}
- UnpinPrimitiveArray(soa, s->GetCharArray());
+ soa.Vm()->UnpinPrimitiveArray(soa.Self(), s->GetCharArray());
}
static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* is_copy) {
@@ -2031,7 +1730,7 @@ class JNI {
mirror::String* s = soa.Decode<mirror::String*>(java_string);
mirror::CharArray* chars = s->GetCharArray();
int32_t offset = s->GetOffset();
- PinPrimitiveArray(soa, chars);
+ soa.Vm()->PinPrimitiveArray(soa.Self(), chars);
gc::Heap* heap = Runtime::Current()->GetHeap();
if (heap->IsMovableObject(chars)) {
StackHandleScope<1> hs(soa.Self());
@@ -2047,7 +1746,8 @@ class JNI {
static void ReleaseStringCritical(JNIEnv* env, jstring java_string, const jchar* chars) {
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
ScopedObjectAccess soa(env);
- UnpinPrimitiveArray(soa, soa.Decode<mirror::String*>(java_string)->GetCharArray());
+ soa.Vm()->UnpinPrimitiveArray(soa.Self(),
+ soa.Decode<mirror::String*>(java_string)->GetCharArray());
gc::Heap* heap = Runtime::Current()->GetHeap();
mirror::String* s = soa.Decode<mirror::String*>(java_string);
mirror::CharArray* s_chars = s->GetCharArray();
@@ -2083,7 +1783,8 @@ class JNI {
ScopedObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(java_array);
if (UNLIKELY(!obj->IsArrayInstance())) {
- JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
+ soa.Vm()->JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
+ return 0;
}
mirror::Array* array = obj->AsArray();
return array->GetLength();
@@ -2138,7 +1839,7 @@ class JNI {
static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass,
jobject initial_element) {
if (UNLIKELY(length < 0)) {
- JniAbortF("NewObjectArray", "negative array length: %d", length);
+ JavaVmExtFromEnv(env)->JniAbortF("NewObjectArray", "negative array length: %d", length);
return nullptr;
}
CHECK_NON_NULL_ARGUMENT(element_jclass);
@@ -2149,8 +1850,8 @@ class JNI {
{
mirror::Class* element_class = soa.Decode<mirror::Class*>(element_jclass);
if (UNLIKELY(element_class->IsPrimitive())) {
- JniAbortF("NewObjectArray", "not an object type: %s",
- PrettyDescriptor(element_class).c_str());
+ soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s",
+ PrettyDescriptor(element_class).c_str());
return nullptr;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -2168,10 +1869,11 @@ class JNI {
if (initial_object != nullptr) {
mirror::Class* element_class = result->GetClass()->GetComponentType();
if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) {
- JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with element "
- "type of '%s'", PrettyDescriptor(initial_object->GetClass()).c_str(),
- PrettyDescriptor(element_class).c_str());
-
+ soa.Vm()->JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with "
+ "element type of '%s'",
+ PrettyDescriptor(initial_object->GetClass()).c_str(),
+ PrettyDescriptor(element_class).c_str());
+ return nullptr;
} else {
for (jsize i = 0; i < length; ++i) {
result->SetWithoutChecks<false>(i, initial_object);
@@ -2191,8 +1893,8 @@ class JNI {
ScopedObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
- JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
- PrettyDescriptor(array->GetClass()).c_str());
+ soa.Vm()->JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
+ PrettyDescriptor(array->GetClass()).c_str());
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -2201,7 +1903,7 @@ class JNI {
// Re-decode in case the object moved since IncrementDisableGC waits for GC to complete.
array = soa.Decode<mirror::Array*>(java_array);
}
- PinPrimitiveArray(soa, array);
+ soa.Vm()->PinPrimitiveArray(soa.Self(), array);
if (is_copy != nullptr) {
*is_copy = JNI_FALSE;
}
@@ -2214,8 +1916,8 @@ class JNI {
ScopedObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
- JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
- PrettyDescriptor(array->GetClass()).c_str());
+ soa.Vm()->JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
+ PrettyDescriptor(array->GetClass()).c_str());
return;
}
const size_t component_size = array->GetClass()->GetComponentSize();
@@ -2387,8 +2089,9 @@ class JNI {
static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
jint method_count, bool return_errors) {
if (UNLIKELY(method_count < 0)) {
- JniAbortF("RegisterNatives", "negative method count: %d", method_count);
- return JNI_ERR; // Not reached.
+ JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
+ method_count);
+ return JNI_ERR; // Not reached except in unit tests.
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
ScopedObjectAccess soa(env);
@@ -2512,17 +2215,21 @@ class JNI {
static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
if (capacity < 0) {
- JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64, capacity);
+ JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64,
+ capacity);
return nullptr;
}
if (address == nullptr && capacity != 0) {
- JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity);
+ JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer",
+ "non-zero capacity for nullptr pointer: %" PRId64, capacity);
return nullptr;
}
// At the moment, the capacity of DirectByteBuffer is limited to a signed int.
if (capacity > INT_MAX) {
- JniAbortF("NewDirectByteBuffer", "buffer capacity greater than maximum jint: %" PRId64, capacity);
+ JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer",
+ "buffer capacity greater than maximum jint: %" PRId64,
+ capacity);
return nullptr;
}
jlong address_arg = reinterpret_cast<jlong>(address);
@@ -2576,8 +2283,9 @@ class JNI {
}
private:
- static jint EnsureLocalCapacity(ScopedObjectAccess& soa, jint desired_capacity,
- const char* caller) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static jint EnsureLocalCapacityInternal(ScopedObjectAccess& soa, jint desired_capacity,
+ const char* caller)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// TODO: we should try to expand the table if necessary.
if (desired_capacity < 0 || desired_capacity > static_cast<jint>(kLocalsMax)) {
LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
@@ -2594,11 +2302,11 @@ class JNI {
template<typename JniT, typename ArtT>
static JniT NewPrimitiveArray(JNIEnv* env, jsize length) {
+ ScopedObjectAccess soa(env);
if (UNLIKELY(length < 0)) {
- JniAbortF("NewPrimitiveArray", "negative array length: %d", length);
+ soa.Vm()->JniAbortF("NewPrimitiveArray", "negative array length: %d", length);
return nullptr;
}
- ScopedObjectAccess soa(env);
ArtT* result = ArtT::Alloc(soa.Self(), length);
return soa.AddLocalReference<JniT>(result);
}
@@ -2609,9 +2317,11 @@ class JNI {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ArtArrayT* array = soa.Decode<ArtArrayT*>(java_array);
if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) {
- JniAbortF(fn_name, "attempt to %s %s primitive array elements with an object of type %s",
- operation, PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(),
- PrettyDescriptor(array->GetClass()).c_str());
+ soa.Vm()->JniAbortF(fn_name,
+ "attempt to %s %s primitive array elements with an object of type %s",
+ operation,
+ PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(),
+ PrettyDescriptor(array->GetClass()).c_str());
return nullptr;
}
DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize());
@@ -2628,7 +2338,7 @@ class JNI {
if (UNLIKELY(array == nullptr)) {
return nullptr;
}
- PinPrimitiveArray(soa, array);
+ soa.Vm()->PinPrimitiveArray(soa.Self(), array);
// Only make a copy if necessary.
if (Runtime::Current()->GetHeap()->IsMovableObject(array)) {
if (is_copy != nullptr) {
@@ -2674,8 +2384,9 @@ class JNI {
// heap address. TODO: This might be slow to check, may be worth keeping track of which
// copies we make?
if (heap->IsNonDiscontinuousSpaceHeapAddress(reinterpret_cast<mirror::Object*>(elements))) {
- JniAbortF("ReleaseArrayElements", "invalid element pointer %p, array elements are %p",
- reinterpret_cast<void*>(elements), array_data);
+ soa.Vm()->JniAbortF("ReleaseArrayElements",
+ "invalid element pointer %p, array elements are %p",
+ reinterpret_cast<void*>(elements), array_data);
return;
}
}
@@ -2690,7 +2401,7 @@ class JNI {
// Non copy to a movable object must means that we had disabled the moving GC.
heap->DecrementDisableMovingGC(soa.Self());
}
- UnpinPrimitiveArray(soa, array);
+ soa.Vm()->UnpinPrimitiveArray(soa.Self(), array);
}
}
@@ -2971,487 +2682,8 @@ const JNINativeInterface gJniNativeInterface = {
JNI::GetObjectRefType,
};
-JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
- : self(self),
- vm(vm),
- local_ref_cookie(IRT_FIRST_SEGMENT),
- locals(kLocalsInitial, kLocalsMax, kLocal),
- check_jni(false),
- critical(0),
- monitors("monitors", kMonitorsInitial, kMonitorsMax) {
- functions = unchecked_functions = &gJniNativeInterface;
- if (vm->check_jni) {
- SetCheckJniEnabled(true);
- }
-}
-
-JNIEnvExt::~JNIEnvExt() {
-}
-
-jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (obj == nullptr) {
- return nullptr;
- }
- return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
-}
-
-void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (obj != nullptr) {
- locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
- }
-}
-void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
- check_jni = enabled;
- functions = enabled ? GetCheckJniNativeInterface() : &gJniNativeInterface;
-}
-
-void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
- locals.Dump(os);
- monitors.Dump(os);
-}
-
-void JNIEnvExt::PushFrame(int capacity) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- UNUSED(capacity); // cpplint gets confused with (int) and thinks its a cast.
- // TODO: take 'capacity' into account.
- stacked_local_ref_cookies.push_back(local_ref_cookie);
- local_ref_cookie = locals.GetSegmentState();
-}
-
-void JNIEnvExt::PopFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- locals.SetSegmentState(local_ref_cookie);
- local_ref_cookie = stacked_local_ref_cookies.back();
- stacked_local_ref_cookies.pop_back();
-}
-
-Offset JNIEnvExt::SegmentStateOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
- IndirectReferenceTable::SegmentStateOffset().Int32Value());
-}
-
-// JNI Invocation interface.
-
-extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
- const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
- if (IsBadJniVersion(args->version)) {
- LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
- return JNI_EVERSION;
- }
- RuntimeOptions options;
- for (int i = 0; i < args->nOptions; ++i) {
- JavaVMOption* option = &args->options[i];
- options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
- }
- bool ignore_unrecognized = args->ignoreUnrecognized;
- if (!Runtime::Create(options, ignore_unrecognized)) {
- return JNI_ERR;
- }
- Runtime* runtime = Runtime::Current();
- bool started = runtime->Start();
- if (!started) {
- delete Thread::Current()->GetJniEnv();
- delete runtime->GetJavaVM();
- LOG(WARNING) << "CreateJavaVM failed";
- return JNI_ERR;
- }
- *p_env = Thread::Current()->GetJniEnv();
- *p_vm = runtime->GetJavaVM();
- return JNI_OK;
-}
-
-extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
- Runtime* runtime = Runtime::Current();
- if (runtime == nullptr) {
- *vm_count = 0;
- } else {
- *vm_count = 1;
- vms[0] = runtime->GetJavaVM();
- }
- return JNI_OK;
-}
-
-// Historically unsupported.
-extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* /*vm_args*/) {
- return JNI_ERR;
-}
-
-class JII {
- public:
- static jint DestroyJavaVM(JavaVM* vm) {
- if (vm == nullptr) {
- return JNI_ERR;
- }
- JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
- delete raw_vm->runtime;
- return JNI_OK;
- }
-
- static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- return JII_AttachCurrentThread(vm, p_env, thr_args, false);
- }
-
- static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- return JII_AttachCurrentThread(vm, p_env, thr_args, true);
- }
-
- static jint DetachCurrentThread(JavaVM* vm) {
- if (vm == nullptr || Thread::Current() == nullptr) {
- return JNI_ERR;
- }
- JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
- Runtime* runtime = raw_vm->runtime;
- runtime->DetachCurrentThread();
- return JNI_OK;
- }
-
- static jint GetEnv(JavaVM* vm, void** env, jint version) {
- // GetEnv always returns a JNIEnv* for the most current supported JNI version,
- // and unlike other calls that take a JNI version doesn't care if you supply
- // JNI_VERSION_1_1, which we don't otherwise support.
- if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
- LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
- return JNI_EVERSION;
- }
- if (vm == nullptr || env == nullptr) {
- return JNI_ERR;
- }
- Thread* thread = Thread::Current();
- if (thread == nullptr) {
- *env = nullptr;
- return JNI_EDETACHED;
- }
- *env = thread->GetJniEnv();
- return JNI_OK;
- }
-};
-
-const JNIInvokeInterface gJniInvokeInterface = {
- nullptr, // reserved0
- nullptr, // reserved1
- nullptr, // reserved2
- JII::DestroyJavaVM,
- JII::AttachCurrentThread,
- JII::DetachCurrentThread,
- JII::GetEnv,
- JII::AttachCurrentThreadAsDaemon
-};
-
-JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
- : runtime(runtime),
- check_jni_abort_hook(nullptr),
- check_jni_abort_hook_data(nullptr),
- check_jni(false),
- force_copy(false), // TODO: add a way to enable this
- trace(options->jni_trace_),
- pins_lock("JNI pin table lock", kPinTableLock),
- pin_table("pin table", kPinTableInitial, kPinTableMax),
- globals_lock("JNI global reference table lock"),
- globals(gGlobalsInitial, gGlobalsMax, kGlobal),
- libraries_lock("JNI shared libraries map lock", kLoadLibraryLock),
- libraries(new Libraries),
- weak_globals_lock_("JNI weak global reference table lock"),
- weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
- allow_new_weak_globals_(true),
- weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
- functions = unchecked_functions = &gJniInvokeInterface;
- if (options->check_jni_) {
- SetCheckJniEnabled(true);
- }
-}
-
-JavaVMExt::~JavaVMExt() {
- delete libraries;
-}
-
-jweak JavaVMExt::AddWeakGlobalReference(Thread* self, mirror::Object* obj) {
- if (obj == nullptr) {
- return nullptr;
- }
- MutexLock mu(self, weak_globals_lock_);
- while (UNLIKELY(!allow_new_weak_globals_)) {
- weak_globals_add_condition_.WaitHoldingLocks(self);
- }
- IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
- return reinterpret_cast<jweak>(ref);
-}
-
-void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
- MutexLock mu(self, weak_globals_lock_);
- if (!weak_globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
- LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
- << "failed to find entry";
- }
-}
-
-void JavaVMExt::SetCheckJniEnabled(bool enabled) {
- check_jni = enabled;
- functions = enabled ? GetCheckJniInvokeInterface() : &gJniInvokeInterface;
-}
-
-void JavaVMExt::DumpForSigQuit(std::ostream& os) {
- os << "JNI: CheckJNI is " << (check_jni ? "on" : "off");
- if (force_copy) {
- os << " (with forcecopy)";
- }
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, pins_lock);
- os << "; pins=" << pin_table.Size();
- }
- {
- ReaderMutexLock mu(self, globals_lock);
- os << "; globals=" << globals.Capacity();
- }
- {
- MutexLock mu(self, weak_globals_lock_);
- if (weak_globals_.Capacity() > 0) {
- os << " (plus " << weak_globals_.Capacity() << " weak)";
- }
- }
- os << '\n';
-
- {
- MutexLock mu(self, libraries_lock);
- os << "Libraries: " << Dumpable<Libraries>(*libraries) << " (" << libraries->size() << ")\n";
- }
-}
-
-void JavaVMExt::DisallowNewWeakGlobals() {
- MutexLock mu(Thread::Current(), weak_globals_lock_);
- allow_new_weak_globals_ = false;
-}
-
-void JavaVMExt::AllowNewWeakGlobals() {
- Thread* self = Thread::Current();
- MutexLock mu(self, weak_globals_lock_);
- allow_new_weak_globals_ = true;
- weak_globals_add_condition_.Broadcast(self);
-}
-
-mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
- MutexLock mu(self, weak_globals_lock_);
- while (UNLIKELY(!allow_new_weak_globals_)) {
- weak_globals_add_condition_.WaitHoldingLocks(self);
- }
- return weak_globals_.Get(ref);
-}
-
-void JavaVMExt::DumpReferenceTables(std::ostream& os) {
- Thread* self = Thread::Current();
- {
- ReaderMutexLock mu(self, globals_lock);
- globals.Dump(os);
- }
- {
- MutexLock mu(self, weak_globals_lock_);
- weak_globals_.Dump(os);
- }
- {
- MutexLock mu(self, pins_lock);
- pin_table.Dump(os);
- }
-}
-
-bool JavaVMExt::LoadNativeLibrary(const std::string& path,
- Handle<mirror::ClassLoader> class_loader,
- std::string* detail) {
- detail->clear();
-
- // See if we've already loaded this library. If we have, and the class loader
- // matches, return successfully without doing anything.
- // TODO: for better results we should canonicalize the pathname (or even compare
- // inodes). This implementation is fine if everybody is using System.loadLibrary.
- SharedLibrary* library;
- Thread* self = Thread::Current();
- {
- // TODO: move the locking (and more of this logic) into Libraries.
- MutexLock mu(self, libraries_lock);
- library = libraries->Get(path);
- }
- if (library != nullptr) {
- if (library->GetClassLoader() != class_loader.Get()) {
- // The library will be associated with class_loader. The JNI
- // spec says we can't load the same library into more than one
- // class loader.
- StringAppendF(detail, "Shared library \"%s\" already opened by "
- "ClassLoader %p; can't open in ClassLoader %p",
- path.c_str(), library->GetClassLoader(), class_loader.Get());
- LOG(WARNING) << detail;
- return false;
- }
- VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
- << "ClassLoader " << class_loader.Get() << "]";
- if (!library->CheckOnLoadResult()) {
- StringAppendF(detail, "JNI_OnLoad failed on a previous attempt "
- "to load \"%s\"", path.c_str());
- return false;
- }
- return true;
- }
-
- // Open the shared library. Because we're using a full path, the system
- // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
- // resolve this library's dependencies though.)
-
- // Failures here are expected when java.library.path has several entries
- // and we have to hunt for the lib.
-
- // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
- // class unloading. Libraries will only be unloaded when the reference count (incremented by
- // dlopen) becomes zero from dlclose.
-
- // This can execute slowly for a large library on a busy system, so we
- // want to switch from kRunnable while it executes. This allows the GC to ignore us.
- self->TransitionFromRunnableToSuspended(kWaitingForJniOnLoad);
- const char* path_str = path.empty() ? nullptr : path.c_str();
- void* handle = dlopen(path_str, RTLD_LAZY);
- bool needs_native_bridge = false;
- if (handle == nullptr) {
- if (NativeBridge::IsSupported(path_str)) {
- handle = NativeBridge::LoadLibrary(path_str, RTLD_LAZY);
- needs_native_bridge = true;
- }
- }
- self->TransitionFromSuspendedToRunnable();
-
- VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
-
- if (handle == nullptr) {
- *detail = dlerror();
- LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << *detail;
- return false;
- }
-
- // Create a new entry.
- // TODO: move the locking (and more of this logic) into Libraries.
- bool created_library = false;
- {
- MutexLock mu(self, libraries_lock);
- library = libraries->Get(path);
- if (library == nullptr) { // We won race to get libraries_lock
- library = new SharedLibrary(path, handle, class_loader.Get());
- libraries->Put(path, library);
- created_library = true;
- }
- }
- if (!created_library) {
- LOG(INFO) << "WOW: we lost a race to add shared library: "
- << "\"" << path << "\" ClassLoader=" << class_loader.Get();
- return library->CheckOnLoadResult();
- }
-
- VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader.Get()
- << "]";
-
- bool was_successful = false;
- void* sym = nullptr;
- if (UNLIKELY(needs_native_bridge)) {
- library->SetNeedsNativeBridge();
- sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
- } else {
- sym = dlsym(handle, "JNI_OnLoad");
- }
-
- if (sym == nullptr) {
- VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
- was_successful = true;
- } else {
- // Call JNI_OnLoad. We have to override the current class
- // loader, which will always be "null" since the stuff at the
- // top of the stack is around Runtime.loadLibrary(). (See
- // the comments in the JNI FindClass function.)
- typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
- JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
- StackHandleScope<1> hs(self);
- Handle<mirror::ClassLoader> old_class_loader(hs.NewHandle(self->GetClassLoaderOverride()));
- self->SetClassLoaderOverride(class_loader.Get());
-
- int version = 0;
- {
- ScopedThreadStateChange tsc(self, kNative);
- VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
- version = (*jni_on_load)(this, nullptr);
- }
-
- self->SetClassLoaderOverride(old_class_loader.Get());
-
- if (version == JNI_ERR) {
- StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
- } else if (IsBadJniVersion(version)) {
- StringAppendF(detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
- path.c_str(), version);
- // It's unwise to call dlclose() here, but we can mark it
- // as bad and ensure that future load attempts will fail.
- // We don't know how far JNI_OnLoad got, so there could
- // be some partially-initialized stuff accessible through
- // newly-registered native method calls. We could try to
- // unregister them, but that doesn't seem worthwhile.
- } else {
- was_successful = true;
- }
- VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
- << " from JNI_OnLoad in \"" << path << "\"]";
- }
-
- library->SetResult(was_successful);
- return was_successful;
-}
-
-void* JavaVMExt::FindCodeForNativeMethod(mirror::ArtMethod* m) {
- CHECK(m->IsNative());
- mirror::Class* c = m->GetDeclaringClass();
- // If this is a static method, it could be called before the class has been initialized.
- if (m->IsStatic()) {
- c = EnsureInitialized(Thread::Current(), c);
- if (c == nullptr) {
- return nullptr;
- }
- } else {
- CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
- }
- std::string detail;
- void* native_method;
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, libraries_lock);
- native_method = libraries->FindNativeMethod(m, detail);
- }
- // Throwing can cause libraries_lock to be reacquired.
- if (native_method == nullptr) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
- }
- return native_method;
-}
-
-void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
- MutexLock mu(Thread::Current(), weak_globals_lock_);
- for (mirror::Object** entry : weak_globals_) {
- // Since this is called by the GC, we don't need a read barrier.
- mirror::Object* obj = *entry;
- mirror::Object* new_obj = callback(obj, arg);
- if (new_obj == nullptr) {
- new_obj = kClearedJniWeakGlobal;
- }
- *entry = new_obj;
- }
-}
-
-void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
- Thread* self = Thread::Current();
- {
- ReaderMutexLock mu(self, globals_lock);
- globals.VisitRoots(callback, arg, 0, kRootJNIGlobal);
- }
- {
- MutexLock mu(self, pins_lock);
- pin_table.VisitRoots(callback, arg, 0, kRootVMInternal);
- }
- {
- MutexLock mu(self, libraries_lock);
- // Libraries contains shared libraries which hold a pointer to a class loader.
- libraries->VisitRoots(callback, arg);
- }
- // The weak_globals table is visited by the GC itself (because it mutates the table).
+const JNINativeInterface* GetJniNativeInterface() {
+ return &gJniNativeInterface;
}
void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index ac502e6..48b10f5 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -17,16 +17,8 @@
#ifndef ART_RUNTIME_JNI_INTERNAL_H_
#define ART_RUNTIME_JNI_INTERNAL_H_
-#include "jni.h"
-
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "indirect_reference_table.h"
-#include "object_callbacks.h"
-#include "reference_table.h"
-
+#include <jni.h>
#include <iosfwd>
-#include <string>
#ifndef NATIVE_METHOD
#define NATIVE_METHOD(className, functionName, signature) \
@@ -36,189 +28,18 @@
RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
namespace art {
-namespace mirror {
- class ArtField;
- class ArtMethod;
- class ClassLoader;
-} // namespace mirror
-union JValue;
-class Libraries;
-class ParsedOptions;
-class Runtime;
-class ScopedObjectAccess;
-template<class T> class Handle;
-class Thread;
-void JniAbortF(const char* jni_function_name, const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3)));
+const JNINativeInterface* GetJniNativeInterface();
+
+// Similar to RegisterNatives except its passed a descriptor for a class name and failures are
+// fatal.
void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
jint method_count);
int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause);
-class JavaVMExt : public JavaVM {
- public:
- JavaVMExt(Runtime* runtime, ParsedOptions* options);
- ~JavaVMExt();
-
- /**
- * Loads the given shared library. 'path' is an absolute pathname.
- *
- * Returns 'true' on success. On failure, sets 'detail' to a
- * human-readable description of the error.
- */
- bool LoadNativeLibrary(const std::string& path, Handle<mirror::ClassLoader> class_loader,
- std::string* detail)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- /**
- * Returns a pointer to the code for the native method 'm', found
- * using dlsym(3) on every native library that's been loaded so far.
- */
- void* FindCodeForNativeMethod(mirror::ArtMethod* m)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void DumpForSigQuit(std::ostream& os);
-
- void DumpReferenceTables(std::ostream& os)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void SetCheckJniEnabled(bool enabled);
-
- void VisitRoots(RootCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
- void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- jweak AddWeakGlobalReference(Thread* self, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DeleteWeakGlobalRef(Thread* self, jweak obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- Runtime* runtime;
-
- // Used for testing. By default, we'll LOG(FATAL) the reason.
- void (*check_jni_abort_hook)(void* data, const std::string& reason);
- void* check_jni_abort_hook_data;
-
- // Extra checking.
- bool check_jni;
- bool force_copy;
-
- // Extra diagnostics.
- std::string trace;
-
- // Used to hold references to pinned primitive arrays.
- Mutex pins_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
- ReferenceTable pin_table GUARDED_BY(pins_lock);
-
- // JNI global references.
- ReaderWriterMutex globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
- // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
- IndirectReferenceTable globals;
-
- Mutex libraries_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
- Libraries* libraries GUARDED_BY(libraries_lock);
-
- // Used by -Xcheck:jni.
- const JNIInvokeInterface* unchecked_functions;
-
- private:
- // TODO: Make the other members of this class also private.
- // JNI weak global references.
- Mutex weak_globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- // Since weak_globals_ contain weak roots, be careful not to
- // directly access the object references in it. Use Get() with the
- // read barrier enabled.
- IndirectReferenceTable weak_globals_ GUARDED_BY(weak_globals_lock_);
- bool allow_new_weak_globals_ GUARDED_BY(weak_globals_lock_);
- ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
-};
-
-struct JNIEnvExt : public JNIEnv {
- JNIEnvExt(Thread* self, JavaVMExt* vm);
- ~JNIEnvExt();
-
- void DumpReferenceTables(std::ostream& os)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void SetCheckJniEnabled(bool enabled);
-
- void PushFrame(int capacity);
- void PopFrame();
-
- template<typename T>
- T AddLocalReference(mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static Offset SegmentStateOffset();
-
- static Offset LocalRefCookieOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
- }
-
- static Offset SelfOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
- }
-
- jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- Thread* const self;
- JavaVMExt* vm;
-
- // Cookie used when using the local indirect reference table.
- uint32_t local_ref_cookie;
-
- // JNI local references.
- IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
-
- // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
- // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
- // to a native method.
- std::vector<uint32_t> stacked_local_ref_cookies;
-
- // Frequently-accessed fields cached from JavaVM.
- bool check_jni;
-
- // How many nested "critical" JNI calls are we in?
- int critical;
-
- // Entered JNI monitors, for bulk exit on thread detach.
- ReferenceTable monitors;
-
- // Used by -Xcheck:jni.
- const JNINativeInterface* unchecked_functions;
-};
-
-const JNINativeInterface* GetCheckJniNativeInterface();
-const JNIInvokeInterface* GetCheckJniInvokeInterface();
-
-// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
-// compiler
-class ScopedJniEnvLocalRefState {
- public:
- explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
- saved_local_ref_cookie_ = env->local_ref_cookie;
- env->local_ref_cookie = env->locals.GetSegmentState();
- }
-
- ~ScopedJniEnvLocalRefState() {
- env_->locals.SetSegmentState(env_->local_ref_cookie);
- env_->local_ref_cookie = saved_local_ref_cookie_;
- }
-
- private:
- JNIEnvExt* env_;
- uint32_t saved_local_ref_cookie_;
- DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
-};
-
} // namespace art
std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
+
#endif // ART_RUNTIME_JNI_INTERNAL_H_
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index da3080f..844d14a 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -17,6 +17,7 @@
#include "jni_internal.h"
#include "common_compiler_test.h"
+#include "java_vm_ext.h"
#include "mirror/art_method-inl.h"
#include "mirror/string-inl.h"
#include "scoped_thread_state_change.h"
@@ -53,24 +54,15 @@ class JniInternalTest : public CommonCompilerTest {
}
void ExpectException(jclass exception_class) {
- EXPECT_TRUE(env_->ExceptionCheck());
+ ScopedObjectAccess soa(env_);
+ EXPECT_TRUE(env_->ExceptionCheck())
+ << PrettyDescriptor(soa.Decode<mirror::Class*>(exception_class));
jthrowable exception = env_->ExceptionOccurred();
EXPECT_NE(nullptr, exception);
env_->ExceptionClear();
EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class));
}
- void ExpectClassFound(const char* name) {
- EXPECT_NE(env_->FindClass(name), nullptr) << name;
- EXPECT_FALSE(env_->ExceptionCheck()) << name;
- }
-
- void ExpectClassNotFound(const char* name) {
- EXPECT_EQ(env_->FindClass(name), nullptr) << name;
- EXPECT_TRUE(env_->ExceptionCheck()) << name;
- env_->ExceptionClear();
- }
-
void CleanUpJniEnv() {
if (aioobe_ != nullptr) {
env_->DeleteGlobalRef(aioobe_);
@@ -98,6 +90,510 @@ class JniInternalTest : public CommonCompilerTest {
return soa.AddLocalReference<jclass>(c);
}
+ void ExpectClassFound(const char* name) {
+ EXPECT_NE(env_->FindClass(name), nullptr) << name;
+ EXPECT_FALSE(env_->ExceptionCheck()) << name;
+ }
+
+ void ExpectClassNotFound(const char* name, bool check_jni, const char* check_jni_msg,
+ CheckJniAbortCatcher* abort_catcher) {
+ EXPECT_EQ(env_->FindClass(name), nullptr) << name;
+ if (!check_jni || check_jni_msg == nullptr) {
+ EXPECT_TRUE(env_->ExceptionCheck()) << name;
+ env_->ExceptionClear();
+ } else {
+ abort_catcher->Check(check_jni_msg);
+ }
+ }
+
+ void FindClassTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ // Null argument is always an abort.
+ env_->FindClass(nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+
+ // Reference types...
+ ExpectClassFound("java/lang/String");
+ // ...for arrays too, where you must include "L;".
+ ExpectClassFound("[Ljava/lang/String;");
+ // Primitive arrays are okay too, if the primitive type is valid.
+ ExpectClassFound("[C");
+
+ // But primitive types aren't allowed...
+ ExpectClassNotFound("C", check_jni, nullptr, &check_jni_abort_catcher);
+ ExpectClassNotFound("V", check_jni, nullptr, &check_jni_abort_catcher);
+ ExpectClassNotFound("K", check_jni, nullptr, &check_jni_abort_catcher);
+
+ if (check_jni) {
+ // Check JNI will reject invalid class names as aborts but without pending exceptions.
+ EXPECT_EQ(env_->FindClass("java.lang.String"), nullptr);
+ EXPECT_FALSE(env_->ExceptionCheck());
+ check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
+
+ EXPECT_EQ(env_->FindClass("[Ljava.lang.String;"), nullptr);
+ EXPECT_FALSE(env_->ExceptionCheck());
+ check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
+ } else {
+ // Without check JNI we're tolerant and replace '.' with '/'.
+ ExpectClassFound("java.lang.String");
+ ExpectClassFound("[Ljava.lang.String;");
+ }
+
+ ExpectClassNotFound("Ljava.lang.String;", check_jni, "illegal class name 'Ljava.lang.String;'",
+ &check_jni_abort_catcher);
+ ExpectClassNotFound("[java.lang.String", check_jni, "illegal class name '[java.lang.String'",
+ &check_jni_abort_catcher);
+
+ // You can't include the "L;" in a JNI class descriptor.
+ ExpectClassNotFound("Ljava/lang/String;", check_jni, "illegal class name 'Ljava/lang/String;'",
+ &check_jni_abort_catcher);
+
+ // But you must include it for an array of any reference type.
+ ExpectClassNotFound("[java/lang/String", check_jni, "illegal class name '[java/lang/String'",
+ &check_jni_abort_catcher);
+
+ ExpectClassNotFound("[K", check_jni, "illegal class name '[K'", &check_jni_abort_catcher);
+
+ // Void arrays aren't allowed.
+ ExpectClassNotFound("[V", check_jni, "illegal class name '[V'", &check_jni_abort_catcher);
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetFieldIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+
+ jfieldID fid = env_->GetFieldID(nullptr, "count", "I");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "GetFieldID received NULL jclass"
+ : "java_class == null");
+ fid = env_->GetFieldID(c, nullptr, "I");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ fid = env_->GetFieldID(c, "count", nullptr);
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetStaticFieldIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+
+ jfieldID fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "GetStaticFieldID received NULL jclass"
+ : "java_class == null");
+ fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetMethodIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jmethodID method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "GetMethodID received NULL jclass"
+ : "java_class == null");
+ jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
+ ASSERT_TRUE(jlnsme != nullptr);
+ method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ method = env_->GetMethodID(jlnsme, "<init>", nullptr);
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetStaticMethodIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jmethodID method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "GetStaticMethodID received NULL jclass"
+ : "java_class == null");
+ jclass jlstring = env_->FindClass("java/lang/String");
+ method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetFromReflectedField_ToReflectedFieldBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+ jfieldID fid = env_->GetFieldID(c, "count", "I");
+ ASSERT_NE(fid, nullptr);
+
+ // Check class argument for null argument, not checked in non-check JNI.
+ jobject field = env_->ToReflectedField(nullptr, fid, JNI_FALSE);
+ if (check_jni) {
+ EXPECT_EQ(field, nullptr);
+ check_jni_abort_catcher.Check("ToReflectedField received NULL jclass");
+ } else {
+ EXPECT_NE(field, nullptr);
+ }
+
+ field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
+ EXPECT_EQ(field, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "jfieldID was NULL"
+ : "fid == null");
+
+ fid = env_->FromReflectedField(nullptr);
+ ASSERT_EQ(fid, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "expected non-null java.lang.reflect.Field"
+ : "jlr_field == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+ jmethodID mid = env_->GetMethodID(c, "<init>", "()V");
+ ASSERT_NE(mid, nullptr);
+
+ // Check class argument for null argument, not checked in non-check JNI.
+ jobject method = env_->ToReflectedMethod(nullptr, mid, JNI_FALSE);
+ if (check_jni) {
+ EXPECT_EQ(method, nullptr);
+ check_jni_abort_catcher.Check("ToReflectedMethod received NULL jclass");
+ } else {
+ EXPECT_NE(method, nullptr);
+ }
+
+ method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
+ EXPECT_EQ(method, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "jmethodID was NULL"
+ : "mid == null");
+ mid = env_->FromReflectedMethod(method);
+ ASSERT_EQ(mid, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "expected non-null method" : "jlr_method == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void RegisterAndUnregisterNativesBadArguments(bool check_jni,
+ CheckJniAbortCatcher* check_jni_abort_catcher) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ // Passing a class of null is a failure.
+ {
+ JNINativeMethod methods[] = { };
+ EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
+ check_jni_abort_catcher->Check(check_jni ? "RegisterNatives received NULL jclass"
+ : "java_class == null");
+ }
+
+ // Passing methods as null is a failure.
+ jclass jlobject = env_->FindClass("java/lang/Object");
+ EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
+ check_jni_abort_catcher->Check("methods == null");
+
+ // Unregisters null is a failure.
+ EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
+ check_jni_abort_catcher->Check(check_jni ? "UnregisterNatives received NULL jclass"
+ : "java_class == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+
+ void GetPrimitiveArrayElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jbooleanArray array = env_->NewBooleanArray(10);
+ jboolean is_copy;
+ EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to get byte primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to get short primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to get char primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to get int primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to get long primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to get float primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to get double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
+ nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to get boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
+ nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to get boolean primitive array elements with an object of type java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jbooleanArray array = env_->NewBooleanArray(10);
+ ASSERT_TRUE(array != nullptr);
+ jboolean is_copy;
+ jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
+ ASSERT_TRUE(elements != nullptr);
+ env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
+ reinterpret_cast<jbyte*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to release byte primitive array elements with an object of type boolean[]");
+ env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
+ reinterpret_cast<jshort*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to release short primitive array elements with an object of type boolean[]");
+ env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
+ reinterpret_cast<jchar*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to release char primitive array elements with an object of type boolean[]");
+ env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
+ reinterpret_cast<jint*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to release int primitive array elements with an object of type boolean[]");
+ env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
+ reinterpret_cast<jlong*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to release long primitive array elements with an object of type boolean[]");
+ env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
+ reinterpret_cast<jfloat*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to release float primitive array elements with an object of type boolean[]");
+ env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
+ reinterpret_cast<jdouble*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to release double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to release boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
+ jni_abort_catcher.Check(
+ check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to release boolean primitive array elements with an object of type "
+ "java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetReleasePrimitiveArrayCriticalOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jobject object = env_->NewStringUTF("Test String");
+ jboolean is_copy;
+ void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "expected primitive array, given java.lang.String");
+ env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "expected primitive array, given java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+ constexpr size_t kLength = 10;
+ jbooleanArray array = env_->NewBooleanArray(kLength);
+ ASSERT_TRUE(array != nullptr);
+ jboolean elements[kLength];
+ env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+ reinterpret_cast<jbyte*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to get region of byte primitive array elements with an object of type boolean[]");
+ env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+ reinterpret_cast<jshort*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to get region of short primitive array elements with an object of type boolean[]");
+ env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+ reinterpret_cast<jchar*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to get region of char primitive array elements with an object of type boolean[]");
+ env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+ reinterpret_cast<jint*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to get region of int primitive array elements with an object of type boolean[]");
+ env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+ reinterpret_cast<jlong*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to get region of long primitive array elements with an object of type boolean[]");
+ env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+ reinterpret_cast<jfloat*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to get region of float primitive array elements with an object of type boolean[]");
+ env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+ reinterpret_cast<jdouble*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to get region of double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to get region of boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to get region of boolean primitive array elements with an object of type "
+ "java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void SetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+ constexpr size_t kLength = 10;
+ jbooleanArray array = env_->NewBooleanArray(kLength);
+ ASSERT_TRUE(array != nullptr);
+ jboolean elements[kLength];
+ env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+ reinterpret_cast<jbyte*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to set region of byte primitive array elements with an object of type boolean[]");
+ env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+ reinterpret_cast<jshort*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to set region of short primitive array elements with an object of type boolean[]");
+ env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+ reinterpret_cast<jchar*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to set region of char primitive array elements with an object of type boolean[]");
+ env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+ reinterpret_cast<jint*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to set region of int primitive array elements with an object of type boolean[]");
+ env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+ reinterpret_cast<jlong*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to set region of long primitive array elements with an object of type boolean[]");
+ env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+ reinterpret_cast<jfloat*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to set region of float primitive array elements with an object of type boolean[]");
+ env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+ reinterpret_cast<jdouble*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to set region of double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to set region of boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to set region of boolean primitive array elements with an object of type "
+ "java.lang.String");
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void NewObjectArrayBadArguments(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jclass element_class = env_->FindClass("java/lang/String");
+ ASSERT_NE(element_class, nullptr);
+
+ env_->NewObjectArray(-1, element_class, nullptr);
+ jni_abort_catcher.Check(check_jni ? "negative jsize: -1" : "negative array length: -1");
+
+ env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
+ jni_abort_catcher.Check(check_jni ? "negative jsize: -2147483648"
+ : "negative array length: -2147483648");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
JavaVMExt* vm_;
JNIEnv* env_;
jclass aioobe_;
@@ -125,48 +621,8 @@ TEST_F(JniInternalTest, GetVersion) {
}
TEST_F(JniInternalTest, FindClass) {
- // Reference types...
- ExpectClassFound("java/lang/String");
- // ...for arrays too, where you must include "L;".
- ExpectClassFound("[Ljava/lang/String;");
- // Primitive arrays are okay too, if the primitive type is valid.
- ExpectClassFound("[C");
-
- {
- CheckJniAbortCatcher check_jni_abort_catcher;
- env_->FindClass(nullptr);
- check_jni_abort_catcher.Check("name == null");
-
- // We support . as well as / for compatibility, if -Xcheck:jni is off.
- ExpectClassFound("java.lang.String");
- check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
- ExpectClassNotFound("Ljava.lang.String;");
- check_jni_abort_catcher.Check("illegal class name 'Ljava.lang.String;'");
- ExpectClassFound("[Ljava.lang.String;");
- check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
- ExpectClassNotFound("[java.lang.String");
- check_jni_abort_catcher.Check("illegal class name '[java.lang.String'");
-
- // You can't include the "L;" in a JNI class descriptor.
- ExpectClassNotFound("Ljava/lang/String;");
- check_jni_abort_catcher.Check("illegal class name 'Ljava/lang/String;'");
-
- // But you must include it for an array of any reference type.
- ExpectClassNotFound("[java/lang/String");
- check_jni_abort_catcher.Check("illegal class name '[java/lang/String'");
-
- ExpectClassNotFound("[K");
- check_jni_abort_catcher.Check("illegal class name '[K'");
-
- // Void arrays aren't allowed.
- ExpectClassNotFound("[V");
- check_jni_abort_catcher.Check("illegal class name '[V'");
- }
-
- // But primitive types aren't allowed...
- ExpectClassNotFound("C");
- ExpectClassNotFound("V");
- ExpectClassNotFound("K");
+ FindClassTest(false);
+ FindClassTest(true);
}
TEST_F(JniInternalTest, GetFieldID) {
@@ -208,16 +664,8 @@ TEST_F(JniInternalTest, GetFieldID) {
ExpectException(jlnsfe);
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- fid = env_->GetFieldID(nullptr, "count", "I");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("java_class == null");
- fid = env_->GetFieldID(c, nullptr, "I");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("name == null");
- fid = env_->GetFieldID(c, "count", nullptr);
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("sig == null");
+ GetFieldIdBadArgumentTest(false);
+ GetFieldIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, GetStaticFieldID) {
@@ -253,16 +701,8 @@ TEST_F(JniInternalTest, GetStaticFieldID) {
ExpectException(jlnsfe);
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("java_class == null");
- fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("name == null");
- fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("sig == null");
+ GetStaticFieldIdBadArgumentTest(false);
+ GetStaticFieldIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, GetMethodID) {
@@ -302,16 +742,8 @@ TEST_F(JniInternalTest, GetMethodID) {
EXPECT_FALSE(env_->ExceptionCheck());
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("java_class == null");
- method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("name == null");
- method = env_->GetMethodID(jlnsme, "<init>", nullptr);
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("sig == null");
+ GetMethodIdBadArgumentTest(false);
+ GetMethodIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, GetStaticMethodID) {
@@ -340,16 +772,8 @@ TEST_F(JniInternalTest, GetStaticMethodID) {
EXPECT_FALSE(env_->ExceptionCheck());
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("java_class == null");
- method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("name == null");
- method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("sig == null");
+ GetStaticMethodIdBadArgumentTest(false);
+ GetStaticMethodIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
@@ -370,13 +794,8 @@ TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
ASSERT_EQ(4, env_->GetIntField(s, fid2));
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
- EXPECT_EQ(field, nullptr);
- check_jni_abort_catcher.Check("fid == null");
- fid2 = env_->FromReflectedField(nullptr);
- ASSERT_EQ(fid2, nullptr);
- check_jni_abort_catcher.Check("jlr_field == null");
+ GetFromReflectedField_ToReflectedFieldBadArgumentTest(false);
+ GetFromReflectedField_ToReflectedFieldBadArgumentTest(true);
}
TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
@@ -417,13 +836,8 @@ TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
ASSERT_EQ(4, env_->CallIntMethod(s, mid2));
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
- EXPECT_EQ(method, nullptr);
- check_jni_abort_catcher.Check("mid == null");
- mid2 = env_->FromReflectedMethod(method);
- ASSERT_EQ(mid2, nullptr);
- check_jni_abort_catcher.Check("jlr_method == null");
+ GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(false);
+ GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(true);
}
static void BogusMethod() {
@@ -498,23 +912,11 @@ TEST_F(JniInternalTest, RegisterAndUnregisterNatives) {
}
EXPECT_FALSE(env_->ExceptionCheck());
- // Passing a class of null is a failure.
- {
- JNINativeMethod methods[] = { };
- EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
- check_jni_abort_catcher.Check("java_class == null");
- }
-
- // Passing methods as null is a failure.
- EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
- check_jni_abort_catcher.Check("methods == null");
-
- // Unregisters null is a failure.
- EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
- check_jni_abort_catcher.Check("java_class == null");
-
// Unregistering a class with no natives is a warning.
EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK);
+
+ RegisterAndUnregisterNativesBadArguments(false, &check_jni_abort_catcher);
+ RegisterAndUnregisterNativesBadArguments(true, &check_jni_abort_catcher);
}
#define EXPECT_PRIMITIVE_ARRAY(new_fn, \
@@ -528,6 +930,7 @@ TEST_F(JniInternalTest, RegisterAndUnregisterNatives) {
\
{ \
CheckJniAbortCatcher jni_abort_catcher; \
+ down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(false); \
/* Allocate an negative sized array and check it has the right failure type. */ \
EXPECT_EQ(env_->new_fn(-1), nullptr); \
jni_abort_catcher.Check("negative array length: -1"); \
@@ -550,6 +953,7 @@ TEST_F(JniInternalTest, RegisterAndUnregisterNatives) {
jni_abort_catcher.Check("buf == null"); \
env_->set_region_fn(a, 0, size, nullptr); \
jni_abort_catcher.Check("buf == null"); \
+ down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(true); \
} \
/* Allocate an array and check it has the right type and length. */ \
scalar_type ## Array a = env_->new_fn(size); \
@@ -654,189 +1058,28 @@ TEST_F(JniInternalTest, ShortArrays) {
}
TEST_F(JniInternalTest, GetPrimitiveArrayElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- jbooleanArray array = env_->NewBooleanArray(10);
- jboolean is_copy;
- EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get byte primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get short primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get char primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get int primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get long primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get float primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
- nullptr);
- jni_abort_catcher.Check(
- "attempt to get boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
- nullptr);
- jni_abort_catcher.Check(
- "attempt to get boolean primitive array elements with an object of type java.lang.String");
+ GetPrimitiveArrayElementsOfWrongType(false);
+ GetPrimitiveArrayElementsOfWrongType(true);
}
TEST_F(JniInternalTest, ReleasePrimitiveArrayElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- jbooleanArray array = env_->NewBooleanArray(10);
- ASSERT_TRUE(array != nullptr);
- jboolean is_copy;
- jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
- ASSERT_TRUE(elements != nullptr);
- env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
- reinterpret_cast<jbyte*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release byte primitive array elements with an object of type boolean[]");
- env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
- reinterpret_cast<jshort*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release short primitive array elements with an object of type boolean[]");
- env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
- reinterpret_cast<jchar*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release char primitive array elements with an object of type boolean[]");
- env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
- reinterpret_cast<jint*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release int primitive array elements with an object of type boolean[]");
- env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
- reinterpret_cast<jlong*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release long primitive array elements with an object of type boolean[]");
- env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
- reinterpret_cast<jfloat*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release float primitive array elements with an object of type boolean[]");
- env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
- reinterpret_cast<jdouble*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
- jni_abort_catcher.Check(
- "attempt to release boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
- jni_abort_catcher.Check(
- "attempt to release boolean primitive array elements with an object of type "
- "java.lang.String");
+ ReleasePrimitiveArrayElementsOfWrongType(false);
+ ReleasePrimitiveArrayElementsOfWrongType(true);
}
+
TEST_F(JniInternalTest, GetReleasePrimitiveArrayCriticalOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- jobject object = env_->NewStringUTF("Test String");
- jboolean is_copy;
- void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
- jni_abort_catcher.Check("expected primitive array, given java.lang.String");
- env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
- jni_abort_catcher.Check("expected primitive array, given java.lang.String");
+ GetReleasePrimitiveArrayCriticalOfWrongType(false);
+ GetReleasePrimitiveArrayCriticalOfWrongType(true);
}
TEST_F(JniInternalTest, GetPrimitiveArrayRegionElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- constexpr size_t kLength = 10;
- jbooleanArray array = env_->NewBooleanArray(kLength);
- ASSERT_TRUE(array != nullptr);
- jboolean elements[kLength];
- env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
- reinterpret_cast<jbyte*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of byte primitive array elements with an object of type boolean[]");
- env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
- reinterpret_cast<jshort*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of short primitive array elements with an object of type boolean[]");
- env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
- reinterpret_cast<jchar*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of char primitive array elements with an object of type boolean[]");
- env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
- reinterpret_cast<jint*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of int primitive array elements with an object of type boolean[]");
- env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
- reinterpret_cast<jlong*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of long primitive array elements with an object of type boolean[]");
- env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
- reinterpret_cast<jfloat*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of float primitive array elements with an object of type boolean[]");
- env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
- reinterpret_cast<jdouble*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of boolean primitive array elements with an object of type "
- "java.lang.String");
+ GetPrimitiveArrayRegionElementsOfWrongType(false);
+ GetPrimitiveArrayRegionElementsOfWrongType(true);
}
TEST_F(JniInternalTest, SetPrimitiveArrayRegionElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- constexpr size_t kLength = 10;
- jbooleanArray array = env_->NewBooleanArray(kLength);
- ASSERT_TRUE(array != nullptr);
- jboolean elements[kLength];
- env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
- reinterpret_cast<jbyte*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of byte primitive array elements with an object of type boolean[]");
- env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
- reinterpret_cast<jshort*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of short primitive array elements with an object of type boolean[]");
- env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
- reinterpret_cast<jchar*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of char primitive array elements with an object of type boolean[]");
- env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
- reinterpret_cast<jint*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of int primitive array elements with an object of type boolean[]");
- env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
- reinterpret_cast<jlong*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of long primitive array elements with an object of type boolean[]");
- env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
- reinterpret_cast<jfloat*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of float primitive array elements with an object of type boolean[]");
- env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
- reinterpret_cast<jdouble*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of boolean primitive array elements with an object of type "
- "java.lang.String");
+ SetPrimitiveArrayRegionElementsOfWrongType(false);
+ SetPrimitiveArrayRegionElementsOfWrongType(true);
}
TEST_F(JniInternalTest, NewObjectArray) {
@@ -857,12 +1100,8 @@ TEST_F(JniInternalTest, NewObjectArray) {
EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr));
// Negative array length checks.
- CheckJniAbortCatcher jni_abort_catcher;
- env_->NewObjectArray(-1, element_class, nullptr);
- jni_abort_catcher.Check("negative array length: -1");
-
- env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
- jni_abort_catcher.Check("negative array length: -2147483648");
+ NewObjectArrayBadArguments(false);
+ NewObjectArrayBadArguments(true);
}
TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) {
@@ -872,6 +1111,7 @@ TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) {
};
ASSERT_EQ(strlen(primitive_descriptors), arraysize(primitive_names));
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
CheckJniAbortCatcher jni_abort_catcher;
for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
env_->NewObjectArray(0, nullptr, nullptr);
@@ -881,6 +1121,16 @@ TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) {
std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
jni_abort_catcher.Check(error_msg.c_str());
}
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
+ env_->NewObjectArray(0, nullptr, nullptr);
+ jni_abort_catcher.Check("NewObjectArray received NULL jclass");
+ jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]);
+ env_->NewObjectArray(1, primitive_class, nullptr);
+ std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
+ jni_abort_catcher.Check(error_msg.c_str());
+ }
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) {
@@ -940,8 +1190,13 @@ TEST_F(JniInternalTest, GetSuperclass) {
// Null as class should fail.
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
jni_abort_catcher.Check("java_class == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
+ jni_abort_catcher.Check("GetSuperclass received NULL jclass");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, IsAssignableFrom) {
@@ -975,10 +1230,17 @@ TEST_F(JniInternalTest, IsAssignableFrom) {
// Null as either class should fail.
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
jni_abort_catcher.Check("java_class1 == null");
EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
jni_abort_catcher.Check("java_class2 == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
+ jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+ EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
+ jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, GetObjectRefType) {
@@ -1063,10 +1325,17 @@ TEST_F(JniInternalTest, NewStringNullCharsNonzeroLength) {
TEST_F(JniInternalTest, NewStringNegativeLength) {
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
env_->NewString(nullptr, -1);
jni_abort_catcher.Check("char_count < 0: -1");
env_->NewString(nullptr, std::numeric_limits<jint>::min());
jni_abort_catcher.Check("char_count < 0: -2147483648");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ env_->NewString(nullptr, -1);
+ jni_abort_catcher.Check("negative jsize: -1");
+ env_->NewString(nullptr, std::numeric_limits<jint>::min());
+ jni_abort_catcher.Check("negative jsize: -2147483648");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) {
@@ -1124,10 +1393,17 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) {
TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
// Passing in a nullptr jstring is ignored normally, but caught by -Xcheck:jni.
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
{
CheckJniAbortCatcher check_jni_abort_catcher;
EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
- check_jni_abort_catcher.Check("GetStringUTFChars received null jstring");
+ }
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
+ check_jni_abort_catcher.Check("GetStringUTFChars received NULL jstring");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
jstring s = env_->NewStringUTF("hello");
@@ -1222,10 +1498,17 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
// Null as array should fail.
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
jni_abort_catcher.Check("java_array == null");
env_->SetObjectArrayElement(nullptr, 0, nullptr);
jni_abort_catcher.Check("java_array == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
+ jni_abort_catcher.Check("jarray was NULL");
+ env_->SetObjectArrayElement(nullptr, 0, nullptr);
+ jni_abort_catcher.Check("jarray was NULL");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
#define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \
@@ -1237,15 +1520,28 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
env_->SetStatic ## type ## Field(c, fid, value2); \
EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \
\
+ bool old_check_jni = vm_->SetCheckJniEnabled(false); \
+ { \
+ CheckJniAbortCatcher jni_abort_catcher; \
+ env_->GetStatic ## type ## Field(nullptr, fid); \
+ env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+ } \
CheckJniAbortCatcher jni_abort_catcher; \
- env_->GetStatic ## type ## Field(nullptr, fid); \
- jni_abort_catcher.Check("received null jclass"); \
- env_->SetStatic ## type ## Field(nullptr, fid, value1); \
- jni_abort_catcher.Check("received null jclass"); \
env_->GetStatic ## type ## Field(c, nullptr); \
jni_abort_catcher.Check("fid == null"); \
env_->SetStatic ## type ## Field(c, nullptr, value1); \
jni_abort_catcher.Check("fid == null"); \
+ \
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+ env_->GetStatic ## type ## Field(nullptr, fid); \
+ jni_abort_catcher.Check("received NULL jclass"); \
+ env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+ jni_abort_catcher.Check("received NULL jclass"); \
+ env_->GetStatic ## type ## Field(c, nullptr); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ env_->SetStatic ## type ## Field(c, nullptr, value1); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
} while (false)
#define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \
@@ -1257,6 +1553,7 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
env_->Set ## type ## Field(instance, fid, value2); \
EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \
\
+ bool old_check_jni = vm_->SetCheckJniEnabled(false); \
CheckJniAbortCatcher jni_abort_catcher; \
env_->Get ## type ## Field(nullptr, fid); \
jni_abort_catcher.Check("obj == null"); \
@@ -1266,6 +1563,16 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
jni_abort_catcher.Check("fid == null"); \
env_->Set ## type ## Field(instance, nullptr, value1); \
jni_abort_catcher.Check("fid == null"); \
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+ env_->Get ## type ## Field(nullptr, fid); \
+ jni_abort_catcher.Check("field operation on NULL object:"); \
+ env_->Set ## type ## Field(nullptr, fid, value1); \
+ jni_abort_catcher.Check("field operation on NULL object:"); \
+ env_->Get ## type ## Field(instance, nullptr); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ env_->Set ## type ## Field(instance, nullptr, value1); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
} while (false)
@@ -1357,12 +1664,17 @@ TEST_F(JniInternalTest, DeleteLocalRef) {
// Currently, deleting an already-deleted reference is just a CheckJNI warning.
{
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteLocalRef(s);
+ }
CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
env_->DeleteLocalRef(s);
-
- std::string expected(StringPrintf("native code passing in reference to "
- "invalid local reference: %p", s));
+ std::string expected(StringPrintf("jobject is an invalid local reference: %p", s));
check_jni_abort_catcher.Check(expected.c_str());
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
s = env_->NewStringUTF("");
@@ -1453,12 +1765,17 @@ TEST_F(JniInternalTest, DeleteGlobalRef) {
// Currently, deleting an already-deleted reference is just a CheckJNI warning.
{
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteGlobalRef(o);
+ }
CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
env_->DeleteGlobalRef(o);
-
- std::string expected(StringPrintf("native code passing in reference to "
- "invalid global reference: %p", o));
+ std::string expected(StringPrintf("jobject is an invalid global reference: %p", o));
check_jni_abort_catcher.Check(expected.c_str());
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
jobject o1 = env_->NewGlobalRef(s);
@@ -1498,12 +1815,17 @@ TEST_F(JniInternalTest, DeleteWeakGlobalRef) {
// Currently, deleting an already-deleted reference is just a CheckJNI warning.
{
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteWeakGlobalRef(o);
+ }
CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
env_->DeleteWeakGlobalRef(o);
-
- std::string expected(StringPrintf("native code passing in reference to "
- "invalid weak global reference: %p", o));
+ std::string expected(StringPrintf("jobject is an invalid weak global reference: %p", o));
check_jni_abort_catcher.Check(expected.c_str());
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
jobject o1 = env_->NewWeakGlobalRef(s);
@@ -1522,8 +1844,6 @@ TEST_F(JniInternalTest, ExceptionDescribe) {
}
TEST_F(JniInternalTest, Throw) {
- EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
jclass exception_class = env_->FindClass("java/lang/RuntimeException");
ASSERT_TRUE(exception_class != nullptr);
jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class));
@@ -1534,11 +1854,18 @@ TEST_F(JniInternalTest, Throw) {
jthrowable thrown_exception = env_->ExceptionOccurred();
env_->ExceptionClear();
EXPECT_TRUE(env_->IsSameObject(exception, thrown_exception));
-}
-TEST_F(JniInternalTest, ThrowNew) {
+ // Bad argument.
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+ check_jni_abort_catcher.Check("Throw received NULL jthrowable");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
+}
+TEST_F(JniInternalTest, ThrowNew) {
jclass exception_class = env_->FindClass("java/lang/RuntimeException");
ASSERT_TRUE(exception_class != nullptr);
@@ -1555,6 +1882,16 @@ TEST_F(JniInternalTest, ThrowNew) {
thrown_exception = env_->ExceptionOccurred();
env_->ExceptionClear();
EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
+
+ // Bad argument.
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+ check_jni_abort_catcher.Check("c == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+ check_jni_abort_catcher.Check("ThrowNew received NULL jclass");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 1e254fa..519685a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -449,8 +449,14 @@ class MANAGED Class FINAL : public Object {
bool IsObjectClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return !IsPrimitive() && GetSuperClass() == NULL;
}
+
+ bool IsInstantiableNonArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return !IsPrimitive() && !IsInterface() && !IsAbstract() && !IsArrayClass();
+ }
+
bool IsInstantiable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (!IsPrimitive() && !IsInterface() && !IsAbstract()) || ((IsAbstract()) && IsArrayClass());
+ return (!IsPrimitive() && !IsInterface() && !IsAbstract()) ||
+ ((IsAbstract()) && IsArrayClass());
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index a7ea6c9..da3c36c 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -73,7 +73,12 @@ class ObjectTest : public CommonRuntimeTest {
}
};
-// Keep the assembly code in sync
+// Keep constants in sync.
+TEST_F(ObjectTest, Constants) {
+ EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::HeapReference<mirror::Object>));
+}
+
+// Keep the assembly code constats in sync.
TEST_F(ObjectTest, AsmConstants) {
EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value());
EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value());
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index b0b64aa..a9ef8fc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -167,7 +167,7 @@ static jboolean VMRuntime_is64Bit(JNIEnv* env, jobject) {
}
static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) {
- return Runtime::Current()->GetJavaVM()->check_jni ? JNI_TRUE : JNI_FALSE;
+ return Runtime::Current()->GetJavaVM()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
}
static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 820bd04..df6055d 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -17,6 +17,7 @@
#include <stdlib.h>
#include "debugger.h"
+#include "java_vm_ext.h"
#include "jni_internal.h"
#include "JNIHelp.h"
#include "thread-inl.h"
@@ -47,7 +48,7 @@ static void EnableDebugger() {
}
static void EnableDebugFeatures(uint32_t debug_flags) {
- // Must match values in dalvik.system.Zygote.
+ // Must match values in com.android.internal.os.Zygote.
enum {
DEBUG_ENABLE_DEBUGGER = 1,
DEBUG_ENABLE_CHECKJNI = 1 << 1,
@@ -59,7 +60,7 @@ static void EnableDebugFeatures(uint32_t debug_flags) {
if ((debug_flags & DEBUG_ENABLE_CHECKJNI) != 0) {
Runtime* runtime = Runtime::Current();
JavaVMExt* vm = runtime->GetJavaVM();
- if (!vm->check_jni) {
+ if (!vm->IsCheckJniEnabled()) {
LOG(INFO) << "Late-enabling -Xcheck:jni";
vm->SetCheckJniEnabled(true);
// There's only one thread running at this point, so only one JNIEnv to fix up.
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 51cd5b8..c1c6c26 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -15,6 +15,7 @@
*/
#include "dex_file.h"
+#include "jni_internal.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "scoped_fast_native_object_access.h"
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index fb708a2..a85eec7 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -43,7 +43,10 @@ static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
exit(status);
}
-static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) {
+static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader,
+ jstring javaLdLibraryPath) {
+ // TODO: returns NULL on success or an error message describing the failure on failure. This
+ // should be refactored in terms of suppressed exceptions.
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
@@ -64,14 +67,10 @@ static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, job
}
}
- std::string detail;
+ std::string error_msg;
{
- ScopedObjectAccess soa(env);
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> classLoader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail);
+ bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, &error_msg);
if (success) {
return nullptr;
}
@@ -79,7 +78,7 @@ static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, job
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
env->ExceptionClear();
- return env->NewStringUTF(detail.c_str());
+ return env->NewStringUTF(error_msg.c_str());
}
static jlong Runtime_maxMemory(JNIEnv*, jclass) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index 163ae20..8b2aecb 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "debugger.h"
+#include "jni_internal.h"
#include "scoped_fast_native_object_access.h"
#include "ScopedPrimitiveArray.h"
diff --git a/runtime/native_bridge.cc b/runtime/native_bridge.cc
index ad26ee4..18532e2 100644
--- a/runtime/native_bridge.cc
+++ b/runtime/native_bridge.cc
@@ -53,6 +53,17 @@ static constexpr const char* kPropEnableNativeBridge = "persist.enable.native.br
// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
+// The library name we are supposed to load.
+static std::string native_bridge_library_string = "";
+
+// Whether a native bridge is available (loaded and ready).
+static bool available = false;
+// Whether we have already initialized (or tried to).
+static bool initialized = false;
+
+struct NativeBridgeCallbacks;
+static NativeBridgeCallbacks* callbacks = nullptr;
+
// ART interfaces to native-bridge.
struct NativeBridgeArtCallbacks {
// Get shorty of a Java method. The shorty is supposed to be persistent in memory.
@@ -71,7 +82,7 @@ struct NativeBridgeArtCallbacks {
// clazz [IN] Java class object.
// Returns:
// number of native methods.
- int (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
+ uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
// Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed
// via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty.
@@ -83,7 +94,8 @@ struct NativeBridgeArtCallbacks {
// method_count [IN] max number of elements in methods.
// Returns:
// number of method it actually wrote to methods.
- int (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods, uint32_t method_count);
+ uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
+ uint32_t method_count);
};
// Native-bridge interfaces to ART
@@ -135,52 +147,62 @@ static const char* GetMethodShorty(JNIEnv* env, jmethodID mid) {
return mh.GetShorty();
}
-static int GetNativeMethodCount(JNIEnv* env, jclass clazz) {
+static uint32_t GetNativeMethodCount(JNIEnv* env, jclass clazz) {
if (clazz == nullptr)
return 0;
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(clazz);
- size_t method_count = 0;
- for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
+ uint32_t native_method_count = 0;
+ for (uint32_t i = 0; i < c->NumDirectMethods(); ++i) {
mirror::ArtMethod* m = c->GetDirectMethod(i);
- if (m->IsNative())
- method_count++;
+ if (m->IsNative()) {
+ native_method_count++;
+ }
}
- for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
+ for (uint32_t i = 0; i < c->NumVirtualMethods(); ++i) {
mirror::ArtMethod* m = c->GetVirtualMethod(i);
- if (m->IsNative())
- method_count++;
+ if (m->IsNative()) {
+ native_method_count++;
+ }
}
- return method_count;
+ return native_method_count;
}
-static int GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
- uint32_t method_count) {
- if ((clazz == nullptr) || (methods == nullptr))
+static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
+ uint32_t method_count) {
+ if ((clazz == nullptr) || (methods == nullptr)) {
return 0;
-
+ }
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(clazz);
- size_t count = 0;
- for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
+ uint32_t count = 0;
+ for (uint32_t i = 0; i < c->NumDirectMethods(); ++i) {
mirror::ArtMethod* m = c->GetDirectMethod(i);
- if (m->IsNative() && count < method_count) {
- methods[count].name = m->GetName();
- methods[count].signature = m->GetShorty();
- methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
- count++;
+ if (m->IsNative()) {
+ if (count < method_count) {
+ methods[count].name = m->GetName();
+ methods[count].signature = m->GetShorty();
+ methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
+ count++;
+ } else {
+ LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m);
+ }
}
}
- for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
+ for (uint32_t i = 0; i < c->NumVirtualMethods(); ++i) {
mirror::ArtMethod* m = c->GetVirtualMethod(i);
- if (m->IsNative() && count < method_count) {
- methods[count].name = m->GetName();
- methods[count].signature = m->GetShorty();
- methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
- count++;
+ if (m->IsNative()) {
+ if (count < method_count) {
+ methods[count].name = m->GetName();
+ methods[count].signature = m->GetShorty();
+ methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
+ count++;
+ } else {
+ LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m);
+ }
}
}
return count;
@@ -192,30 +214,32 @@ NativeBridgeArtCallbacks NativeBridgeArtItf = {
GetNativeMethods
};
-void NativeBridge::SetNativeBridgeLibraryString(std::string& native_bridge_library_string) {
- native_bridge_library_string_ = native_bridge_library_string;
+void SetNativeBridgeLibraryString(const std::string& nb_library_string) {
+ native_bridge_library_string = nb_library_string;
// TODO: when given an empty string, set initialized_ to true and available_ to false. This
// change is dependent on the property removal in Initialize().
}
-bool NativeBridge::Initialize() {
+bool NativeBridgeInitialize() {
if (!kNativeBridgeEnabled) {
return false;
}
+ // TODO: Missing annotalysis static lock ordering of DEFAULT_MUTEX_ACQUIRED, place lock into
+ // global order or remove.
+ static Mutex lock("native bridge lock");
+ MutexLock mu(Thread::Current(), lock);
- MutexLock mu(Thread::Current(), lock_);
-
- if (initialized_) {
+ if (initialized) {
// Somebody did it before.
- return available_;
+ return available;
}
- available_ = false;
+ available = false;
const char* libnb_path;
- if (!native_bridge_library_string_.empty()) {
- libnb_path = native_bridge_library_string_.c_str();
+ if (!native_bridge_library_string.empty()) {
+ libnb_path = native_bridge_library_string.c_str();
} else {
// TODO: Remove this once the frameworks side is completely implemented.
@@ -224,7 +248,7 @@ bool NativeBridge::Initialize() {
char prop_buf[PROP_VALUE_MAX];
property_get(kPropEnableNativeBridge, prop_buf, "false");
if (strcmp(prop_buf, "true") != 0) {
- initialized_ = true;
+ initialized = true;
return false;
}
@@ -237,46 +261,43 @@ bool NativeBridge::Initialize() {
void* handle = dlopen(libnb_path, RTLD_LAZY);
if (handle != nullptr) {
- callbacks_ = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
- kNativeBridgeInterfaceSymbol));
+ callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
+ kNativeBridgeInterfaceSymbol));
- if (callbacks_ != nullptr) {
- available_ = callbacks_->initialize(&NativeBridgeArtItf);
+ if (callbacks != nullptr) {
+ available = callbacks->initialize(&NativeBridgeArtItf);
}
- if (!available_) {
+ if (!available) {
dlclose(handle);
}
}
- initialized_ = true;
+ initialized = true;
- return available_;
+ return available;
}
-void* NativeBridge::LoadLibrary(const char* libpath, int flag) {
- if (Initialize())
- return callbacks_->loadLibrary(libpath, flag);
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+ if (NativeBridgeInitialize()) {
+ return callbacks->loadLibrary(libpath, flag);
+ }
return nullptr;
}
-void* NativeBridge::GetTrampoline(void* handle, const char* name, const char* shorty,
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty,
uint32_t len) {
- if (Initialize())
- return callbacks_->getTrampoline(handle, name, shorty, len);
+ if (NativeBridgeInitialize()) {
+ return callbacks->getTrampoline(handle, name, shorty, len);
+ }
return nullptr;
}
-bool NativeBridge::IsSupported(const char* libpath) {
- if (Initialize())
- return callbacks_->isSupported(libpath);
+bool NativeBridgeIsSupported(const char* libpath) {
+ if (NativeBridgeInitialize()) {
+ return callbacks->isSupported(libpath);
+ }
return false;
}
-bool NativeBridge::available_ = false;
-bool NativeBridge::initialized_ = false;
-Mutex NativeBridge::lock_("native bridge lock");
-std::string NativeBridge::native_bridge_library_string_ = "";
-NativeBridgeCallbacks* NativeBridge::callbacks_ = nullptr;
-
}; // namespace art
diff --git a/runtime/native_bridge.h b/runtime/native_bridge.h
index 3d20fe4..be647fc 100644
--- a/runtime/native_bridge.h
+++ b/runtime/native_bridge.h
@@ -17,42 +17,22 @@
#ifndef ART_RUNTIME_NATIVE_BRIDGE_H_
#define ART_RUNTIME_NATIVE_BRIDGE_H_
-#include "base/mutex.h"
-
#include <string>
namespace art {
-struct NativeBridgeCallbacks;
-
-class NativeBridge {
- public:
- // Initialize the native bridge, if any. Should be called by Runtime::Init(). An empty string
- // signals that we do not want to load a native bridge.
- static void SetNativeBridgeLibraryString(std::string& native_bridge_library_string);
-
- // Load a shared library that is supported by the native-bridge.
- static void* LoadLibrary(const char* libpath, int flag);
- // Get a native-bridge trampoline for specified native method.
- static void* GetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
- // True if native library is valid and is for an ABI that is supported by native-bridge.
- static bool IsSupported(const char* libpath);
-
- private:
- static bool Initialize();
-
- // The library name we are supposed to load.
- static std::string native_bridge_library_string_;
+// Initialize the native bridge, if any. Should be called by Runtime::Init(). An empty string
+// signals that we do not want to load a native bridge.
+void SetNativeBridgeLibraryString(const std::string& native_bridge_library_string);
- // Whether we have already initialized (or tried to).
- static bool initialized_ GUARDED_BY(lock_);
- static Mutex lock_;
+// Load a shared library that is supported by the native-bridge.
+void* NativeBridgeLoadLibrary(const char* libpath, int flag);
- // Whether a native bridge is available (loaded and ready).
- static bool available_;
+// Get a native-bridge trampoline for specified native method.
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
- static NativeBridgeCallbacks* callbacks_;
-};
+// True if native library is valid and is for an ABI that is supported by native-bridge.
+bool NativeBridgeIsSupported(const char* libpath);
}; // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 49f6585..12f9f33 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -177,6 +177,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
}
// -Xcheck:jni is off by default for regular builds but on by default in debug builds.
check_jni_ = kIsDebugBuild;
+ force_copy_ = false;
heap_initial_size_ = gc::Heap::kDefaultInitialSize;
heap_maximum_size_ = gc::Heap::kDefaultMaximumSize;
@@ -300,6 +301,8 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
}
} else if (StartsWith(option, "-Xcheck:jni")) {
check_jni_ = true;
+ } else if (StartsWith(option, "-Xjniopts:forcecopy")) {
+ force_copy_ = true;
} else if (StartsWith(option, "-Xrunjdwp:") || StartsWith(option, "-agentlib:jdwp=")) {
std::string tail(option.substr(option[1] == 'X' ? 10 : 15));
// TODO: move parsing logic out of Dbg
@@ -613,7 +616,6 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
StartsWith(option, "-Xint:") ||
StartsWith(option, "-Xdexopt:") ||
(option == "-Xnoquithandler") ||
- StartsWith(option, "-Xjniopts:") ||
StartsWith(option, "-Xjnigreflimit:") ||
(option == "-Xgenregmap") ||
(option == "-Xnogenregmap") ||
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 3dbe26f..c328ca7 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -44,6 +44,7 @@ class ParsedOptions {
std::string class_path_string_;
std::string image_;
bool check_jni_;
+ bool force_copy_;
std::string jni_trace_;
std::string native_bridge_library_string_;
CompilerCallbacks* compiler_callbacks_;
diff --git a/runtime/primitive.cc b/runtime/primitive.cc
index 16ca0fe..a639f93 100644
--- a/runtime/primitive.cc
+++ b/runtime/primitive.cc
@@ -30,6 +30,7 @@ static const char* kTypeNames[] = {
"PrimDouble",
"PrimVoid",
};
+
std::ostream& operator<<(std::ostream& os, const Primitive::Type& type) {
int32_t int_type = static_cast<int32_t>(type);
if (type >= Primitive::kPrimNot && type <= Primitive::kPrimVoid) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index b436bd2..a36e9cb 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -21,12 +21,10 @@
#include "base/logging.h"
#include "base/macros.h"
-#include "mirror/object_reference.h"
namespace art {
-namespace mirror {
-class Object;
-} // namespace mirror
+
+static constexpr size_t kObjectReferenceSize = 4;
class Primitive {
public:
@@ -79,7 +77,7 @@ class Primitive {
case kPrimFloat: return 4;
case kPrimLong:
case kPrimDouble: return 8;
- case kPrimNot: return sizeof(mirror::HeapReference<mirror::Object>);
+ case kPrimNot: return kObjectReferenceSize;
default:
LOG(FATAL) << "Invalid type " << static_cast<int>(type);
return 0;
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 758c1bb..0169ccc 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -347,7 +347,7 @@ class ArgArray {
std::unique_ptr<uint32_t[]> large_arg_array_;
};
-static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
+static void CheckMethodArguments(JavaVMExt* vm, mirror::ArtMethod* m, uint32_t* args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile::TypeList* params = m->GetParameterTypeList();
if (params == nullptr) {
@@ -375,11 +375,11 @@ static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
self->ClearException();
++error_count;
} else if (!param_type->IsPrimitive()) {
- // TODO: check primitives are in range.
// TODO: There is a compaction bug here since GetClassFromTypeIdx can cause thread suspension,
// this is a hard to fix problem since the args can contain Object*, we need to save and
// restore them by using a visitor similar to the ones used in the trampoline entrypoints.
- mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]);
+ mirror::Object* argument =
+ (reinterpret_cast<StackReference<mirror::Object>*>(&args[i + offset]))->AsMirrorPtr();
if (argument != nullptr && !argument->InstanceOf(param_type)) {
LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
<< PrettyTypeOf(argument) << " as argument " << (i + 1)
@@ -388,13 +388,40 @@ static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
}
} else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) {
offset++;
+ } else {
+ int32_t arg = static_cast<int32_t>(args[i + offset]);
+ if (param_type->IsPrimitiveBoolean()) {
+ if (arg != JNI_TRUE && arg != JNI_FALSE) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jboolean (0/1) but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ } else if (param_type->IsPrimitiveByte()) {
+ if (arg < -128 || arg > 127) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jbyte but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ } else if (param_type->IsPrimitiveChar()) {
+ if (args[i + offset] > 0xFFFF) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jchar but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ } else if (param_type->IsPrimitiveShort()) {
+ if (arg < -32768 || arg > 0x7FFF) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jshort but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ }
}
}
- if (error_count > 0) {
+ if (UNLIKELY(error_count > 0)) {
// TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort
// with an argument.
- JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
- PrettyMethod(h_m.Get()).c_str());
+ vm->JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
+ PrettyMethod(h_m.Get()).c_str());
}
}
@@ -411,7 +438,7 @@ static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t* args = arg_array->GetArray();
if (UNLIKELY(soa.Env()->check_jni)) {
- CheckMethodArguments(method, args);
+ CheckMethodArguments(soa.Vm(), method, args);
}
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
}
@@ -567,11 +594,6 @@ bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
return true;
}
-static std::string PrettyDescriptor(Primitive::Type type) {
- std::string descriptor_string(Primitive::Descriptor(type));
- return PrettyDescriptor(descriptor_string);
-}
-
bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
Primitive::Type srcType, Primitive::Type dstType,
const JValue& src, JValue* dst) {
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 2c54c06..61370c6 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_REFLECTION_H_
#define ART_RUNTIME_REFLECTION_H_
+#include "base/mutex.h"
#include "jni.h"
#include "primitive.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e0c0d63..d677729 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -339,7 +339,7 @@ jobject CreateSystemClassLoader() {
ScopedObjectAccess soa(Thread::Current());
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- StackHandleScope<3> hs(soa.Self());
+ StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> class_loader_class(
hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader)));
CHECK(cl->EnsureInitialized(class_loader_class, true, true));
@@ -349,15 +349,12 @@ jobject CreateSystemClassLoader() {
CHECK(getSystemClassLoader != NULL);
JValue result = InvokeWithJValues(soa, nullptr, soa.EncodeMethod(getSystemClassLoader), nullptr);
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(down_cast<mirror::ClassLoader*>(result.GetL())));
- CHECK(class_loader.Get() != nullptr);
JNIEnv* env = soa.Self()->GetJniEnv();
ScopedLocalRef<jobject> system_class_loader(env,
- soa.AddLocalReference<jobject>(class_loader.Get()));
+ soa.AddLocalReference<jobject>(result.GetL()));
CHECK(system_class_loader.get() != nullptr);
- soa.Self()->SetClassLoaderOverride(class_loader.Get());
+ soa.Self()->SetClassLoaderOverride(system_class_loader.get());
Handle<mirror::Class> thread_class(
hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread)));
@@ -368,7 +365,8 @@ jobject CreateSystemClassLoader() {
CHECK(contextClassLoader != NULL);
// We can't run in a transaction yet.
- contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), class_loader.Get());
+ contextClassLoader->SetObject<false>(soa.Self()->GetPeer(),
+ soa.Decode<mirror::ClassLoader*>(system_class_loader.get()));
return env->NewGlobalRef(system_class_loader.get());
}
@@ -708,7 +706,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
self->ClearException();
// Look for a native bridge.
- NativeBridge::SetNativeBridgeLibraryString(options->native_bridge_library_string_);
+ SetNativeBridgeLibraryString(options->native_bridge_library_string_);
VLOG(startup) << "Runtime::Init exiting";
return true;
@@ -736,13 +734,9 @@ void Runtime::InitNativeMethods() {
{
std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
std::string reason;
- self->TransitionFromSuspendedToRunnable();
- StackHandleScope<1> hs(self);
- auto class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
- if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, class_loader, &reason)) {
+ if (!instance_->java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
}
- self->TransitionFromRunnableToSuspended(kNative);
}
// Initialize well known classes that may invoke runtime native methods.
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 23aca45..ae3eaf2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -18,7 +18,8 @@
#define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
#include "base/casts.h"
-#include "jni_internal-inl.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext-inl.h"
#include "read_barrier.h"
#include "thread-inl.h"
#include "verify_object.h"
@@ -114,6 +115,10 @@ class ScopedObjectAccessAlreadyRunnable {
return vm_;
}
+ bool ForceCopy() const {
+ return vm_->ForceCopy();
+ }
+
/*
* Add a local reference for an object to the indirect reference table associated with the
* current stack frame. When the native function returns, the reference will be discarded.
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index a5caa07..bd399e7 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -24,7 +24,7 @@
#include "base/casts.h"
#include "base/mutex-inl.h"
#include "gc/heap.h"
-#include "jni_internal.h"
+#include "jni_env_ext.h"
namespace art {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 18e28ea..8e6da74 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1163,6 +1163,21 @@ void Thread::Destroy() {
Thread* self = this;
DCHECK_EQ(self, Thread::Current());
+ if (tlsPtr_.jni_env != nullptr) {
+ // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
+ tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+ // Release locally held global references which releasing may require the mutator lock.
+ if (tlsPtr_.jpeer != nullptr) {
+ // If pthread_create fails we don't have a jni env here.
+ tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
+ tlsPtr_.jpeer = nullptr;
+ }
+ if (tlsPtr_.class_loader_override != nullptr) {
+ tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.class_loader_override);
+ tlsPtr_.class_loader_override = nullptr;
+ }
+ }
+
if (tlsPtr_.opeer != nullptr) {
ScopedObjectAccess soa(self);
// We may need to call user-supplied managed code, do this before final clean-up.
@@ -1190,22 +1205,16 @@ void Thread::Destroy() {
ObjectLock<mirror::Object> locker(self, h_obj);
locker.NotifyAll();
}
+ tlsPtr_.opeer = nullptr;
}
- // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
- if (tlsPtr_.jni_env != nullptr) {
- tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
- }
+ Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
}
Thread::~Thread() {
- if (tlsPtr_.jni_env != nullptr && tlsPtr_.jpeer != nullptr) {
- // If pthread_create fails we don't have a jni env here.
- tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
- tlsPtr_.jpeer = nullptr;
- }
- tlsPtr_.opeer = nullptr;
-
+ CHECK(tlsPtr_.class_loader_override == nullptr);
+ CHECK(tlsPtr_.jpeer == nullptr);
+ CHECK(tlsPtr_.opeer == nullptr);
bool initialized = (tlsPtr_.jni_env != nullptr); // Did Thread::Init run?
if (initialized) {
delete tlsPtr_.jni_env;
@@ -1237,7 +1246,7 @@ Thread::~Thread() {
delete tlsPtr_.name;
delete tlsPtr_.stack_trace_sample;
- Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+ Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
TearDownAlternateSignalStack();
}
@@ -1347,11 +1356,10 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const {
result = kInvalidIndirectRefObject;
}
} else if (kind == kGlobal) {
- JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
- result = vm->globals.SynchronizedGet(const_cast<Thread*>(this), &vm->globals_lock, ref);
+ result = tlsPtr_.jni_env->vm->DecodeGlobal(const_cast<Thread*>(this), ref);
} else {
DCHECK_EQ(kind, kWeakGlobal);
- result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
+ result = tlsPtr_.jni_env->vm->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
if (result == kClearedJniWeakGlobal) {
// This is a special case where it's okay to return nullptr.
return nullptr;
@@ -1359,7 +1367,8 @@ mirror::Object* Thread::DecodeJObject(jobject obj) const {
}
if (UNLIKELY(result == nullptr)) {
- JniAbortF(nullptr, "use of deleted %s %p", ToStr<IndirectRefKind>(kind).c_str(), obj);
+ tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p",
+ ToStr<IndirectRefKind>(kind).c_str(), obj);
}
return result;
}
@@ -1399,6 +1408,13 @@ void Thread::NotifyLocked(Thread* self) {
}
}
+void Thread::SetClassLoaderOverride(jobject class_loader_override) {
+ if (tlsPtr_.class_loader_override != nullptr) {
+ GetJniEnv()->DeleteGlobalRef(tlsPtr_.class_loader_override);
+ }
+ tlsPtr_.class_loader_override = GetJniEnv()->NewGlobalRef(class_loader_override);
+}
+
class CountStackDepthVisitor : public StackVisitor {
public:
explicit CountStackDepthVisitor(Thread* thread)
@@ -2173,11 +2189,6 @@ class RootCallbackVisitor {
const uint32_t tid_;
};
-void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) {
- VerifyObject(class_loader_override);
- tlsPtr_.class_loader_override = class_loader_override;
-}
-
void Thread::VisitRoots(RootCallback* visitor, void* arg) {
uint32_t thread_id = GetThreadId();
if (tlsPtr_.opeer != nullptr) {
@@ -2187,10 +2198,6 @@ void Thread::VisitRoots(RootCallback* visitor, void* arg) {
visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
}
tlsPtr_.throw_location.VisitRoots(visitor, arg);
- if (tlsPtr_.class_loader_override != nullptr) {
- visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.class_loader_override), arg, thread_id,
- kRootNativeStack);
- }
if (tlsPtr_.monitor_enter_object != nullptr) {
visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 3e9372f..c2b200b 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -431,12 +431,11 @@ class Thread {
tlsPtr_.wait_next = next;
}
- mirror::ClassLoader* GetClassLoaderOverride() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ jobject GetClassLoaderOverride() {
return tlsPtr_.class_loader_override;
}
- void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetClassLoaderOverride(jobject class_loader_override);
// Create the internal representation of a stack trace, that is more time
// and space efficient to compute than the StackTraceElement[].
@@ -1040,7 +1039,7 @@ class Thread {
// Needed to get the right ClassLoader in JNI_OnLoad, but also
// useful for testing.
- mirror::ClassLoader* class_loader_override;
+ jobject class_loader_override;
// Thread local, lazily allocated, long jump context. Used to deliver exceptions.
Context* long_jump_context;
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 7cf26e6..5077a89 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -801,6 +801,8 @@ void ThreadList::Register(Thread* self) {
void ThreadList::Unregister(Thread* self) {
DCHECK_EQ(self, Thread::Current());
+ CHECK_NE(self->GetState(), kRunnable);
+ Locks::mutator_lock_->AssertNotHeld(self);
VLOG(threads) << "ThreadList::Unregister() " << *self;
@@ -815,14 +817,18 @@ void ThreadList::Unregister(Thread* self) {
// Note: deliberately not using MutexLock that could hold a stale self pointer.
Locks::thread_list_lock_->ExclusiveLock(self);
CHECK(Contains(self));
- // Note: we don't take the thread_suspend_count_lock_ here as to be suspending a thread other
- // than yourself you need to hold the thread_list_lock_ (see Thread::ModifySuspendCount).
+ Locks::thread_suspend_count_lock_->ExclusiveLock(self);
+ bool removed = false;
if (!self->IsSuspended()) {
list_.remove(self);
+ removed = true;
+ }
+ Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
+ Locks::thread_list_lock_->ExclusiveUnlock(self);
+ if (removed) {
delete self;
self = nullptr;
}
- Locks::thread_list_lock_->ExclusiveUnlock(self);
}
// Release the thread ID after the thread is finished and deleted to avoid cases where we can
// temporarily have multiple threads with the same thread id. When this occurs, it causes
diff --git a/runtime/utils.cc b/runtime/utils.cc
index f966fbd..48d6cdf 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -281,6 +281,11 @@ std::string PrettyDescriptor(const std::string& descriptor) {
return result;
}
+std::string PrettyDescriptor(Primitive::Type type) {
+ std::string descriptor_string(Primitive::Descriptor(type));
+ return PrettyDescriptor(descriptor_string);
+}
+
std::string PrettyField(mirror::ArtField* f, bool with_type) {
if (f == NULL) {
return "null";
diff --git a/runtime/utils.h b/runtime/utils.h
index 49bcbf9..f6773be 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -24,13 +24,10 @@
#include <vector>
#include "base/logging.h"
+#include "base/mutex.h"
#include "globals.h"
#include "instruction_set.h"
-#include "base/mutex.h"
-
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
+#include "primitive.h"
namespace art {
@@ -279,6 +276,7 @@ std::string PrettyDescriptor(mirror::String* descriptor)
std::string PrettyDescriptor(const std::string& descriptor);
std::string PrettyDescriptor(mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+std::string PrettyDescriptor(Primitive::Type type);
// Returns a human-readable signature for 'f'. Something like "a.b.C.f" or
// "int a.b.C.f" (depending on the value of 'with_type').
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index d508fb5..1682d4e 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -25,6 +25,7 @@
#include "jni.h"
#include "base/macros.h"
+#include "base/mutex.h"
#include "gc_root.h"
#include "globals.h"
#include "object_callbacks.h"