Merge "Revert "Revert "Revert "(dl)Close native libraries on unload""""
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 6703695..eb9c381 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -206,6 +206,22 @@
return instruction;
}
+static bool UpdateDominatorOfSuccessor(HBasicBlock* block, HBasicBlock* successor) {
+ DCHECK(ContainsElement(block->GetSuccessors(), successor));
+
+ HBasicBlock* old_dominator = successor->GetDominator();
+ HBasicBlock* new_dominator =
+ (old_dominator == nullptr) ? block
+ : CommonDominator::ForPair(old_dominator, block);
+
+ if (old_dominator == new_dominator) {
+ return false;
+ } else {
+ successor->SetDominator(new_dominator);
+ return true;
+ }
+}
+
void HGraph::ComputeDominanceInformation() {
DCHECK(reverse_post_order_.empty());
reverse_post_order_.reserve(blocks_.size());
@@ -228,15 +244,7 @@
worklist.pop_back();
} else {
HBasicBlock* successor = current->GetSuccessors()[successors_visited[current_id]++];
-
- if (successor->GetDominator() == nullptr) {
- successor->SetDominator(current);
- } else {
- // The CommonDominator can work for multiple blocks as long as the
- // domination information doesn't change. However, since we're changing
- // that information here, we can use the finder only for pairs of blocks.
- successor->SetDominator(CommonDominator::ForPair(successor->GetDominator(), current));
- }
+ UpdateDominatorOfSuccessor(current, successor);
// Once all the forward edges have been visited, we know the immediate
// dominator of the block. We can then start visiting its successors.
@@ -248,6 +256,44 @@
}
}
+ // Check if the graph has back edges not dominated by their respective headers.
+ // If so, we need to update the dominators of those headers and recursively of
+ // their successors. We do that with a fix-point iteration over all blocks.
+ // The algorithm is guaranteed to terminate because it loops only if the sum
+ // of all dominator chains has decreased in the current iteration.
+ bool must_run_fix_point = false;
+ for (HBasicBlock* block : blocks_) {
+ if (block != nullptr &&
+ block->IsLoopHeader() &&
+ block->GetLoopInformation()->HasBackEdgeNotDominatedByHeader()) {
+ must_run_fix_point = true;
+ break;
+ }
+ }
+ if (must_run_fix_point) {
+ bool update_occurred = true;
+ while (update_occurred) {
+ update_occurred = false;
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HBasicBlock* successor : block->GetSuccessors()) {
+ update_occurred |= UpdateDominatorOfSuccessor(block, successor);
+ }
+ }
+ }
+ }
+
+ // Make sure that there are no remaining blocks whose dominator information
+ // needs to be updated.
+ if (kIsDebugBuild) {
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HBasicBlock* successor : block->GetSuccessors()) {
+ DCHECK(!UpdateDominatorOfSuccessor(block, successor));
+ }
+ }
+ }
+
// Populate `dominated_blocks_` information after computing all dominators.
// The potential presence of irreducible loops requires to do it after.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
@@ -595,20 +641,16 @@
blocks_.SetBit(header_->GetBlockId());
header_->SetInLoop(this);
- bool is_irreducible_loop = false;
- for (HBasicBlock* back_edge : GetBackEdges()) {
- DCHECK(back_edge->GetDominator() != nullptr);
- if (!header_->Dominates(back_edge)) {
- is_irreducible_loop = true;
- break;
- }
- }
+ bool is_irreducible_loop = HasBackEdgeNotDominatedByHeader();
if (is_irreducible_loop) {
ArenaBitVector visited(graph->GetArena(),
graph->GetBlocks().size(),
/* expandable */ false,
kArenaAllocGraphBuilder);
+ // Stop marking blocks at the loop header.
+ visited.SetBit(header_->GetBlockId());
+
for (HBasicBlock* back_edge : GetBackEdges()) {
PopulateIrreducibleRecursive(back_edge, &visited);
}
@@ -667,6 +709,16 @@
return last_position;
}
+bool HLoopInformation::HasBackEdgeNotDominatedByHeader() const {
+ for (HBasicBlock* back_edge : GetBackEdges()) {
+ DCHECK(back_edge->GetDominator() != nullptr);
+ if (!header_->Dominates(back_edge)) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool HBasicBlock::Dominates(HBasicBlock* other) const {
// Walk up the dominator tree from `other`, to find out if `this`
// is an ancestor.
@@ -2396,6 +2448,7 @@
}
if (!NeedsEnvironment()) {
RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index afb995d..829fe71 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -725,6 +725,8 @@
blocks_.ClearAllBits();
}
+ bool HasBackEdgeNotDominatedByHeader() const;
+
private:
// Internal recursive implementation of `Populate`.
void PopulateRecursive(HBasicBlock* block);
@@ -5530,6 +5532,7 @@
SetPackedFlag<kFlagIsInDexCache>(true);
DCHECK(!NeedsEnvironment());
RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
}
size_t InputCount() const OVERRIDE {
diff --git a/profman/profman.cc b/profman/profman.cc
index 3e632bc..b3454fa 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -216,7 +216,11 @@
}
void LogCompletionTime() {
- LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_);
+ static constexpr uint64_t kLogThresholdTime = MsToNs(100); // 100ms
+ uint64_t time_taken = NanoTime() - start_ns_;
+ if (time_taken > kLogThresholdTime) {
+ LOG(WARNING) << "profman took " << PrettyDuration(NanoTime() - start_ns_);
+ }
}
std::vector<std::string> profile_files_;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f58af5a..5bdb36c 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -418,26 +418,6 @@
(*icu_cleanup_fn)();
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
-
- // Manually closing the JNI libraries.
- // Runtime does not support repeatedly doing JNI->CreateVM, thus we need to manually clean up the
- // dynamic linking loader so that gtests would not fail.
- // Bug: 25785594
- if (runtime_->IsStarted()) {
- {
- // We retrieve the handle by calling dlopen on the library. To close it, we need to call
- // dlclose twice, the first time to undo our dlopen and the second time to actually unload it.
- // See man dlopen.
- void* handle = dlopen("libjavacore.so", RTLD_LAZY);
- dlclose(handle);
- CHECK_EQ(0, dlclose(handle));
- }
- {
- void* handle = dlopen("libopenjdkd.so", RTLD_LAZY);
- dlclose(handle);
- CHECK_EQ(0, dlclose(handle));
- }
- }
}
static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 3df4e98..c504074 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2358,7 +2358,8 @@
static std::string GetStringOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t string_idx) {
- if (header->string_ids_size_ < string_idx) {
+ // The `string_idx` is not guaranteed to be valid yet.
+ if (header->string_ids_size_ <= string_idx) {
return "(error)";
}
@@ -2375,9 +2376,11 @@
static std::string GetClassOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t class_idx) {
- if (header->type_ids_size_ < class_idx) {
- return "(error)";
- }
+ // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and
+ // it has already been checked in `DexFileVerifier::CheckClassDataItemField()`
+ // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match
+ // a valid defining class.
+ CHECK_LT(class_idx, header->type_ids_size_);
const DexFile::TypeId* type_id =
reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx;
@@ -2390,9 +2393,8 @@
static std::string GetFieldDescriptionOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t idx) {
- if (header->field_ids_size_ < idx) {
- return "(error)";
- }
+ // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemField()`.
+ CHECK_LT(idx, header->field_ids_size_);
const DexFile::FieldId* field_id =
reinterpret_cast<const DexFile::FieldId*>(begin + header->field_ids_off_) + idx;
@@ -2408,9 +2410,8 @@
static std::string GetMethodDescriptionOrError(const uint8_t* const begin,
const DexFile::Header* const header,
uint32_t idx) {
- if (header->method_ids_size_ < idx) {
- return "(error)";
- }
+ // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemMethod()`.
+ CHECK_LT(idx, header->method_ids_size_);
const DexFile::MethodId* method_id =
reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + idx;
@@ -2608,7 +2609,13 @@
*error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
}
// Check that static and private methods, as well as constructors, are in the direct methods list,
@@ -2662,7 +2669,13 @@
*error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
+ if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
if ((method_access_flags & kAccAbstract) != 0) {
// Abstract methods are not allowed to have the following flags.
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 344d186..177a373 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -57,7 +57,14 @@
255, 255, 255, 255
};
-static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+// Make the Dex file version 37.
+static void MakeDexVersion37(DexFile* dex_file) {
+ size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
+ CHECK_EQ(*(dex_file->Begin() + offset), '5');
+ *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
+}
+
+static inline std::unique_ptr<uint8_t[]> DecodeBase64(const char* src, size_t* dst_size) {
std::vector<uint8_t> tmp;
uint32_t t = 0, y = 0;
int g = 3;
@@ -100,7 +107,7 @@
*dst_size = 0;
}
std::copy(tmp.begin(), tmp.end(), dst.get());
- return dst.release();
+ return dst;
}
static void FixUpChecksum(uint8_t* dex_file) {
@@ -113,25 +120,18 @@
header->checksum_ = adler_checksum;
}
-// Custom deleter. Necessary to clean up the memory we use (to be able to mutate).
-struct DexFileDeleter {
- void operator()(DexFile* in) {
- if (in != nullptr) {
- delete[] in->Begin();
- delete in;
- }
- }
-};
-
-using DexFileUniquePtr = std::unique_ptr<DexFile, DexFileDeleter>;
-
class DexFileVerifierTest : public CommonRuntimeTest {
protected:
void VerifyModification(const char* dex_file_base64_content,
const char* location,
std::function<void(DexFile*)> f,
const char* expected_error) {
- DexFileUniquePtr dex_file(WrapAsDexFile(dex_file_base64_content));
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(dex_file_base64_content, &length);
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(
+ new DexFile(dex_bytes.get(), length, "tmp", 0, nullptr, nullptr));
f(dex_file.get());
FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
@@ -150,15 +150,6 @@
}
}
}
-
- private:
- static DexFile* WrapAsDexFile(const char* dex_file_content_in_base_64) {
- // Decode base64.
- size_t length;
- uint8_t* dex_bytes = DecodeBase64(dex_file_content_in_base_64, &length);
- CHECK(dex_bytes != nullptr);
- return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
- }
};
static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
@@ -290,7 +281,9 @@
// Find the method data for the first method with the given name (from class 0). Note: the pointer
// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
// delta.
-static const uint8_t* FindMethodData(const DexFile* dex_file, const char* name) {
+static const uint8_t* FindMethodData(const DexFile* dex_file,
+ const char* name,
+ /*out*/ uint32_t* method_idx = nullptr) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
const uint8_t* class_data = dex_file->GetClassData(class_def);
@@ -316,6 +309,9 @@
const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
const char* str = dex_file->GetStringData(string_id);
if (strcmp(name, str) == 0) {
+ if (method_idx != nullptr) {
+ *method_idx = method_index;
+ }
DecodeUnsignedLeb128(&trailing);
return trailing;
}
@@ -449,6 +445,7 @@
kMethodFlagsTestDex,
"method_flags_constructor_native_nocode",
[&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
@@ -461,6 +458,7 @@
kMethodFlagsTestDex,
"method_flags_constructor_abstract_nocode",
[&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
@@ -521,6 +519,7 @@
kMethodFlagsTestDex,
"init_not_allowed_flags",
[&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
@@ -683,6 +682,22 @@
}
}
+TEST_F(DexFileVerifierTest, B28552165) {
+ // Regression test for bad error string retrieval in different situations.
+ // Using invalid access flags to trigger the error.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "b28552165",
+ [](DexFile* dex_file) {
+ OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected);
+ uint32_t method_idx;
+ FindMethodData(dex_file, "foo", &method_idx);
+ auto* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(method_idx));
+ method_id->name_idx_ = dex_file->NumStringIds();
+ },
+ "Method may have only one of public/protected/private, LMethodFlags;.(error)");
+}
+
// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
// just easier to break up bad cases.
@@ -725,13 +740,6 @@
return result;
}
-// Make the Dex file version 37.
-static void MakeDexVersion37(DexFile* dex_file) {
- size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
- CHECK_EQ(*(dex_file->Begin() + offset), '5');
- *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
-}
-
TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
VerifyModification(
kMethodFlagsInterface,
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 81a396a..6c630cc 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -515,8 +515,24 @@
// instruction, as it already executed.
// TODO: should be tested more once b/17586779 is fixed.
const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
- DCHECK(instr->IsInvoke());
- new_dex_pc = dex_pc + instr->SizeInCodeUnits();
+ if (instr->IsInvoke()) {
+ new_dex_pc = dex_pc + instr->SizeInCodeUnits();
+ } else if (instr->Opcode() == Instruction::NEW_INSTANCE) {
+ // It's possible to deoptimize at a NEW_INSTANCE dex instruciton that's for a
+ // java string, which is turned into a call into StringFactory.newEmptyString();
+ if (kIsDebugBuild) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::Class* klass = class_linker->ResolveType(
+ instr->VRegB_21c(), shadow_frame->GetMethod());
+ DCHECK(klass->IsStringClass());
+ }
+ // Skip the dex instruction since we essentially come back from an invocation.
+ new_dex_pc = dex_pc + instr->SizeInCodeUnits();
+ } else {
+ DCHECK(false) << "Unexpected instruction opcode " << instr->Opcode()
+ << " at dex_pc " << dex_pc
+ << " of method: " << PrettyMethod(shadow_frame->GetMethod(), false);
+ }
} else {
// Nothing to do, the dex_pc is the one at which the code requested
// the deoptimization.
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index 2bdf8d1..8619ff7 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-#include <assert.h>
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <vector>
+#include "art_method-inl.h"
+#include "base/logging.h"
#include "jni.h"
-#if defined(NDEBUG)
-#error test code compiled without NDEBUG
-#endif
+namespace art {
static JavaVM* jvm = nullptr;
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void*) {
- assert(vm != nullptr);
- assert(jvm == nullptr);
+ CHECK(vm != nullptr);
+ CHECK(jvm == nullptr);
jvm = vm;
std::cout << "JNI_OnLoad called" << std::endl;
return JNI_VERSION_1_6;
@@ -39,24 +38,24 @@
extern "C" JNIEXPORT void JNI_OnUnload(JavaVM*, void*) {
// std::cout since LOG(INFO) adds extra stuff like pid.
std::cout << "JNI_OnUnload called" << std::endl;
- // Clear jvm for assert in test 004-JniTest.
+ // Clear jvm for CHECK in test 004-JniTest.
jvm = nullptr;
}
static void* AttachHelper(void* arg) {
- assert(jvm != nullptr);
+ CHECK(jvm != nullptr);
JNIEnv* env = nullptr;
JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, nullptr };
int attach_result = jvm->AttachCurrentThread(&env, &args);
- assert(attach_result == 0);
+ CHECK_EQ(attach_result, 0);
typedef void (*Fn)(JNIEnv*);
Fn fn = reinterpret_cast<Fn>(arg);
fn(env);
int detach_result = jvm->DetachCurrentThread();
- assert(detach_result == 0);
+ CHECK_EQ(detach_result, 0);
return nullptr;
}
@@ -64,19 +63,19 @@
pthread_t pthread;
int pthread_create_result = pthread_create(&pthread, nullptr, AttachHelper,
reinterpret_cast<void*>(fn));
- assert(pthread_create_result == 0);
+ CHECK_EQ(pthread_create_result, 0);
int pthread_join_result = pthread_join(pthread, nullptr);
- assert(pthread_join_result == 0);
+ CHECK_EQ(pthread_join_result, 0);
}
static void testFindClassOnAttachedNativeThread(JNIEnv* env) {
jclass clazz = env->FindClass("Main");
- assert(clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jobjectArray array = env->NewObjectArray(0, clazz, nullptr);
- assert(array != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(array != nullptr);
+ CHECK(!env->ExceptionCheck());
}
// http://b/10994325
@@ -86,12 +85,12 @@
static void testFindFieldOnAttachedNativeThread(JNIEnv* env) {
jclass clazz = env->FindClass("Main");
- assert(clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z");
- assert(field != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field != nullptr);
+ CHECK(!env->ExceptionCheck());
env->SetStaticBooleanField(clazz, field, JNI_TRUE);
}
@@ -103,38 +102,38 @@
static void testReflectFieldGetFromAttachedNativeThread(JNIEnv* env) {
jclass clazz = env->FindClass("Main");
- assert(clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jclass class_clazz = env->FindClass("java/lang/Class");
- assert(class_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(class_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID getFieldMetodId = env->GetMethodID(class_clazz, "getField",
"(Ljava/lang/String;)Ljava/lang/reflect/Field;");
- assert(getFieldMetodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getFieldMetodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jstring field_name = env->NewStringUTF("testReflectFieldGetFromAttachedNativeThreadField");
- assert(field_name != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field_name != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject field = env->CallObjectMethod(clazz, getFieldMetodId, field_name);
- assert(field != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field != nullptr);
+ CHECK(!env->ExceptionCheck());
jclass field_clazz = env->FindClass("java/lang/reflect/Field");
- assert(field_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(field_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID getBooleanMetodId = env->GetMethodID(field_clazz, "getBoolean",
"(Ljava/lang/Object;)Z");
- assert(getBooleanMetodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getBooleanMetodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jboolean value = env->CallBooleanMethod(field, getBooleanMetodId, /* ignored */ clazz);
- assert(value == false);
- assert(!env->ExceptionCheck());
+ CHECK(value == false);
+ CHECK(!env->ExceptionCheck());
}
// http://b/15539150
@@ -148,22 +147,22 @@
extern "C" JNIEXPORT void JNICALL Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
jclass) {
jclass super_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SuperClass");
- assert(super_class != nullptr);
+ CHECK(super_class != nullptr);
jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V");
- assert(execute != nullptr);
+ CHECK(execute != nullptr);
jclass sub_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SubClass");
- assert(sub_class != nullptr);
+ CHECK(sub_class != nullptr);
env->CallStaticVoidMethod(sub_class, execute);
}
extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass) {
jclass abstract_class = env->FindClass("Main$testGetMirandaMethod_MirandaAbstract");
- assert(abstract_class != nullptr);
+ CHECK(abstract_class != nullptr);
jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z");
- assert(miranda_method != nullptr);
+ CHECK(miranda_method != nullptr);
return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE);
}
@@ -171,11 +170,11 @@
extern "C" void JNICALL Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass) {
std::vector<uint8_t> buffer(1);
jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0);
- assert(byte_buffer != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(byte_buffer != nullptr);
+ CHECK(!env->ExceptionCheck());
- assert(env->GetDirectBufferAddress(byte_buffer) == &buffer[0]);
- assert(env->GetDirectBufferCapacity(byte_buffer) == 0);
+ CHECK_EQ(env->GetDirectBufferAddress(byte_buffer), &buffer[0]);
+ CHECK_EQ(env->GetDirectBufferCapacity(byte_buffer), 0);
}
constexpr size_t kByteReturnSize = 7;
@@ -185,18 +184,18 @@
jbyte b3, jbyte b4, jbyte b5, jbyte b6,
jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
// We use b1 to drive the output.
- assert(b2 == 2);
- assert(b3 == -3);
- assert(b4 == 4);
- assert(b5 == -5);
- assert(b6 == 6);
- assert(b7 == -7);
- assert(b8 == 8);
- assert(b9 == -9);
- assert(b10 == 10);
+ CHECK_EQ(b2, 2);
+ CHECK_EQ(b3, -3);
+ CHECK_EQ(b4, 4);
+ CHECK_EQ(b5, -5);
+ CHECK_EQ(b6, 6);
+ CHECK_EQ(b7, -7);
+ CHECK_EQ(b8, 8);
+ CHECK_EQ(b9, -9);
+ CHECK_EQ(b10, 10);
- assert(0 <= b1);
- assert(b1 < static_cast<jbyte>(kByteReturnSize));
+ CHECK_LE(0, b1);
+ CHECK_LT(b1, static_cast<jbyte>(kByteReturnSize));
return byte_returns[b1];
}
@@ -210,18 +209,18 @@
jshort s3, jshort s4, jshort s5, jshort s6,
jshort s7, jshort s8, jshort s9, jshort s10) {
// We use s1 to drive the output.
- assert(s2 == 2);
- assert(s3 == -3);
- assert(s4 == 4);
- assert(s5 == -5);
- assert(s6 == 6);
- assert(s7 == -7);
- assert(s8 == 8);
- assert(s9 == -9);
- assert(s10 == 10);
+ CHECK_EQ(s2, 2);
+ CHECK_EQ(s3, -3);
+ CHECK_EQ(s4, 4);
+ CHECK_EQ(s5, -5);
+ CHECK_EQ(s6, 6);
+ CHECK_EQ(s7, -7);
+ CHECK_EQ(s8, 8);
+ CHECK_EQ(s9, -9);
+ CHECK_EQ(s10, 10);
- assert(0 <= s1);
- assert(s1 < static_cast<jshort>(kShortReturnSize));
+ CHECK_LE(0, s1);
+ CHECK_LT(s1, static_cast<jshort>(kShortReturnSize));
return short_returns[s1];
}
@@ -231,17 +230,17 @@
jboolean b5, jboolean b6, jboolean b7,
jboolean b8, jboolean b9, jboolean b10) {
// We use b1 to drive the output.
- assert(b2 == JNI_TRUE);
- assert(b3 == JNI_FALSE);
- assert(b4 == JNI_TRUE);
- assert(b5 == JNI_FALSE);
- assert(b6 == JNI_TRUE);
- assert(b7 == JNI_FALSE);
- assert(b8 == JNI_TRUE);
- assert(b9 == JNI_FALSE);
- assert(b10 == JNI_TRUE);
+ CHECK_EQ(b2, JNI_TRUE);
+ CHECK_EQ(b3, JNI_FALSE);
+ CHECK_EQ(b4, JNI_TRUE);
+ CHECK_EQ(b5, JNI_FALSE);
+ CHECK_EQ(b6, JNI_TRUE);
+ CHECK_EQ(b7, JNI_FALSE);
+ CHECK_EQ(b8, JNI_TRUE);
+ CHECK_EQ(b9, JNI_FALSE);
+ CHECK_EQ(b10, JNI_TRUE);
- assert(b1 == JNI_TRUE || b1 == JNI_FALSE);
+ CHECK(b1 == JNI_TRUE || b1 == JNI_FALSE);
return b1;
}
@@ -252,17 +251,17 @@
jchar c3, jchar c4, jchar c5, jchar c6, jchar c7,
jchar c8, jchar c9, jchar c10) {
// We use c1 to drive the output.
- assert(c2 == 'a');
- assert(c3 == 'b');
- assert(c4 == 'c');
- assert(c5 == '0');
- assert(c6 == '1');
- assert(c7 == '2');
- assert(c8 == 1234);
- assert(c9 == 2345);
- assert(c10 == 3456);
+ CHECK_EQ(c2, 'a');
+ CHECK_EQ(c3, 'b');
+ CHECK_EQ(c4, 'c');
+ CHECK_EQ(c5, '0');
+ CHECK_EQ(c6, '1');
+ CHECK_EQ(c7, '2');
+ CHECK_EQ(c8, 1234);
+ CHECK_EQ(c9, 2345);
+ CHECK_EQ(c10, 3456);
- assert(c1 < static_cast<jchar>(kCharReturnSize));
+ CHECK_LT(c1, static_cast<jchar>(kCharReturnSize));
return char_returns[c1];
}
@@ -281,39 +280,39 @@
// Test direct call.
{
jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack");
- assert(vmstack_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(vmstack_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID getCallingClassLoaderMethodId = env->GetStaticMethodID(vmstack_clazz,
"getCallingClassLoader",
"()Ljava/lang/ClassLoader;");
- assert(getCallingClassLoaderMethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getCallingClassLoaderMethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject class_loader = env->CallStaticObjectMethod(vmstack_clazz,
getCallingClassLoaderMethodId);
- assert(class_loader == nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(class_loader == nullptr);
+ CHECK(!env->ExceptionCheck());
}
// Test one-level call. Use System.loadLibrary().
{
jclass system_clazz = env->FindClass("java/lang/System");
- assert(system_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(system_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID loadLibraryMethodId = env->GetStaticMethodID(system_clazz, "loadLibrary",
"(Ljava/lang/String;)V");
- assert(loadLibraryMethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(loadLibraryMethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
// Create a string object.
jobject library_string = env->NewStringUTF("non_existing_library");
- assert(library_string != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(library_string != nullptr);
+ CHECK(!env->ExceptionCheck());
env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string);
- assert(env->ExceptionCheck());
+ CHECK(env->ExceptionCheck());
// We expect UnsatisfiedLinkError.
jthrowable thrown = env->ExceptionOccurred();
@@ -321,7 +320,7 @@
jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
jclass thrown_class = env->GetObjectClass(thrown);
- assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
+ CHECK(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
}
}
@@ -333,31 +332,31 @@
static void testShallowGetStackClass2(JNIEnv* env) {
jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack");
- assert(vmstack_clazz != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(vmstack_clazz != nullptr);
+ CHECK(!env->ExceptionCheck());
// Test direct call.
{
jmethodID getStackClass2MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass2",
"()Ljava/lang/Class;");
- assert(getStackClass2MethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getStackClass2MethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass2MethodId);
- assert(caller_class == nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(caller_class == nullptr);
+ CHECK(!env->ExceptionCheck());
}
// Test one-level call. Use VMStack.getStackClass1().
{
jmethodID getStackClass1MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass1",
"()Ljava/lang/Class;");
- assert(getStackClass1MethodId != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(getStackClass1MethodId != nullptr);
+ CHECK(!env->ExceptionCheck());
jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass1MethodId);
- assert(caller_class == nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(caller_class == nullptr);
+ CHECK(!env->ExceptionCheck());
}
// For better testing we would need to compile against libcore and have a two-deep stack
@@ -416,8 +415,8 @@
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(!env_->ExceptionCheck());
- assert(c != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(c != nullptr);
return c;
}
@@ -429,7 +428,7 @@
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(m != nullptr);
+ CHECK(m != nullptr);
return m;
}
@@ -439,7 +438,7 @@
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(o != nullptr);
+ CHECK(o != nullptr);
return o;
}
@@ -467,7 +466,7 @@
env_->ExceptionDescribe();
env_->FatalError(__FUNCTION__);
}
- assert(m != nullptr);
+ CHECK(m != nullptr);
return m;
}
@@ -508,21 +507,21 @@
jobject sub_super = CallConstructor(sub_, super_constructor_);
jobject sub_sub = CallConstructor(sub_, sub_constructor_);
- assert(env_->IsInstanceOf(super_super, super_));
- assert(!env_->IsInstanceOf(super_super, sub_));
+ CHECK(env_->IsInstanceOf(super_super, super_));
+ CHECK(!env_->IsInstanceOf(super_super, sub_));
// Note that even though we called (and ran) the subclass
// constructor, we are not the subclass.
- assert(env_->IsInstanceOf(super_sub, super_));
- assert(!env_->IsInstanceOf(super_sub, sub_));
+ CHECK(env_->IsInstanceOf(super_sub, super_));
+ CHECK(!env_->IsInstanceOf(super_sub, sub_));
// Note that even though we called the superclass constructor, we
// are still the subclass.
- assert(env_->IsInstanceOf(sub_super, super_));
- assert(env_->IsInstanceOf(sub_super, sub_));
+ CHECK(env_->IsInstanceOf(sub_super, super_));
+ CHECK(env_->IsInstanceOf(sub_super, sub_));
- assert(env_->IsInstanceOf(sub_sub, super_));
- assert(env_->IsInstanceOf(sub_sub, sub_));
+ CHECK(env_->IsInstanceOf(sub_sub, super_));
+ CHECK(env_->IsInstanceOf(sub_sub, sub_));
}
void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
@@ -542,8 +541,8 @@
CallMethod(o, c, m, true, test_case);
jboolean super_field = GetBooleanField(o, super_field_);
jboolean sub_field = GetBooleanField(o, sub_field_);
- assert(super_field == super_method);
- assert(sub_field != super_method);
+ CHECK_EQ(super_field, super_method);
+ CHECK_NE(sub_field, super_method);
}
void TestnonstaticCallNonvirtualMethod() {
@@ -565,20 +564,20 @@
extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jclass) {
jclass c = env->FindClass("java/lang/String");
- assert(c != nullptr);
+ CHECK(c != nullptr);
jmethodID mid1 = env->GetMethodID(c, "<init>", "()V");
- assert(mid1 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid1 != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID mid2 = env->GetMethodID(c, "<init>", "([B)V");
- assert(mid2 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid2 != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID mid3 = env->GetMethodID(c, "<init>", "([C)V");
- assert(mid3 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid3 != nullptr);
+ CHECK(!env->ExceptionCheck());
jmethodID mid4 = env->GetMethodID(c, "<init>", "(Ljava/lang/String;)V");
- assert(mid4 != nullptr);
- assert(!env->ExceptionCheck());
+ CHECK(mid4 != nullptr);
+ CHECK(!env->ExceptionCheck());
const char* test_array = "Test";
int byte_array_length = strlen(test_array);
@@ -587,22 +586,22 @@
// Test NewObject
jstring s = reinterpret_cast<jstring>(env->NewObject(c, mid2, byte_array));
- assert(s != nullptr);
- assert(env->GetStringLength(s) == byte_array_length);
- assert(env->GetStringUTFLength(s) == byte_array_length);
+ CHECK(s != nullptr);
+ CHECK_EQ(env->GetStringLength(s), byte_array_length);
+ CHECK_EQ(env->GetStringUTFLength(s), byte_array_length);
const char* chars = env->GetStringUTFChars(s, nullptr);
- assert(strcmp(test_array, chars) == 0);
+ CHECK_EQ(strcmp(test_array, chars), 0);
env->ReleaseStringUTFChars(s, chars);
// Test AllocObject and Call(Nonvirtual)VoidMethod
jstring s1 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s1 != nullptr);
+ CHECK(s1 != nullptr);
jstring s2 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s2 != nullptr);
+ CHECK(s2 != nullptr);
jstring s3 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s3 != nullptr);
+ CHECK(s3 != nullptr);
jstring s4 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s4 != nullptr);
+ CHECK(s4 != nullptr);
jcharArray char_array = env->NewCharArray(5);
jstring string_arg = env->NewStringUTF("helloworld");
@@ -621,18 +620,18 @@
// Test with global and weak global references
jstring s5 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s5 != nullptr);
+ CHECK(s5 != nullptr);
s5 = reinterpret_cast<jstring>(env->NewGlobalRef(s5));
jstring s6 = reinterpret_cast<jstring>(env->AllocObject(c));
- assert(s6 != nullptr);
+ CHECK(s6 != nullptr);
s6 = reinterpret_cast<jstring>(env->NewWeakGlobalRef(s6));
env->CallVoidMethod(s5, mid1);
env->CallNonvirtualVoidMethod(s6, c, mid2, byte_array);
- assert(env->GetStringLength(s5) == 0);
- assert(env->GetStringLength(s6) == byte_array_length);
+ CHECK_EQ(env->GetStringLength(s5), 0);
+ CHECK_EQ(env->GetStringLength(s6), byte_array_length);
const char* chars6 = env->GetStringUTFChars(s6, nullptr);
- assert(strcmp(test_array, chars6) == 0);
+ CHECK_EQ(strcmp(test_array, chars6), 0);
env->ReleaseStringUTFChars(s6, chars6);
}
@@ -664,8 +663,8 @@
public:
explicit JniCallDefaultMethodsTest(JNIEnv* env)
: env_(env), concrete_class_(env_->FindClass("ConcreteClass")) {
- assert(!env_->ExceptionCheck());
- assert(concrete_class_ != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(concrete_class_ != nullptr);
}
void Test() {
@@ -688,14 +687,14 @@
void TestCalls(const char* declaring_class, std::vector<const char*> methods) {
jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V");
jobject obj = env_->NewObject(concrete_class_, new_method);
- assert(!env_->ExceptionCheck());
- assert(obj != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(obj != nullptr);
jclass decl_class = env_->FindClass(declaring_class);
- assert(!env_->ExceptionCheck());
- assert(decl_class != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(decl_class != nullptr);
for (const char* method : methods) {
jmethodID method_id = env_->GetMethodID(decl_class, method, "()V");
- assert(!env_->ExceptionCheck());
+ CHECK(!env_->ExceptionCheck());
printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method);
env_->CallVoidMethod(obj, method_id);
if (env_->ExceptionCheck()) {
@@ -704,10 +703,10 @@
jmethodID to_string = env_->GetMethodID(
env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;");
jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string);
- assert(!env_->ExceptionCheck());
+ CHECK(!env_->ExceptionCheck());
const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr);
- assert(!env_->ExceptionCheck());
- assert(exception_string_utf8 != nullptr);
+ CHECK(!env_->ExceptionCheck());
+ CHECK(exception_string_utf8 != nullptr);
printf("EXCEPTION OCCURED: %s\n", exception_string_utf8);
env_->ReleaseStringUTFChars(exception_string, exception_string_utf8);
}
@@ -724,12 +723,12 @@
static void InvokeSpecificMethod(JNIEnv* env, jobject obj, const char* method) {
jclass lambda_class = env->FindClass("LambdaInterface");
- assert(!env->ExceptionCheck());
- assert(lambda_class != nullptr);
+ CHECK(!env->ExceptionCheck());
+ CHECK(lambda_class != nullptr);
jmethodID method_id = env->GetMethodID(lambda_class, method, "()V");
- assert(!env->ExceptionCheck());
+ CHECK(!env->ExceptionCheck());
env->CallVoidMethod(obj, method_id);
- assert(!env->ExceptionCheck());
+ CHECK(!env->ExceptionCheck());
}
extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaDefaultMethod(
@@ -740,3 +739,6 @@
extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaMethod(JNIEnv* e, jclass, jobject l) {
InvokeSpecificMethod(e, l, "sayHi");
}
+
+} // namespace art
+
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 284e554..5304590 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
#include "check_reference_map_visitor.h"
#include "jni.h"
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 51bb68f..420224d 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
#include "check_reference_map_visitor.h"
#include "jni.h"
diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
index 54879fb..c9110a9 100644
--- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
+++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <dlfcn.h>
#include <iostream>
#include "base/casts.h"
@@ -45,6 +46,10 @@
self->SetTopOfShadowStack(nullptr);
JavaVM* vm = down_cast<JNIEnvExt*>(env)->vm;
vm->DetachCurrentThread();
+ // Open ourself again to make sure the native library does not get unloaded from
+ // underneath us due to DestroyJavaVM. b/28406866
+ void* handle = dlopen(kIsDebugBuild ? "libarttestd.so" : "libarttest.so", RTLD_NOW);
+ CHECK(handle != nullptr);
vm->DestroyJavaVM();
vm_was_shutdown.store(true);
// Give threads some time to get stuck in ExceptionCheck.
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index 286f0d9..a59ce5b 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -98,6 +98,7 @@
public static native void ensureJittedAndPolymorphicInline();
public void increment() {
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
counter++;
}
public static int counter = 0;
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index bd8f0a9..2fa5800 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "art_method.h"
+#include "art_method-inl.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jit/profiling_info.h"
diff --git a/test/594-load-string-regression/expected.txt b/test/594-load-string-regression/expected.txt
new file mode 100644
index 0000000..365b0e1
--- /dev/null
+++ b/test/594-load-string-regression/expected.txt
@@ -0,0 +1 @@
+String: ""
diff --git a/test/594-load-string-regression/info.txt b/test/594-load-string-regression/info.txt
new file mode 100644
index 0000000..6a07ace
--- /dev/null
+++ b/test/594-load-string-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for LoadString listing side effects when it doesn't have any
+and triggering a DCHECK() failure when merging ClinitCheck into NewInstance.
diff --git a/test/594-load-string-regression/src/Main.java b/test/594-load-string-regression/src/Main.java
new file mode 100644
index 0000000..0b9f7b5
--- /dev/null
+++ b/test/594-load-string-regression/src/Main.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static boolean doThrow = false;
+
+ // Note: We're not doing checker tests as we cannot do them specifically for a non-PIC
+ // configuration. The check here would be "prepare_for_register_allocation (before)"
+ // CHECK: LoadClass
+ // CHECK-NEXT: ClinitCheck
+ // CHECK-NEXT: LoadString load_kind:BootImageAddress
+ // CHECK-NEXT: NewInstance
+ // and "prepare_for_register_allocation (after)"
+ // CHECK: LoadString
+ // CHECK-NEXT: NewInstance
+ // but the order of instructions for non-PIC mode is different.
+ public static int $noinline$test() {
+ if (doThrow) { throw new Error(); }
+
+ int r = 0x12345678;
+ do {
+ // LICM pulls the LoadClass and ClinitCheck out of the loop, leaves NewInstance in the loop.
+ Helper h = new Helper();
+ // For non-PIC mode, LICM pulls the boot image LoadString out of the loop.
+ // (For PIC mode, the LoadString can throw and will not be moved out of the loop.)
+ String s = ""; // Empty string is known to be in the boot image.
+ r = r ^ (r >> 5);
+ h.$noinline$printString(s);
+ // During DCE after inlining, the loop back-edge disappears and the pre-header is
+ // merged with the body, leaving consecutive LoadClass, ClinitCheck, LoadString
+ // and NewInstance in non-PIC mode. The prepare_for_register_allocation pass
+ // merges the LoadClass and ClinitCheck with the NewInstance and checks that
+ // there are no instructions with side effects in between. This check used to
+ // fail because LoadString was always listing SideEffects::CanTriggerGC() even
+ // when it doesn't really have any side effects, i.e. for direct references to
+ // boot image Strings or for Strings known to be in the dex cache.
+ } while ($inline$shouldContinue());
+ return r;
+ }
+
+ static boolean $inline$shouldContinue() {
+ return false;
+ }
+
+ public static void main(String[] args) {
+ assertIntEquals(0x12345678 ^ (0x12345678 >> 5), $noinline$test());
+ }
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
+
+class Helper {
+ static boolean doThrow = false;
+
+ public void $noinline$printString(String s) {
+ if (doThrow) { throw new Error(); }
+
+ System.out.println("String: \"" + s + "\"");
+ }
+}
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index a7e6f8b..0d26f45 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -16,6 +16,7 @@
#include "dex_file.h"
+#include "art_method-inl.h"
#include "jit/offline_profiling_info.h"
#include "jit/profile_saver.h"
#include "jni.h"
diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc
index 11c0f42..a5bbf5f 100644
--- a/test/596-app-images/app_images.cc
+++ b/test/596-app-images/app_images.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <assert.h>
#include <iostream>
#include <pthread.h>
#include <stdio.h>
diff --git a/test/597-deopt-new-string/deopt.cc b/test/597-deopt-new-string/deopt.cc
new file mode 100644
index 0000000..844a786
--- /dev/null
+++ b/test/597-deopt-new-string/deopt.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "mirror/class-inl.h"
+#include "runtime.h"
+#include "thread_list.h"
+#include "thread_state.h"
+#include "gc/gc_cause.h"
+#include "gc/scoped_gc_critical_section.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeAll(
+ JNIEnv* env,
+ jclass cls ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(env);
+ ScopedThreadSuspension sts(Thread::Current(), kWaitingForDeoptimization);
+ gc::ScopedGCCriticalSection gcs(Thread::Current(),
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
+ // We need to suspend mutator threads first.
+ ScopedSuspendAll ssa(__FUNCTION__);
+ static bool first = true;
+ if (first) {
+ // We need to enable deoptimization once in order to call DeoptimizeEverything().
+ Runtime::Current()->GetInstrumentation()->EnableDeoptimization();
+ first = false;
+ }
+ Runtime::Current()->GetInstrumentation()->DeoptimizeEverything("test");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_undeoptimizeAll(
+ JNIEnv* env,
+ jclass cls ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(env);
+ ScopedThreadSuspension sts(Thread::Current(), kWaitingForDeoptimization);
+ gc::ScopedGCCriticalSection gcs(Thread::Current(),
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
+ // We need to suspend mutator threads first.
+ ScopedSuspendAll ssa(__FUNCTION__);
+ Runtime::Current()->GetInstrumentation()->UndeoptimizeEverything("test");
+}
+
+} // namespace art
diff --git a/test/597-deopt-new-string/expected.txt b/test/597-deopt-new-string/expected.txt
new file mode 100644
index 0000000..f993efc
--- /dev/null
+++ b/test/597-deopt-new-string/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Finishing
diff --git a/test/597-deopt-new-string/info.txt b/test/597-deopt-new-string/info.txt
new file mode 100644
index 0000000..1bd1f79
--- /dev/null
+++ b/test/597-deopt-new-string/info.txt
@@ -0,0 +1 @@
+Regression test for b/28555675
diff --git a/test/597-deopt-new-string/run b/test/597-deopt-new-string/run
new file mode 100644
index 0000000..9776ab3
--- /dev/null
+++ b/test/597-deopt-new-string/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# We want to run in debuggable mode which keeps the call into StringFactory.newEmptyString().
+exec ${RUN} -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-new-string/src/Main.java b/test/597-deopt-new-string/src/Main.java
new file mode 100644
index 0000000..1224e40
--- /dev/null
+++ b/test/597-deopt-new-string/src/Main.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main implements Runnable {
+ static final int numberOfThreads = 2;
+ static final int totalOperations = 40000;
+ static boolean sFlag = false;
+ static volatile boolean done = false;
+ int threadIndex;
+
+ public static native void deoptimizeAll();
+ public static native void undeoptimizeAll();
+
+ Main(int index) {
+ threadIndex = index;
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ final Thread[] threads = new Thread[numberOfThreads];
+ for (int t = 0; t < threads.length; t++) {
+ threads[t] = new Thread(new Main(t));
+ threads[t].start();
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+ System.out.println("Finishing");
+ }
+
+ public String $noinline$run0() {
+ // Prevent inlining.
+ if (sFlag) {
+ throw new Error();
+ }
+ char[] arr = {'a', 'b', 'c'};
+ return new String(arr, 0, arr.length);
+ }
+
+ public void run() {
+ if (threadIndex == 0) {
+ // This thread keeps doing deoptimization of all threads.
+ // Hopefully that will trigger one deoptimization when returning from
+ // StringFactory.newEmptyString() in one of the other threads.
+ for (int i = 0; i < totalOperations; ++i) {
+ if (i % 50 == 0) {
+ deoptimizeAll();
+ }
+ if (i % 50 == 25) {
+ undeoptimizeAll();
+ }
+ }
+ done = true;
+ } else {
+ // This thread keeps doing new String() from a char array.
+ while (!done) {
+ $noinline$run0();
+ }
+ }
+ }
+}
diff --git a/test/598-checker-irreducible-dominance/expected.txt b/test/598-checker-irreducible-dominance/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/expected.txt
diff --git a/test/598-checker-irreducible-dominance/info.txt b/test/598-checker-irreducible-dominance/info.txt
new file mode 100644
index 0000000..8ca4e63
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/info.txt
@@ -0,0 +1,2 @@
+Regression test for HGraphBuilder which would compute wrong dominance information
+in the presence of irreducible loops.
\ No newline at end of file
diff --git a/test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali b/test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..4d8b515
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/smali/IrreducibleLoop.smali
@@ -0,0 +1,52 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+.super Ljava/lang/Object;
+
+# Test case in which `inner_back_edge` is not dominated by `inner_header` and
+# causes `outer_back_edge` to not be dominated by `outer_header`. HGraphBuilder
+# not do a fix-point iteration and would miss the path to `outer_back_edge`
+# through `inner_back_edge` and incorrectly label the outer loop non-irreducible.
+
+## CHECK-START: int IrreducibleLoop.dominance(int) builder (after)
+## CHECK: Add irreducible:true
+
+.method public static dominance(I)I
+ .registers 2
+
+ if-eqz p0, :outer_header
+ goto :inner_back_edge
+
+ :outer_header
+ if-eqz p0, :inner_header
+
+ :outer_branch_exit
+ if-eqz p0, :outer_merge
+ return p0
+
+ :inner_header
+ goto :outer_merge
+
+ :inner_back_edge
+ goto :inner_header
+
+ :outer_merge
+ if-eqz p0, :inner_back_edge
+
+ :outer_back_edge
+ add-int/2addr p0, p0
+ goto :outer_header
+
+.end method
diff --git a/test/598-checker-irreducible-dominance/src/Main.java b/test/598-checker-irreducible-dominance/src/Main.java
new file mode 100644
index 0000000..38b2ab4
--- /dev/null
+++ b/test/598-checker-irreducible-dominance/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) {
+ // Nothing to run. This regression test merely makes sure the smali test
+ // case successfully compiles.
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index d6f5d37..21f8141 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -43,7 +43,8 @@
566-polymorphic-inlining/polymorphic_inline.cc \
570-checker-osr/osr.cc \
595-profile-saving/profile-saving.cc \
- 596-app-images/app_images.cc
+ 596-app-images/app_images.cc \
+ 597-deopt-new-string/deopt.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
@@ -92,7 +93,12 @@
include $(BUILD_SHARED_LIBRARY)
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
- LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_CFLAGS := $(ART_HOST_CFLAGS)
+ ifeq ($$(suffix),d)
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ else
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ endif
LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
LOCAL_IS_HOST_MODULE := true
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 01114b7..aa45d40 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -473,11 +473,14 @@
LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY
fi
+ PUBLIC_LIBS=libart.so:libartd.so
+
# Create a script with the command. The command can get longer than the longest
# allowed adb command and there is no way to get the exit status from a adb shell
# command.
cmdline="cd $DEX_LOCATION && \
export ANDROID_DATA=$DEX_LOCATION && \
+ export ANDROID_ADDITIONAL_PUBLIC_LIBRARIES=$PUBLIC_LIBS && \
export DEX_LOCATION=$DEX_LOCATION && \
export ANDROID_ROOT=$ANDROID_ROOT && \
$mkdir_cmdline && \
diff --git a/test/run-test b/test/run-test
index 424c54f..bfb1709 100755
--- a/test/run-test
+++ b/test/run-test
@@ -467,7 +467,7 @@
run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}"
else
guess_target_arch_name
- run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}"
+ run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}:/system/lib${suffix64}"
run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
fi
if [ "$relocate" = "yes" ]; then