summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.common_build.mk9
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc40
-rw-r--r--compiler/utils/mips64/assembler_mips64.h14
-rw-r--r--runtime/check_jni.cc6
-rw-r--r--runtime/class_linker.h1
-rw-r--r--runtime/entrypoints/quick/quick_jni_entrypoints.cc3
-rw-r--r--runtime/jni_env_ext.cc129
-rw-r--r--runtime/jni_env_ext.h21
-rw-r--r--runtime/jni_internal_test.cc87
-rw-r--r--runtime/read_barrier_c.h10
-rw-r--r--test/088-monitor-verification/smali/NotStructuredOverUnlock.smali2
-rw-r--r--test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali2
-rw-r--r--test/088-monitor-verification/smali/OK.smali6
-rw-r--r--test/088-monitor-verification/smali/TooDeep.smali2
-rw-r--r--test/088-monitor-verification/smali/UnbalancedJoin.smali2
-rw-r--r--test/088-monitor-verification/smali/UnbalancedStraight.smali2
-rw-r--r--test/088-monitor-verification/src/Main.java8
-rw-r--r--test/449-checker-bce/expected.txt1
-rw-r--r--test/449-checker-bce/src/Main.java126
-rw-r--r--test/common/runtime_state.cc50
-rw-r--r--test/common/stack_inspect.cc35
21 files changed, 481 insertions, 75 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 288bdddfe7..b50712429e 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -300,9 +300,17 @@ ifeq ($(ART_HEAP_POISONING),true)
art_asflags += -DART_HEAP_POISONING=1
endif
+#
+# Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
+# The default is BAKER.
+#
+ART_READ_BARRIER_TYPE ?= BAKER
+
ifeq ($(ART_USE_READ_BARRIER),true)
art_cflags += -DART_USE_READ_BARRIER=1
+ art_cflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
art_asflags += -DART_USE_READ_BARRIER=1
+ art_asflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
endif
ifeq ($(ART_USE_TLAB),true)
@@ -392,7 +400,6 @@ art_debug_cflags :=
art_non_debug_cflags :=
art_host_non_debug_cflags :=
art_target_non_debug_cflags :=
-art_default_gc_type :=
art_default_gc_type_cflags :=
ART_HOST_LDLIBS :=
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 1b4d1614f1..b60905d682 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -404,6 +404,29 @@ static void GenMinMax(LocationSummary* locations,
GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ // Some architectures, such as ARM and MIPS (prior to r6), have a
+ // conditional move instruction which only changes the target
+ // (output) register if the condition is true (MIPS prior to r6 had
+ // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
+ // change the target (output) register. If the condition is true the
+ // output register gets the contents of the "rs" register; otherwise,
+ // the output register is set to zero. One consequence of this is
+ // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
+ // needs to use a pair of SELEQZ/SELNEZ instructions. After
+ // executing this pair of instructions one of the output registers
+ // from the pair will necessarily contain zero. Then the code ORs the
+ // output registers from the SELEQZ/SELNEZ instructions to get the
+ // final result.
+ //
+ // The initial test to see if the output register is same as the
+ // first input register is needed to make sure that value in the
+ // first input register isn't clobbered before we've finished
+ // computing the output value. The logic in the corresponding else
+ // clause performs the same task but makes sure the second input
+ // register isn't clobbered in the event that it's the same register
+ // as the output register; the else clause also handles the case
+ // where the output register is distinct from both the first, and the
+ // second input registers.
if (out == lhs) {
__ Slt(AT, rhs, lhs);
if (is_min) {
@@ -512,13 +535,12 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathFloor(HInvoke* invoke) {
CreateFPToFP(arena_, invoke);
}
-// 0x200 - +zero
-// 0x040 - +infinity
-// 0x020 - -zero
-// 0x004 - -infinity
-// 0x002 - quiet NaN
-// 0x001 - signaling NaN
-const constexpr uint16_t CLASS_MASK = 0x267;
+const constexpr uint16_t kFPLeaveUnchanged = kPositiveZero |
+ kPositiveInfinity |
+ kNegativeZero |
+ kNegativeInfinity |
+ kQuietNaN |
+ kSignalingNaN;
void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
LocationSummary* locations = invoke->GetLocations();
@@ -534,7 +556,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
// }
__ ClassD(out, in);
__ Dmfc1(AT, out);
- __ Andi(AT, AT, CLASS_MASK); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
+ __ Andi(AT, AT, kFPLeaveUnchanged); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
__ MovD(out, in);
__ Bnezc(AT, &done);
@@ -583,7 +605,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathCeil(HInvoke* invoke) {
// }
__ ClassD(out, in);
__ Dmfc1(AT, out);
- __ Andi(AT, AT, CLASS_MASK); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
+ __ Andi(AT, AT, kFPLeaveUnchanged); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
__ MovD(out, in);
__ Bnezc(AT, &done);
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index c170313728..d083eb4306 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -46,6 +46,20 @@ enum StoreOperandType {
kStoreDoubleword
};
+// Used to test the values returned by ClassS/ClassD.
+enum FPClassMaskType {
+ kSignalingNaN = 0x001,
+ kQuietNaN = 0x002,
+ kNegativeInfinity = 0x004,
+ kNegativeNormal = 0x008,
+ kNegativeSubnormal = 0x010,
+ kNegativeZero = 0x020,
+ kPositiveInfinity = 0x040,
+ kPositiveNormal = 0x080,
+ kPositiveSubnormal = 0x100,
+ kPositiveZero = 0x200,
+};
+
class Mips64Assembler FINAL : public Assembler {
public:
Mips64Assembler() {}
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index b6ad5473ff..beabce36fb 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -2463,6 +2463,9 @@ class CheckJNI {
ScopedCheck sc(kFlag_Default, __FUNCTION__);
JniValueType args[2] = {{.E = env}, {.L = obj}};
if (sc.Check(soa, true, "EL", args)) {
+ if (obj != nullptr) {
+ down_cast<JNIEnvExt*>(env)->RecordMonitorEnter(obj);
+ }
JniValueType result;
result.i = baseEnv(env)->MonitorEnter(env, obj);
if (sc.Check(soa, false, "i", &result)) {
@@ -2477,6 +2480,9 @@ class CheckJNI {
ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
JniValueType args[2] = {{.E = env}, {.L = obj}};
if (sc.Check(soa, true, "EL", args)) {
+ if (obj != nullptr) {
+ down_cast<JNIEnvExt*>(env)->CheckMonitorRelease(obj);
+ }
JniValueType result;
result.i = baseEnv(env)->MonitorExit(env, obj);
if (sc.Check(soa, false, "i", &result)) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 739403f6c9..7f3e93806e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -883,6 +883,7 @@ class ClassLinker {
friend class ImageWriter; // for GetClassRoots
friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub
+ friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub
ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache
DISALLOW_COPY_AND_ASSIGN(ClassLinker);
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index fc5c52e75a..58f256a191 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -65,6 +65,9 @@ static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
JNIEnvExt* env = self->GetJniEnv();
+ if (UNLIKELY(env->check_jni)) {
+ env->CheckNoHeldMonitors();
+ }
env->locals.SetSegmentState(env->local_ref_cookie);
env->local_ref_cookie = saved_local_ref_cookie;
self->PopHandleScope();
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index b18b430403..4104d7a0e8 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -16,10 +16,17 @@
#include "jni_env_ext.h"
+#include <algorithm>
+#include <vector>
+
#include "check_jni.h"
#include "indirect_reference_table.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
+#include "lock_word.h"
+#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
+#include "thread-inl.h"
namespace art {
@@ -63,14 +70,14 @@ JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in)
JNIEnvExt::~JNIEnvExt() {
}
-jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) {
if (obj == nullptr) {
return nullptr;
}
return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
}
-void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::DeleteLocalRef(jobject obj) {
if (obj != nullptr) {
locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
}
@@ -86,14 +93,14 @@ void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
monitors.Dump(os);
}
-void JNIEnvExt::PushFrame(int capacity) SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::PushFrame(int capacity) {
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_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::PopFrame() {
locals.SetSegmentState(local_ref_cookie);
local_ref_cookie = stacked_local_ref_cookies.back();
stacked_local_ref_cookies.pop_back();
@@ -104,4 +111,118 @@ Offset JNIEnvExt::SegmentStateOffset() {
IndirectReferenceTable::SegmentStateOffset().Int32Value());
}
+// Use some defining part of the caller's frame as the identifying mark for the JNI segment.
+static uintptr_t GetJavaCallFrame(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) {
+ NthCallerVisitor zeroth_caller(self, 0, false);
+ zeroth_caller.WalkStack();
+ if (zeroth_caller.caller == nullptr) {
+ // No Java code, must be from pure native code.
+ return 0;
+ } else if (zeroth_caller.GetCurrentQuickFrame() == nullptr) {
+ // Shadow frame = interpreter. Use the actual shadow frame's address.
+ DCHECK(zeroth_caller.GetCurrentShadowFrame() != nullptr);
+ return reinterpret_cast<uintptr_t>(zeroth_caller.GetCurrentShadowFrame());
+ } else {
+ // Quick frame = compiled code. Use the bottom of the frame.
+ return reinterpret_cast<uintptr_t>(zeroth_caller.GetCurrentQuickFrame());
+ }
+}
+
+void JNIEnvExt::RecordMonitorEnter(jobject obj) {
+ locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self), obj));
+}
+
+static std::string ComputeMonitorDescription(Thread* self,
+ jobject obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* o = self->DecodeJObject(obj);
+ if ((o->GetLockWord(false).GetState() == LockWord::kThinLocked) &&
+ Locks::mutator_lock_->IsExclusiveHeld(self)) {
+ // Getting the identity hashcode here would result in lock inflation and suspension of the
+ // current thread, which isn't safe if this is the only runnable thread.
+ return StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)",
+ reinterpret_cast<intptr_t>(o),
+ PrettyTypeOf(o).c_str());
+ } else {
+ // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So
+ // we get the pretty type before we call IdentityHashCode.
+ const std::string pretty_type(PrettyTypeOf(o));
+ return StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str());
+ }
+}
+
+static void RemoveMonitors(Thread* self,
+ uintptr_t frame,
+ ReferenceTable* monitors,
+ std::vector<std::pair<uintptr_t, jobject>>* locked_objects)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ auto kept_end = std::remove_if(
+ locked_objects->begin(),
+ locked_objects->end(),
+ [self, frame, monitors](const std::pair<uintptr_t, jobject>& pair)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (frame == pair.first) {
+ mirror::Object* o = self->DecodeJObject(pair.second);
+ monitors->Remove(o);
+ return true;
+ }
+ return false;
+ });
+ locked_objects->erase(kept_end, locked_objects->end());
+}
+
+void JNIEnvExt::CheckMonitorRelease(jobject obj) {
+ uintptr_t current_frame = GetJavaCallFrame(self);
+ std::pair<uintptr_t, jobject> exact_pair = std::make_pair(current_frame, obj);
+ auto it = std::find(locked_objects_.begin(), locked_objects_.end(), exact_pair);
+ bool will_abort = false;
+ if (it != locked_objects_.end()) {
+ locked_objects_.erase(it);
+ } else {
+ // Check whether this monitor was locked in another JNI "session."
+ mirror::Object* mirror_obj = self->DecodeJObject(obj);
+ for (std::pair<uintptr_t, jobject>& pair : locked_objects_) {
+ if (self->DecodeJObject(pair.second) == mirror_obj) {
+ std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
+ vm->JniAbortF("<JNI MonitorExit>",
+ "Unlocking monitor that wasn't locked here: %s",
+ monitor_descr.c_str());
+ will_abort = true;
+ break;
+ }
+ }
+ }
+
+ // When we abort, also make sure that any locks from the current "session" are removed from
+ // the monitors table, otherwise we may visit local objects in GC during abort (which won't be
+ // valid anymore).
+ if (will_abort) {
+ RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+ }
+}
+
+void JNIEnvExt::CheckNoHeldMonitors() {
+ uintptr_t current_frame = GetJavaCallFrame(self);
+ // The locked_objects_ are grouped by their stack frame component, as this enforces structured
+ // locking, and the groups form a stack. So the current frame entries are at the end. Check
+ // whether the vector is empty, and when there are elements, whether the last element belongs
+ // to this call - this signals that there are unlocked monitors.
+ if (!locked_objects_.empty()) {
+ std::pair<uintptr_t, jobject>& pair = locked_objects_[locked_objects_.size() - 1];
+ if (pair.first == current_frame) {
+ std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
+ vm->JniAbortF("<JNI End>",
+ "Still holding a locked object on JNI end: %s",
+ monitor_descr.c_str());
+ // When we abort, also make sure that any locks from the current "session" are removed from
+ // the monitors table, otherwise we may visit local objects in GC during abort.
+ RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+ } else if (kIsDebugBuild) {
+ // Make sure there are really no other entries and our checking worked as expected.
+ for (std::pair<uintptr_t, jobject>& check_pair : locked_objects_) {
+ CHECK_NE(check_pair.first, current_frame);
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 9b55536e98..3828ff045d 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,8 +43,8 @@ struct JNIEnvExt : public JNIEnv {
void SetCheckJniEnabled(bool enabled);
- void PushFrame(int capacity);
- void PopFrame();
+ void PushFrame(int capacity) SHARED_REQUIRES(Locks::mutator_lock_);
+ void PopFrame() SHARED_REQUIRES(Locks::mutator_lock_);
template<typename T>
T AddLocalReference(mirror::Object* obj)
@@ -89,10 +89,27 @@ struct JNIEnvExt : public JNIEnv {
// Used by -Xcheck:jni.
const JNINativeInterface* unchecked_functions;
+ // Functions to keep track of monitor lock and unlock operations. Used to ensure proper locking
+ // rules in CheckJNI mode.
+
+ // Record locking of a monitor.
+ void RecordMonitorEnter(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Check the release, that is, that the release is performed in the same JNI "segment."
+ void CheckMonitorRelease(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Check that no monitors are held that have been acquired in this JNI "segment."
+ void CheckNoHeldMonitors() SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
// The constructor should not be called directly. It may leave the object in an erronuous state,
// and the result needs to be checked.
JNIEnvExt(Thread* self, JavaVMExt* vm);
+
+ // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
+ // to ensure that only monitors locked in this native frame are being unlocked, and that at
+ // the end all are unlocked.
+ std::vector<std::pair<uintptr_t, jobject>> locked_objects_;
};
// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 2a0cb28f0c..41b368ec32 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -607,11 +607,64 @@ class JniInternalTest : public CommonCompilerTest {
EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
}
+ void SetUpForTest(bool direct, const char* method_name, const char* method_sig,
+ void* native_fnptr) {
+ // Initialize class loader and set generic JNI entrypoint.
+ // Note: this code is adapted from the jni_compiler_test, and taken with minimal modifications.
+ if (!runtime_->IsStarted()) {
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ class_loader_ = LoadDex("MyClassNatives");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
+ mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
+ const auto pointer_size = class_linker_->GetImagePointerSize();
+ ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) :
+ c->FindVirtualMethod(method_name, method_sig, pointer_size);
+ ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
+ method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
+ }
+ // Start runtime.
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ CHECK(started);
+ }
+ // JNI operations after runtime start.
+ env_ = Thread::Current()->GetJniEnv();
+ jklass_ = env_->FindClass("MyClassNatives");
+ ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig;
+
+ if (direct) {
+ jmethod_ = env_->GetStaticMethodID(jklass_, method_name, method_sig);
+ } else {
+ jmethod_ = env_->GetMethodID(jklass_, method_name, method_sig);
+ }
+ ASSERT_TRUE(jmethod_ != nullptr) << method_name << " " << method_sig;
+
+ if (native_fnptr != nullptr) {
+ JNINativeMethod methods[] = { { method_name, method_sig, native_fnptr } };
+ ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1))
+ << method_name << " " << method_sig;
+ } else {
+ env_->UnregisterNatives(jklass_);
+ }
+
+ jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
+ jobj_ = env_->NewObject(jklass_, constructor);
+ ASSERT_TRUE(jobj_ != nullptr) << method_name << " " << method_sig;
+ }
+
JavaVMExt* vm_;
JNIEnv* env_;
jclass aioobe_;
jclass ase_;
jclass sioobe_;
+
+ jclass jklass_;
+ jobject jobj_;
+ jobject class_loader_;
+ jmethodID jmethod_;
};
TEST_F(JniInternalTest, AllocObject) {
@@ -2111,4 +2164,38 @@ TEST_F(JniInternalTest, MonitorEnterExit) {
}
}
+void Java_MyClassNatives_foo_exit(JNIEnv* env, jobject thisObj) {
+ // Release the monitor on self. This should trigger an abort.
+ env->MonitorExit(thisObj);
+}
+
+TEST_F(JniInternalTest, MonitorExitLockedInDifferentCall) {
+ SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo_exit));
+ ASSERT_NE(jobj_, nullptr);
+
+ env_->MonitorEnter(jobj_);
+ EXPECT_FALSE(env_->ExceptionCheck());
+
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+ check_jni_abort_catcher.Check("Unlocking monitor that wasn't locked here");
+}
+
+void Java_MyClassNatives_foo_enter_no_exit(JNIEnv* env, jobject thisObj) {
+ // Acquire but don't release the monitor on self. This should trigger an abort on return.
+ env->MonitorEnter(thisObj);
+}
+
+TEST_F(JniInternalTest, MonitorExitNotAllUnlocked) {
+ SetUpForTest(false,
+ "foo",
+ "()V",
+ reinterpret_cast<void*>(&Java_MyClassNatives_foo_enter_no_exit));
+ ASSERT_NE(jobj_, nullptr);
+
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+ check_jni_abort_catcher.Check("Still holding a locked object on JNI end");
+}
+
} // namespace art
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 710c21f03e..8e5b1872f2 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -26,10 +26,16 @@
// table-lookup read barriers.
#ifdef ART_USE_READ_BARRIER
+#if ART_READ_BARRIER_TYPE_IS_BAKER
#define USE_BAKER_READ_BARRIER
-// #define USE_BROOKS_READ_BARRIER
-// #define USE_TABLE_LOOKUP_READ_BARRIER
+#elif ART_READ_BARRIER_TYPE_IS_BROOKS
+#define USE_BROOKS_READ_BARRIER
+#elif ART_READ_BARRIER_TYPE_IS_TABLELOOKUP
+#define USE_TABLE_LOOKUP_READ_BARRIER
+#else
+#error "ART read barrier type must be set"
#endif
+#endif // ART_USE_READ_BARRIER
#ifdef ART_HEAP_POISONING
#define USE_HEAP_POISONING
diff --git a/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali b/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
index aa0c2d5a13..0dc492f2b3 100644
--- a/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
+++ b/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
# Lock twice, but unlock thrice.
diff --git a/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali b/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
index 2c31fdaa85..df6e168685 100644
--- a/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
+++ b/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
# Lock thrice, but only unlock twice.
diff --git a/test/088-monitor-verification/smali/OK.smali b/test/088-monitor-verification/smali/OK.smali
index 596798d80c..a43ecb0704 100644
--- a/test/088-monitor-verification/smali/OK.smali
+++ b/test/088-monitor-verification/smali/OK.smali
@@ -20,7 +20,7 @@
.method public static runNoMonitors(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsManaged()V
+ invoke-static {}, LMain;->assertIsManaged()V
return-void
@@ -29,7 +29,7 @@
.method public static runStraightLine(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsManaged()V
+ invoke-static {}, LMain;->assertIsManaged()V
monitor-enter v1 # 1
monitor-enter v2 # 2
@@ -44,7 +44,7 @@
.method public static runBalancedJoin(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsManaged()V
+ invoke-static {}, LMain;->assertIsManaged()V
monitor-enter v1 # 1
diff --git a/test/088-monitor-verification/smali/TooDeep.smali b/test/088-monitor-verification/smali/TooDeep.smali
index 1a8f2f06e8..a1e328148d 100644
--- a/test/088-monitor-verification/smali/TooDeep.smali
+++ b/test/088-monitor-verification/smali/TooDeep.smali
@@ -7,7 +7,7 @@
# Lock depth is 33, which is more than the verifier supports. This should have been punted to
# the interpreter.
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
monitor-enter v2 # 1
monitor-enter v2 # 2
diff --git a/test/088-monitor-verification/smali/UnbalancedJoin.smali b/test/088-monitor-verification/smali/UnbalancedJoin.smali
index da8f7732af..993f32c022 100644
--- a/test/088-monitor-verification/smali/UnbalancedJoin.smali
+++ b/test/088-monitor-verification/smali/UnbalancedJoin.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
if-eqz v2, :Lnull
diff --git a/test/088-monitor-verification/smali/UnbalancedStraight.smali b/test/088-monitor-verification/smali/UnbalancedStraight.smali
index 68edb6c783..cbb8bcc488 100644
--- a/test/088-monitor-verification/smali/UnbalancedStraight.smali
+++ b/test/088-monitor-verification/smali/UnbalancedStraight.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
monitor-enter v1 # 1
monitor-enter v2 # 2
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index fc5755b06f..218805543e 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -220,7 +220,7 @@ public class Main {
// Smali testing code.
private static void runSmaliTests() {
- if (!hasOatFile() || runtimeIsSoftFail() || isCallerInterpreted()) {
+ if (!hasOatFile() || runtimeIsSoftFail() || isInterpreted()) {
// Skip test, this seems to be a non-compiled code test configuration.
return;
}
@@ -277,9 +277,9 @@ public class Main {
}
// Helpers for the smali code.
- public static native void assertCallerIsInterpreted();
- public static native void assertCallerIsManaged();
+ public static native void assertIsInterpreted();
+ public static native void assertIsManaged();
public static native boolean hasOatFile();
public static native boolean runtimeIsSoftFail();
- public static native boolean isCallerInterpreted();
+ public static native boolean isInterpreted();
}
diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt
index e114c50371..4665d7af8b 100644
--- a/test/449-checker-bce/expected.txt
+++ b/test/449-checker-bce/expected.txt
@@ -1 +1,2 @@
+JNI_OnLoad called
java.lang.ArrayIndexOutOfBoundsException: length=5; index=82
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index f06c250dc7..22829cddc8 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -265,6 +265,7 @@ public class Main {
// A helper into which the actual throwing function should be inlined.
static void constantIndexingForward6(int[] array) {
+ assertIsManaged();
constantIndexing6(array);
}
@@ -618,13 +619,17 @@ public class Main {
static int foo() {
try {
+ assertIsManaged();
// This will cause AIOOBE.
constantIndexing2(new int[3]);
} catch (ArrayIndexOutOfBoundsException e) {
+ assertIsManaged(); // This is to ensure that single-frame deoptimization works.
+ // Will need to be updated if constantIndexing2 is inlined.
try {
// This will cause AIOOBE.
constantIndexingForward6(new int[3]);
} catch (ArrayIndexOutOfBoundsException e2) {
+ assertIsManaged();
return 99;
}
}
@@ -634,13 +639,13 @@ public class Main {
int sum;
- /// CHECK-START: void Main.foo1(int[], int, int) BCE (before)
+ /// CHECK-START: void Main.foo1(int[], int, int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo1(int[], int, int) BCE (after)
+ /// CHECK-START: void Main.foo1(int[], int, int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -657,25 +662,30 @@ public class Main {
/// CHECK: Phi
/// CHECK: Goto
- void foo1(int[] array, int start, int end) {
+ void foo1(int[] array, int start, int end, boolean expectInterpreter) {
// Three HDeoptimize will be added. One for
// start >= 0, one for end <= array.length,
// and one for null check on array (to hoist null
// check and array.length out of loop).
for (int i = start ; i < end; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = 1;
sum += array[i];
}
}
- /// CHECK-START: void Main.foo2(int[], int, int) BCE (before)
+ /// CHECK-START: void Main.foo2(int[], int, int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo2(int[], int, int) BCE (after)
+ /// CHECK-START: void Main.foo2(int[], int, int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -692,25 +702,30 @@ public class Main {
/// CHECK: Phi
/// CHECK: Goto
- void foo2(int[] array, int start, int end) {
+ void foo2(int[] array, int start, int end, boolean expectInterpreter) {
// Three HDeoptimize will be added. One for
// start >= 0, one for end <= array.length,
// and one for null check on array (to hoist null
// check and array.length out of loop).
for (int i = start ; i <= end; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = 1;
sum += array[i];
}
}
- /// CHECK-START: void Main.foo3(int[], int) BCE (before)
+ /// CHECK-START: void Main.foo3(int[], int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo3(int[], int) BCE (after)
+ /// CHECK-START: void Main.foo3(int[], int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -726,24 +741,29 @@ public class Main {
/// CHECK: Phi
/// CHECK: Goto
- void foo3(int[] array, int end) {
+ void foo3(int[] array, int end, boolean expectInterpreter) {
// Two HDeoptimize will be added. One for end < array.length,
// and one for null check on array (to hoist null check
// and array.length out of loop).
for (int i = 3 ; i <= end; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = 1;
sum += array[i];
}
}
- /// CHECK-START: void Main.foo4(int[], int) BCE (before)
+ /// CHECK-START: void Main.foo4(int[], int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo4(int[], int) BCE (after)
+ /// CHECK-START: void Main.foo4(int[], int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -759,18 +779,23 @@ public class Main {
/// CHECK: Phi
/// CHECK: Goto
- void foo4(int[] array, int end) {
+ void foo4(int[] array, int end, boolean expectInterpreter) {
// Two HDeoptimize will be added. One for end <= array.length,
// and one for null check on array (to hoist null check
// and array.length out of loop).
for (int i = end ; i > 0; i--) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i - 1] = 1;
sum += array[i - 1];
}
}
- /// CHECK-START: void Main.foo5(int[], int) BCE (before)
+ /// CHECK-START: void Main.foo5(int[], int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK: BoundsCheck
@@ -780,7 +805,7 @@ public class Main {
/// CHECK: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo5(int[], int) BCE (after)
+ /// CHECK-START: void Main.foo5(int[], int, boolean) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
/// CHECK: Phi
@@ -800,7 +825,7 @@ public class Main {
/// CHECK-NOT: Phi
/// CHECK: Goto
- void foo5(int[] array, int end) {
+ void foo5(int[] array, int end, boolean expectInterpreter) {
// Bounds check in this loop can be eliminated without deoptimization.
for (int i = array.length - 1 ; i >= 0; i--) {
array[i] = 1;
@@ -808,6 +833,11 @@ public class Main {
// One HDeoptimize will be added.
// It's for (end - 2 <= array.length - 2).
for (int i = end - 2 ; i > 0; i--) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
sum += array[i - 1];
sum += array[i];
sum += array[i + 1];
@@ -815,7 +845,7 @@ public class Main {
}
- /// CHECK-START: void Main.foo6(int[], int, int) BCE (before)
+ /// CHECK-START: void Main.foo6(int[], int, int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArrayGet
/// CHECK: BoundsCheck
@@ -829,7 +859,7 @@ public class Main {
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
- /// CHECK-START: void Main.foo6(int[], int, int) BCE (after)
+ /// CHECK-START: void Main.foo6(int[], int, int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
@@ -855,12 +885,17 @@ public class Main {
/// CHECK: Goto
/// CHECK-NOT: Deoptimize
- void foo6(int[] array, int start, int end) {
+ void foo6(int[] array, int start, int end, boolean expectInterpreter) {
// Three HDeoptimize will be added. One for
// start >= 2, one for end <= array.length - 3,
// and one for null check on array (to hoist null
// check and array.length out of loop).
for (int i = end; i >= start; i--) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = (array[i-2] + array[i-1] + array[i] + array[i+1] + array[i+2]) / 5;
}
}
@@ -948,12 +983,12 @@ public class Main {
}
- /// CHECK-START: void Main.foo9(int[]) BCE (before)
+ /// CHECK-START: void Main.foo9(int[], boolean) BCE (before)
/// CHECK: NullCheck
/// CHECK: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo9(int[]) BCE (after)
+ /// CHECK-START: void Main.foo9(int[], boolean) BCE (after)
// The loop is guaranteed to be entered. No need to transform the
// loop for loop body entry test.
/// CHECK: Deoptimize
@@ -964,10 +999,15 @@ public class Main {
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- void foo9(int[] array) {
+ void foo9(int[] array, boolean expectInterpreter) {
// Two HDeoptimize will be added. One for
// 10 <= array.length, and one for null check on array.
for (int i = 0 ; i < 10; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
sum += array[i];
}
}
@@ -999,7 +1039,7 @@ public class Main {
static void testUnknownBounds() {
boolean caught = false;
Main main = new Main();
- main.foo1(new int[10], 0, 10);
+ main.foo1(new int[10], 0, 10, false);
if (main.sum != 10) {
System.out.println("foo1 failed!");
}
@@ -1007,7 +1047,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo1(new int[10], 0, 11);
+ main.foo1(new int[10], 0, 11, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1016,7 +1056,7 @@ public class Main {
}
main = new Main();
- main.foo2(new int[10], 0, 9);
+ main.foo2(new int[10], 0, 9, false);
if (main.sum != 10) {
System.out.println("foo2 failed!");
}
@@ -1024,7 +1064,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo2(new int[10], 0, 10);
+ main.foo2(new int[10], 0, 10, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1033,7 +1073,7 @@ public class Main {
}
main = new Main();
- main.foo3(new int[10], 9);
+ main.foo3(new int[10], 9, false);
if (main.sum != 7) {
System.out.println("foo3 failed!");
}
@@ -1041,7 +1081,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo3(new int[10], 10);
+ main.foo3(new int[10], 10, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1050,7 +1090,7 @@ public class Main {
}
main = new Main();
- main.foo4(new int[10], 10);
+ main.foo4(new int[10], 10, false);
if (main.sum != 10) {
System.out.println("foo4 failed!");
}
@@ -1058,7 +1098,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo4(new int[10], 11);
+ main.foo4(new int[10], 11, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1067,7 +1107,7 @@ public class Main {
}
main = new Main();
- main.foo5(new int[10], 10);
+ main.foo5(new int[10], 10, false);
if (main.sum != 24) {
System.out.println("foo5 failed!");
}
@@ -1075,7 +1115,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo5(new int[10], 11);
+ main.foo5(new int[10], 11, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1084,11 +1124,11 @@ public class Main {
}
main = new Main();
- main.foo6(new int[10], 2, 7);
+ main.foo6(new int[10], 2, 7, false);
main = new Main();
int[] array9 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- main.foo9(array9);
+ main.foo9(array9, false);
if (main.sum != 45) {
System.out.println("foo9 failed!");
}
@@ -1104,7 +1144,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo6(new int[10], 2, 8);
+ main.foo6(new int[10], 2, 8, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1115,7 +1155,7 @@ public class Main {
caught = false;
main = new Main();
try {
- main.foo6(new int[10], 1, 7);
+ main.foo6(new int[10], 1, 7, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1152,6 +1192,15 @@ public class Main {
/// CHECK: ParallelMove
public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ if (!compiledWithOptimizing() ||
+ !hasOatFile() ||
+ runtimeIsSoftFail() ||
+ isInterpreted()) {
+ disableStackFrameAsserts();
+ }
+
sieve(20);
int[] array = {5, 2, 3, 7, 0, 1, 6, 4};
@@ -1190,4 +1239,11 @@ public class Main {
new Main().testExceptionMessage();
}
+ public static native boolean compiledWithOptimizing();
+ public static native void disableStackFrameAsserts();
+ public static native void assertIsManaged();
+ public static native void assertIsInterpreted();
+ public static native boolean hasOatFile();
+ public static native boolean runtimeIsSoftFail();
+ public static native boolean isInterpreted();
}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 042b03bfb6..082c9b3c8d 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -66,4 +66,54 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isImageDex2OatEnabled(JNIEnv* en
return Runtime::Current()->IsImageDex2OatEnabled();
}
+// public static native boolean compiledWithOptimizing();
+// Did we use the optimizing compiler to compile this?
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* env, jclass cls) {
+ ScopedObjectAccess soa(env);
+
+ mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+ const DexFile& dex_file = klass->GetDexFile();
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
+ if (oat_dex_file == nullptr) {
+ // Could be JIT, which also uses optimizing, but conservatively say no.
+ return JNI_FALSE;
+ }
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ CHECK(oat_file != nullptr);
+
+ const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+ CHECK(cmd_line != nullptr); // Huh? This should not happen.
+
+ // Check the backend.
+ constexpr const char* kCompilerBackend = "--compiler-backend=";
+ const char* backend = strstr(cmd_line, kCompilerBackend);
+ if (backend != nullptr) {
+ // If it's set, make sure it's optimizing.
+ backend += strlen(kCompilerBackend);
+ if (strncmp(backend, "Optimizing", strlen("Optimizing")) != 0) {
+ return JNI_FALSE;
+ }
+ }
+
+ // Check the filter.
+ constexpr const char* kCompilerFilter = "--compiler-filter=";
+ const char* filter = strstr(cmd_line, kCompilerFilter);
+ if (filter != nullptr) {
+ // If it's set, make sure it's not interpret-only|verify-none|verify-at-runtime.
+ // Note: The space filter might have an impact on the test, but ignore that for now.
+ filter += strlen(kCompilerFilter);
+ constexpr const char* kInterpretOnly = "interpret-only";
+ constexpr const char* kVerifyNone = "verify-none";
+ constexpr const char* kVerifyAtRuntime = "verify-at-runtime";
+ if (strncmp(filter, kInterpretOnly, strlen(kInterpretOnly)) == 0 ||
+ strncmp(filter, kVerifyNone, strlen(kVerifyNone)) == 0 ||
+ strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0) {
+ return JNI_FALSE;
+ }
+ }
+
+ return JNI_TRUE;
+}
+
} // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index d22cf52882..922eae61e2 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -27,9 +27,20 @@
namespace art {
-// public static native boolean isCallerInterpreted();
+static bool asserts_enabled = true;
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerInterpreted(JNIEnv* env, jclass) {
+// public static native void disableStackFrameAsserts();
+// Note: to globally disable asserts in unsupported configurations.
+
+extern "C" JNIEXPORT void JNICALL Java_Main_disableStackFrameAsserts(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass cls ATTRIBUTE_UNUSED) {
+ asserts_enabled = false;
+}
+
+
+// public static native boolean isInterpreted();
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpreted(JNIEnv* env, jclass) {
ScopedObjectAccess soa(env);
NthCallerVisitor caller(soa.Self(), 1, false);
caller.WalkStack();
@@ -37,16 +48,18 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerInterpreted(JNIEnv* env,
return caller.GetCurrentShadowFrame() != nullptr ? JNI_TRUE : JNI_FALSE;
}
-// public static native void assertCallerIsInterpreted();
+// public static native void assertIsInterpreted();
-extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsInterpreted(JNIEnv* env, jclass klass) {
- CHECK(Java_Main_isCallerInterpreted(env, klass));
+extern "C" JNIEXPORT void JNICALL Java_Main_assertIsInterpreted(JNIEnv* env, jclass klass) {
+ if (asserts_enabled) {
+ CHECK(Java_Main_isInterpreted(env, klass));
+ }
}
-// public static native boolean isCallerManaged();
+// public static native boolean isManaged();
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerManaged(JNIEnv* env, jclass cls) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isManaged(JNIEnv* env, jclass cls) {
ScopedObjectAccess soa(env);
mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
@@ -65,10 +78,12 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerManaged(JNIEnv* env, jcl
return caller.GetCurrentShadowFrame() != nullptr ? JNI_FALSE : JNI_TRUE;
}
-// public static native void assertCallerIsManaged();
+// public static native void assertIsManaged();
-extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsManaged(JNIEnv* env, jclass cls) {
- CHECK(Java_Main_isCallerManaged(env, cls));
+extern "C" JNIEXPORT void JNICALL Java_Main_assertIsManaged(JNIEnv* env, jclass cls) {
+ if (asserts_enabled) {
+ CHECK(Java_Main_isManaged(env, cls));
+ }
}
} // namespace art