Merge "Fix true divergence mode"
diff --git a/Android.mk b/Android.mk
index 0e6f3ce..2647268 100644
--- a/Android.mk
+++ b/Android.mk
@@ -443,6 +443,8 @@
$(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
$(TARGET_CORE_IMG_OUT_BASE).art \
$(TARGET_CORE_IMG_OUT_BASE)-interpreter.art
+ sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt
+ # remove libartd.so from public.libraries.txt because golem builds won't have it.
########################################################################
# Phony target for building what go/lem requires on host.
diff --git a/build/art.go b/build/art.go
index 0ae6c8f..b826538 100644
--- a/build/art.go
+++ b/build/art.go
@@ -91,7 +91,7 @@
var cflags []string
deviceFrameSizeLimit := 1736
if len(ctx.AConfig().SanitizeDevice()) > 0 {
- deviceFrameSizeLimit = 6400
+ deviceFrameSizeLimit = 7400
}
cflags = append(cflags,
fmt.Sprintf("-Wframe-larger-than=%d", deviceFrameSizeLimit),
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index b692c6d..1430188 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -431,6 +431,7 @@
TEST_F(JniCompilerTest, TestName ## CriticalGeneric) { \
SCOPED_TRACE("@CriticalNative JNI with generic"); \
gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
+ SetCheckGenericJni(true); \
TestName ## Impl(); \
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index d8ac581..06d9814 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -23,6 +23,7 @@
#include <set>
#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include "arch/instruction_set_features.h"
@@ -42,6 +43,7 @@
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
#include "image-inl.h"
+#include "imtable-inl.h"
#include "indenter.h"
#include "linker/buffered_output_stream.h"
#include "linker/file_output_stream.h"
@@ -2547,6 +2549,392 @@
return EXIT_SUCCESS;
}
+class IMTDumper {
+ public:
+ static bool DumpImt(Runtime* runtime, const std::string& imt_file) {
+ std::vector<std::string> lines = ReadCommentedInputFromFile(imt_file);
+ std::unordered_set<std::string> prepared;
+
+ for (const std::string& line : lines) {
+ // A line should be either a class descriptor, in which case we will dump the complete IMT,
+ // or a class descriptor and an interface method, in which case we will lookup the method,
+ // determine its IMT slot, and check the class' IMT.
+ size_t first_space = line.find(' ');
+ if (first_space == std::string::npos) {
+ DumpIMTForClass(runtime, line, &prepared);
+ } else {
+ DumpIMTForMethod(runtime,
+ line.substr(0, first_space),
+ line.substr(first_space + 1, std::string::npos),
+ &prepared);
+ }
+ std::cerr << std::endl;
+ }
+
+ return true;
+ }
+
+ static bool DumpImtStats(Runtime* runtime, const std::vector<const DexFile*>& dex_files) {
+ size_t wo_imt = 0;
+ size_t w_imt = 0;
+ std::map<size_t, size_t> histogram;
+
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const PointerSize pointer_size = class_linker->GetImagePointerSize();
+ std::unordered_set<std::string> prepared;
+
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> scope(self);
+ MutableHandle<mirror::Class> h_klass(scope.NewHandle<mirror::Class>(nullptr));
+
+ for (const DexFile* dex_file : dex_files) {
+ for (uint32_t class_def_index = 0;
+ class_def_index != dex_file->NumClassDefs();
+ ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ h_klass.Assign(class_linker->FindClass(self,
+ descriptor,
+ ScopedNullHandle<mirror::ClassLoader>()));
+ if (h_klass.Get() == nullptr) {
+ std::cerr << "Warning: could not load " << descriptor << std::endl;
+ continue;
+ }
+
+ if (HasNoIMT(runtime, h_klass, pointer_size, &prepared)) {
+ wo_imt++;
+ continue;
+ }
+
+ ImTable* im_table = PrepareAndGetImTable(runtime, h_klass, pointer_size, &prepared);
+ if (im_table == nullptr) {
+ // Should not happen, but accept.
+ wo_imt++;
+ continue;
+ }
+
+ w_imt++;
+ for (size_t imt_index = 0; imt_index != ImTable::kSize; ++imt_index) {
+ ArtMethod* ptr = im_table->Get(imt_index, pointer_size);
+ if (ptr->IsRuntimeMethod()) {
+ if (ptr->IsImtUnimplementedMethod()) {
+ histogram[0]++;
+ } else {
+ ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
+ histogram[current_table->NumEntries(pointer_size)]++;
+ }
+ } else {
+ histogram[1]++;
+ }
+ }
+ }
+ }
+
+ std::cerr << "IMT stats:"
+ << std::endl << std::endl;
+
+ std::cerr << " " << w_imt << " classes with IMT."
+ << std::endl << std::endl;
+ std::cerr << " " << wo_imt << " classes without IMT (or copy from Object)."
+ << std::endl << std::endl;
+
+ double sum_one = 0;
+ size_t count_one = 0;
+
+ std::cerr << " " << "IMT histogram" << std::endl;
+ for (auto& bucket : histogram) {
+ std::cerr << " " << bucket.first << " " << bucket.second << std::endl;
+ if (bucket.first > 0) {
+ sum_one += bucket.second * bucket.first;
+ count_one += bucket.second;
+ }
+ }
+
+ double count_zero = count_one + histogram[0];
+ std::cerr << " Stats:" << std::endl;
+ std::cerr << " Average depth (including empty): " << (sum_one / count_zero) << std::endl;
+ std::cerr << " Average depth (excluding empty): " << (sum_one / count_one) << std::endl;
+
+ return true;
+ }
+
+ private:
+ // Check whether the given class has no IMT (or the one shared with java.lang.Object).
+ static bool HasNoIMT(Runtime* runtime,
+ Handle<mirror::Class> klass,
+ const PointerSize pointer_size,
+ std::unordered_set<std::string>* prepared)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (klass->IsObjectClass() || !klass->ShouldHaveImt()) {
+ return true;
+ }
+
+ if (klass->GetImt(pointer_size) == nullptr) {
+ PrepareClass(runtime, klass, prepared);
+ }
+
+ mirror::Class* object_class = mirror::Class::GetJavaLangClass()->GetSuperClass();
+ DCHECK(object_class->IsObjectClass());
+
+ bool result = klass->GetImt(pointer_size) == object_class->GetImt(pointer_size);
+
+ if (klass->GetIfTable() == nullptr) {
+ DCHECK(result);
+ }
+
+ return result;
+ }
+
+ static void PrintTable(ImtConflictTable* table, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (table == nullptr) {
+ std::cerr << " <No IMT?>" << std::endl;
+ return;
+ }
+ size_t table_index = 0;
+ for (;;) {
+ ArtMethod* ptr = table->GetInterfaceMethod(table_index, pointer_size);
+ if (ptr == nullptr) {
+ return;
+ }
+ table_index++;
+ std::cerr << " " << PrettyMethod(ptr, true) << std::endl;
+ }
+ }
+
+ static ImTable* PrepareAndGetImTable(Runtime* runtime,
+ Thread* self,
+ const std::string& class_name,
+ const PointerSize pointer_size,
+ mirror::Class** klass_out,
+ std::unordered_set<std::string>* prepared)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (class_name.empty()) {
+ return nullptr;
+ }
+
+ std::string descriptor;
+ if (class_name[0] == 'L') {
+ descriptor = class_name;
+ } else {
+ descriptor = DotToDescriptor(class_name.c_str());
+ }
+
+ ScopedNullHandle<mirror::ClassLoader> null_handle;
+
+ mirror::Class* klass =
+ runtime->GetClassLinker()->FindClass(self, descriptor.c_str(), null_handle);
+
+ if (klass == nullptr) {
+ self->ClearException();
+ std::cerr << "Did not find " << class_name << std::endl;
+ *klass_out = nullptr;
+ return nullptr;
+ }
+
+ StackHandleScope<1> scope(Thread::Current());
+ Handle<mirror::Class> h_klass = scope.NewHandle<mirror::Class>(klass);
+
+ ImTable* ret = PrepareAndGetImTable(runtime, h_klass, pointer_size, prepared);
+ *klass_out = h_klass.Get();
+ return ret;
+ }
+
+ static ImTable* PrepareAndGetImTable(Runtime* runtime,
+ Handle<mirror::Class> h_klass,
+ const PointerSize pointer_size,
+ std::unordered_set<std::string>* prepared)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PrepareClass(runtime, h_klass, prepared);
+ return h_klass->GetImt(pointer_size);
+ }
+
+ static void DumpIMTForClass(Runtime* runtime,
+ const std::string& class_name,
+ std::unordered_set<std::string>* prepared) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+ mirror::Class* klass;
+ ImTable* imt = PrepareAndGetImTable(runtime, self, class_name, pointer_size, &klass, prepared);
+ if (imt == nullptr) {
+ return;
+ }
+
+ std::cerr << class_name << std::endl << " IMT:" << std::endl;
+ for (size_t index = 0; index < ImTable::kSize; ++index) {
+ std::cerr << " " << index << ":" << std::endl;
+ ArtMethod* ptr = imt->Get(index, pointer_size);
+ if (ptr->IsRuntimeMethod()) {
+ if (ptr->IsImtUnimplementedMethod()) {
+ std::cerr << " <empty>" << std::endl;
+ } else {
+ ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
+ PrintTable(current_table, pointer_size);
+ }
+ } else {
+ std::cerr << " " << PrettyMethod(ptr, true) << std::endl;
+ }
+ }
+
+ std::cerr << " Interfaces:" << std::endl;
+ // Run through iftable, find methods that slot here, see if they fit.
+ mirror::IfTable* if_table = klass->GetIfTable();
+ if (if_table != nullptr) {
+ for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
+ mirror::Class* iface = if_table->GetInterface(i);
+ std::string iface_name;
+ std::cerr << " " << iface->GetDescriptor(&iface_name) << std::endl;
+
+ for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) {
+ uint32_t base_hash = ImTable::GetBaseImtHash(&iface_method);
+ uint32_t imt_slot = ImTable::GetImtIndex(&iface_method);
+ std::cerr << " " << PrettyMethod(&iface_method, true) << " slot=" << std::dec
+ << imt_slot << " base_hash=0x" << std::hex << base_hash << std::endl;
+ }
+ }
+ }
+ }
+
+ static void DumpIMTForMethod(Runtime* runtime,
+ const std::string& class_name,
+ const std::string& method,
+ std::unordered_set<std::string>* prepared) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ const PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+ mirror::Class* klass;
+ ImTable* imt = PrepareAndGetImTable(runtime,
+ self,
+ class_name,
+ pointer_size,
+ &klass,
+ prepared);
+ if (imt == nullptr) {
+ return;
+ }
+
+ std::cerr << class_name << " <" << method << ">" << std::endl;
+ for (size_t index = 0; index < ImTable::kSize; ++index) {
+ ArtMethod* ptr = imt->Get(index, pointer_size);
+ if (ptr->IsRuntimeMethod()) {
+ if (ptr->IsImtUnimplementedMethod()) {
+ continue;
+ }
+
+ ImtConflictTable* current_table = ptr->GetImtConflictTable(pointer_size);
+ if (current_table == nullptr) {
+ continue;
+ }
+
+ size_t table_index = 0;
+ for (;;) {
+ ArtMethod* ptr2 = current_table->GetInterfaceMethod(table_index, pointer_size);
+ if (ptr2 == nullptr) {
+ break;
+ }
+ table_index++;
+
+ std::string p_name = PrettyMethod(ptr2, true);
+ if (StartsWith(p_name, method.c_str())) {
+ std::cerr << " Slot "
+ << index
+ << " ("
+ << current_table->NumEntries(pointer_size)
+ << ")"
+ << std::endl;
+ PrintTable(current_table, pointer_size);
+ return;
+ }
+ }
+ } else {
+ std::string p_name = PrettyMethod(ptr, true);
+ if (StartsWith(p_name, method.c_str())) {
+ std::cerr << " Slot " << index << " (1)" << std::endl;
+ std::cerr << " " << p_name << std::endl;
+ } else {
+ // Run through iftable, find methods that slot here, see if they fit.
+ mirror::IfTable* if_table = klass->GetIfTable();
+ if (if_table != nullptr) {
+ for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
+ mirror::Class* iface = if_table->GetInterface(i);
+ size_t num_methods = iface->NumDeclaredVirtualMethods();
+ if (num_methods > 0) {
+ for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) {
+ if (ImTable::GetImtIndex(&iface_method) == index) {
+ std::string i_name = PrettyMethod(&iface_method, true);
+ if (StartsWith(i_name, method.c_str())) {
+ std::cerr << " Slot " << index << " (1)" << std::endl;
+ std::cerr << " " << p_name << " (" << i_name << ")" << std::endl;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Read lines from the given stream, dropping comments and empty lines
+ static std::vector<std::string> ReadCommentedInputStream(std::istream& in_stream) {
+ std::vector<std::string> output;
+ while (in_stream.good()) {
+ std::string dot;
+ std::getline(in_stream, dot);
+ if (StartsWith(dot, "#") || dot.empty()) {
+ continue;
+ }
+ output.push_back(dot);
+ }
+ return output;
+ }
+
+ // Read lines from the given file, dropping comments and empty lines.
+ static std::vector<std::string> ReadCommentedInputFromFile(const std::string& input_filename) {
+ std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
+ if (input_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open input file " << input_filename;
+ return std::vector<std::string>();
+ }
+ std::vector<std::string> result = ReadCommentedInputStream(*input_file);
+ input_file->close();
+ return result;
+ }
+
+ // Prepare a class, i.e., ensure it has a filled IMT. Will do so recursively for superclasses,
+ // and note in the given set that the work was done.
+ static void PrepareClass(Runtime* runtime,
+ Handle<mirror::Class> h_klass,
+ std::unordered_set<std::string>* done)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!h_klass->ShouldHaveImt()) {
+ return;
+ }
+
+ std::string name;
+ name = h_klass->GetDescriptor(&name);
+
+ if (done->find(name) != done->end()) {
+ return;
+ }
+ done->insert(name);
+
+ if (h_klass->HasSuperClass()) {
+ StackHandleScope<1> h(Thread::Current());
+ PrepareClass(runtime, h.NewHandle<mirror::Class>(h_klass->GetSuperClass()), done);
+ }
+
+ if (!h_klass->IsTemp()) {
+ runtime->GetClassLinker()->FillIMTAndConflictTables(h_klass.Get());
+ }
+ }
+};
+
struct OatdumpArgs : public CmdlineArgs {
protected:
using Base = CmdlineArgs;
@@ -2596,6 +2984,10 @@
app_image_ = option.substr(strlen("--app-image=")).data();
} else if (option.starts_with("--app-oat=")) {
app_oat_ = option.substr(strlen("--app-oat=")).data();
+ } else if (option.starts_with("--dump-imt=")) {
+ imt_dump_ = option.substr(strlen("--dump-imt=")).data();
+ } else if (option == "--dump-imt-stats") {
+ imt_stat_dump_ = true;
} else {
return kParseUnknownArgument;
}
@@ -2692,6 +3084,16 @@
" --addr2instr=<address>: output matching method disassembled code from relative\n"
" address (e.g. PC from crash dump)\n"
" Example: --addr2instr=0x00001a3b\n"
+ "\n"
+ " --dump-imt=<file.txt>: output IMT collisions (if any) for the given receiver\n"
+ " types and interface methods in the given file. The file\n"
+ " is read line-wise, and each line should either be a class\n"
+ " name or descriptor, or a class name/descriptor and a prefix\n"
+ " of a complete method name.\n"
+ " Example: --dump-imt=imt.txt\n"
+ "\n"
+ " --dump-imt-stats: output IMT statistics for the given boot image\n"
+ " Example: --dump-imt-stats"
"\n";
return usage;
@@ -2703,6 +3105,7 @@
const char* method_filter_ = "";
const char* image_location_ = nullptr;
std::string elf_filename_prefix_;
+ std::string imt_dump_;
bool dump_vmap_ = true;
bool dump_code_info_stack_maps_ = false;
bool disassemble_code_ = true;
@@ -2711,6 +3114,7 @@
bool list_classes_ = false;
bool list_methods_ = false;
bool dump_header_only_ = false;
+ bool imt_stat_dump_ = false;
uint32_t addr2instr_ = 0;
const char* export_dex_location_ = nullptr;
const char* app_image_ = nullptr;
@@ -2739,7 +3143,9 @@
args_->app_oat_,
args_->addr2instr_));
- return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
+ return (args_->boot_image_location_ != nullptr ||
+ args_->image_location_ != nullptr ||
+ !args_->imt_dump_.empty()) &&
!args_->symbolize_;
}
@@ -2767,6 +3173,14 @@
virtual bool ExecuteWithRuntime(Runtime* runtime) {
CHECK(args_ != nullptr);
+ if (!args_->imt_dump_.empty()) {
+ return IMTDumper::DumpImt(runtime, args_->imt_dump_);
+ }
+
+ if (args_->imt_stat_dump_) {
+ return IMTDumper::DumpImtStats(runtime, runtime->GetClassLinker()->GetBootClassPath());
+ }
+
if (args_->oat_filename_ != nullptr) {
return DumpOat(runtime,
args_->oat_filename_,
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index caabcde..d5b3090 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -70,6 +70,7 @@
mirror::StringDexCachePair::LookupString(declaring_class->GetDexCacheStrings(),
string_idx,
mirror::DexCache::kDexCacheStringCacheSize).Read();
+ Thread::PoisonObjectPointersIfDebug();
if (UNLIKELY(string == nullptr)) {
StackHandleScope<1> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
@@ -84,6 +85,7 @@
inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* referrer) {
mirror::Class* resolved_type = referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
+ Thread::PoisonObjectPointersIfDebug();
if (UNLIKELY(resolved_type == nullptr)) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(Thread::Current());
@@ -101,6 +103,7 @@
mirror::Class* declaring_class = referrer->GetDeclaringClass();
mirror::DexCache* dex_cache_ptr = declaring_class->GetDexCache();
mirror::Class* resolved_type = dex_cache_ptr->GetResolvedType(type_idx);
+ Thread::PoisonObjectPointersIfDebug();
if (UNLIKELY(resolved_type == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_cache_ptr));
@@ -148,6 +151,7 @@
ArtMethod* referrer,
InvokeType type) {
ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer);
+ Thread::PoisonObjectPointersIfDebug();
if (UNLIKELY(resolved_method == nullptr)) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(self);
@@ -179,6 +183,7 @@
bool is_static) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
ArtField* resolved_field = GetResolvedField(field_idx, declaring_class);
+ Thread::PoisonObjectPointersIfDebug();
if (UNLIKELY(resolved_field == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c51b99a..48550f3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2169,6 +2169,7 @@
const char* descriptor,
mirror::Class* klass) {
DCHECK(klass != nullptr);
+ self->PoisonObjectPointers();
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
@@ -2380,7 +2381,7 @@
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
DCHECK(self != nullptr);
self->AssertNoPendingException();
- self->PoisonObjectPointers();
+ self->PoisonObjectPointers(); // For DefineClass, CreateArrayClass, etc...
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
@@ -7526,6 +7527,7 @@
Handle<mirror::DexCache> dex_cache) {
DCHECK(dex_cache.Get() != nullptr);
mirror::String* resolved = dex_cache->GetResolvedString(string_idx);
+ Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
return resolved;
}
@@ -7568,6 +7570,7 @@
Handle<mirror::ClassLoader> class_loader) {
DCHECK(dex_cache.Get() != nullptr);
mirror::Class* resolved = dex_cache->GetResolvedType(type_idx);
+ Thread::PoisonObjectPointersIfDebug();
if (resolved == nullptr) {
Thread* self = Thread::Current();
const char* descriptor = dex_file.StringByTypeIdx(type_idx);
@@ -7606,6 +7609,7 @@
DCHECK(dex_cache.Get() != nullptr);
// Check for hit in the dex cache.
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
+ Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
if (kResolveMode == ClassLinker::kForceICCECheck) {
@@ -7809,6 +7813,7 @@
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
+ Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
return resolved;
@@ -7841,6 +7846,7 @@
bool is_static) {
DCHECK(dex_cache.Get() != nullptr);
ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
+ Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
return resolved;
}
@@ -7883,6 +7889,7 @@
Handle<mirror::ClassLoader> class_loader) {
DCHECK(dex_cache.Get() != nullptr);
ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
+ Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
return resolved;
}
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index e0d5337..789a5bd 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -572,7 +572,7 @@
*annotation_ptr = annotation;
if (result_style == DexFile::kAllObjects && primitive_type != Primitive::kPrimVoid) {
- element_object = BoxPrimitive(primitive_type, annotation_value->value_);
+ element_object = BoxPrimitive(primitive_type, annotation_value->value_).Decode();
set_object = true;
}
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index fd9ffbd..bfa2b69 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -160,7 +160,7 @@
} else {
JValue jv;
jv.SetJ(args.at(i).j);
- mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
+ mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv).Decode();
if (val == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index b0ca18e..6d61c64 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -50,6 +50,7 @@
CHECK_EQ(self->GetState(), kRunnable);
self->AssertThreadSuspensionIsAllowable();
self->AssertNoPendingException();
+ self->PoisonObjectPointers();
}
// Need to check that we arent the large object allocator since the large object allocation code
// path this function. If we didn't check we would have an infinite loop.
diff --git a/runtime/handle.h b/runtime/handle.h
index d4c13d4..c41010a 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -23,6 +23,7 @@
#include "base/mutex.h"
#include "base/value_object.h"
#include "jni.h"
+#include "obj_ptr.h"
#include "stack_reference.h"
namespace art {
@@ -130,6 +131,14 @@
return old;
}
+ ALWAYS_INLINE T* Assign(ObjPtr<T> reference) REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackReference<mirror::Object>* ref = Handle<T>::GetReference();
+ T* old = down_cast<T*>(ref->AsMirrorPtr());
+ ref->Assign(reference.Decode());
+ return old;
+ }
+
+
template<typename S>
explicit MutableHandle(const MutableHandle<S>& handle) REQUIRES_SHARED(Locks::mutator_lock_)
: Handle<T>(handle) {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 0f5cbb2..ad7558c 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -31,6 +31,7 @@
#include "lock_word-inl.h"
#include "monitor.h"
#include "object_array-inl.h"
+#include "obj_ptr-inl.h"
#include "read_barrier-inl.h"
#include "reference.h"
#include "runtime.h"
@@ -281,7 +282,7 @@
}
template<VerifyObjectFlags kVerifyFlags>
-inline bool Object::InstanceOf(Class* klass) {
+inline bool Object::InstanceOf(ObjPtr<Class> klass) {
DCHECK(klass != nullptr);
DCHECK(GetClass<kVerifyNone>() != nullptr);
return klass->IsAssignableFrom(GetClass<kVerifyFlags>());
@@ -509,7 +510,7 @@
template GetObjectSize<kNewFlags, kReadBarrierOption>();
}
DCHECK_GE(result, sizeof(Object))
- << " class=" << PrettyTypeOf(GetClass<kNewFlags, kReadBarrierOption>());
+ << " class=" << PrettyClass(GetClass<kNewFlags, kReadBarrierOption>());
return result;
}
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 262cb57..10faf60 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -20,6 +20,7 @@
#include "base/casts.h"
#include "base/enums.h"
#include "globals.h"
+#include "obj_ptr.h"
#include "object_reference.h"
#include "offsets.h"
#include "verify_object.h"
@@ -120,7 +121,7 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool VerifierInstanceOf(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- ALWAYS_INLINE bool InstanceOf(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ ALWAYS_INLINE bool InstanceOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f4ecfb5..0f3447e 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -746,7 +746,7 @@
ObjPtr<mirror::Object, /*kPoison*/ true> null_ptr;
EXPECT_TRUE(null_ptr.IsNull());
EXPECT_TRUE(null_ptr.IsValid());
- EXPECT_TRUE(null_ptr.Get() == nullptr);
+ EXPECT_TRUE(null_ptr.Decode() == nullptr);
EXPECT_TRUE(null_ptr == nullptr);
EXPECT_TRUE(null_ptr == null_ptr);
EXPECT_FALSE(null_ptr != null_ptr);
@@ -758,13 +758,13 @@
ObjPtr<Class, /*kPoison*/ true> X(h_X.Get());
EXPECT_TRUE(!X.IsNull());
EXPECT_TRUE(X.IsValid());
- EXPECT_TRUE(X.Get() != nullptr);
- EXPECT_EQ(h_X.Get(), X.Get());
+ EXPECT_TRUE(X.Decode() != nullptr);
+ EXPECT_EQ(h_X.Get(), X.Decode());
// FindClass may cause thread suspension, it should invalidate X.
ObjPtr<Class, /*kPoison*/ true> Y(class_linker_->FindClass(soa.Self(), "LY;", class_loader));
EXPECT_TRUE(!Y.IsNull());
EXPECT_TRUE(Y.IsValid());
- EXPECT_TRUE(Y.Get() != nullptr);
+ EXPECT_TRUE(Y.Decode() != nullptr);
// Should IsNull be safe to call on null ObjPtr? I'll allow it for now.
EXPECT_TRUE(!X.IsNull());
@@ -773,7 +773,7 @@
X.Assign(h_X.Get());
EXPECT_TRUE(!X.IsNull());
EXPECT_TRUE(X.IsValid());
- EXPECT_EQ(h_X.Get(), X.Get());
+ EXPECT_EQ(h_X.Get(), X.Decode());
// Allow thread suspension to invalidate Y.
soa.Self()->AllowThreadSuspension();
@@ -784,7 +784,7 @@
ObjPtr<mirror::Object, /*kPoison*/ false> unpoisoned;
EXPECT_TRUE(unpoisoned.IsNull());
EXPECT_TRUE(unpoisoned.IsValid());
- EXPECT_TRUE(unpoisoned.Get() == nullptr);
+ EXPECT_TRUE(unpoisoned.Decode() == nullptr);
EXPECT_TRUE(unpoisoned == nullptr);
EXPECT_TRUE(unpoisoned == unpoisoned);
EXPECT_FALSE(unpoisoned != unpoisoned);
@@ -793,7 +793,7 @@
unpoisoned = h_X.Get();
EXPECT_FALSE(unpoisoned.IsNull());
EXPECT_TRUE(unpoisoned == h_X.Get());
- EXPECT_EQ(unpoisoned.Get(), h_X.Get());
+ EXPECT_EQ(unpoisoned.Decode(), h_X.Get());
}
} // namespace mirror
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index af9b68f..b6260e9 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -33,6 +33,7 @@
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
+#include "obj_ptr-inl.h"
#include "reflection.h"
#include "scoped_thread_state_change.h"
#include "scoped_fast_native_object_access.h"
@@ -669,7 +670,10 @@
caller.Assign(GetCallingClass(soa.Self(), 1));
}
if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(
- receiver.Get(), declaring_class, constructor->GetAccessFlags(), caller.Get()))) {
+ MakeObjPtr(receiver.Get()),
+ MakeObjPtr(declaring_class),
+ constructor->GetAccessFlags(),
+ MakeObjPtr(caller.Get())))) {
soa.Self()->ThrowNewExceptionF(
"Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
PrettyMethod(constructor).c_str(), PrettyClass(caller.Get()).c_str());
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index d001d0c..47c49d5 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -73,7 +73,7 @@
if (!m->IsAccessible() && !c->IsPublic()) {
// Go 2 frames back, this method is always called from newInstance0, which is called from
// Constructor.newInstance(Object... args).
- auto* caller = GetCallingClass(soa.Self(), 2);
+ ObjPtr<mirror::Class> caller = GetCallingClass(soa.Self(), 2);
// If caller is null, then we called from JNI, just avoid the check since JNI avoids most
// access checks anyways. TODO: Investigate if this the correct behavior.
if (caller != nullptr && !caller->CanAccess(c.Get())) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 412445f..dab510d 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -43,9 +43,13 @@
PrettyClass(field->GetDeclaringClass()).c_str()).c_str());
return false;
}
- mirror::Class* calling_class = nullptr;
- if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags(),
- &calling_class, 1)) {
+ ObjPtr<mirror::Class> calling_class;
+ if (!VerifyAccess(self,
+ MakeObjPtr(obj),
+ MakeObjPtr(field->GetDeclaringClass()),
+ field->GetAccessFlags(),
+ &calling_class,
+ 1)) {
ThrowIllegalAccessException(
StringPrintf("Class %s cannot access %s field %s of class %s",
calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
@@ -124,7 +128,7 @@
return true;
}
*class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr);
- if (!VerifyObjectIsClass(*class_or_rcvr, declaringClass)) {
+ if (!VerifyObjectIsClass(MakeObjPtr(*class_or_rcvr), MakeObjPtr(declaringClass))) {
DCHECK(soa.Self()->IsExceptionPending());
return false;
}
@@ -152,7 +156,7 @@
DCHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
- return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value));
+ return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value).Decode());
}
template<Primitive::Type kPrimitiveType>
@@ -323,7 +327,10 @@
// Unbox the value, if necessary.
mirror::Object* boxed_value = soa.Decode<mirror::Object*>(javaValue);
JValue unboxed_value;
- if (!UnboxPrimitiveForField(boxed_value, field_type, f->GetArtField(), &unboxed_value)) {
+ if (!UnboxPrimitiveForField(MakeObjPtr(boxed_value),
+ MakeObjPtr(field_type),
+ f->GetArtField(),
+ &unboxed_value)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 12a8298..4d8687c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '8', '7', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '8', '9', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/obj_ptr-inl.h b/runtime/obj_ptr-inl.h
new file mode 100644
index 0000000..3dfcf9e
--- /dev/null
+++ b/runtime/obj_ptr-inl.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_OBJ_PTR_INL_H_
+#define ART_RUNTIME_OBJ_PTR_INL_H_
+
+#include "obj_ptr.h"
+#include "thread-inl.h"
+
+namespace art {
+
+template<class MirrorType, bool kPoison>
+inline bool ObjPtr<MirrorType, kPoison>::IsValid() const {
+ if (!kPoison || IsNull()) {
+ return true;
+ }
+ return GetCookie() == TrimCookie(Thread::Current()->GetPoisonObjectCookie());
+}
+
+template<class MirrorType, bool kPoison>
+inline void ObjPtr<MirrorType, kPoison>::AssertValid() const {
+ if (kPoison) {
+ CHECK(IsValid()) << "Stale object pointer " << DecodeUnchecked() << " , expected cookie "
+ << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie();
+ }
+}
+
+template<class MirrorType, bool kPoison>
+inline uintptr_t ObjPtr<MirrorType, kPoison>::Encode(MirrorType* ptr) {
+ uintptr_t ref = reinterpret_cast<uintptr_t>(ptr);
+ if (kPoison && ref != 0) {
+ DCHECK_LE(ref, 0xFFFFFFFFU);
+ ref >>= kObjectAlignmentShift;
+ // Put cookie in high bits.
+ Thread* self = Thread::Current();
+ DCHECK(self != nullptr);
+ ref |= self->GetPoisonObjectCookie() << kCookieShift;
+ }
+ return ref;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_OBJ_PTR_INL_H_
diff --git a/runtime/mirror/obj_ptr.h b/runtime/obj_ptr.h
similarity index 73%
rename from runtime/mirror/obj_ptr.h
rename to runtime/obj_ptr.h
index 10378e8..d4076be 100644
--- a/runtime/mirror/obj_ptr.h
+++ b/runtime/obj_ptr.h
@@ -14,23 +14,18 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_MIRROR_OBJ_PTR_H_
-#define ART_RUNTIME_MIRROR_OBJ_PTR_H_
+#ifndef ART_RUNTIME_OBJ_PTR_H_
+#define ART_RUNTIME_OBJ_PTR_H_
#include "base/mutex.h" // For Locks::mutator_lock_.
#include "globals.h"
#include "mirror/object_reference.h"
-#include "utils.h"
namespace art {
-namespace mirror {
-
-class Object;
// Value type representing a pointer to a mirror::Object of type MirrorType
// Pass kPoison as a template boolean for testing in non-debug builds.
-// Note that the functions are not 100% thread safe and may have spurious positive check passes in
-// these cases.
+// Since the cookie is thread based, it is not safe to share an ObjPtr between threads.
template<class MirrorType, bool kPoison = kIsDebugBuild>
class ObjPtr {
static constexpr size_t kCookieShift =
@@ -44,11 +39,13 @@
public:
ALWAYS_INLINE ObjPtr() REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {}
- ALWAYS_INLINE explicit ObjPtr(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_)
- : reference_(Encode(ptr)) {}
+ ALWAYS_INLINE ObjPtr(std::nullptr_t) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {}
- ALWAYS_INLINE explicit ObjPtr(const ObjPtr& other) REQUIRES_SHARED(Locks::mutator_lock_)
- = default;
+ template <typename Type>
+ ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_)
+ : reference_(Encode(static_cast<MirrorType*>(ptr))) {}
+
+ ALWAYS_INLINE ObjPtr(const ObjPtr& other) REQUIRES_SHARED(Locks::mutator_lock_) = default;
ALWAYS_INLINE ObjPtr& operator=(const ObjPtr& other) {
reference_ = other.reference_;
@@ -65,30 +62,23 @@
}
ALWAYS_INLINE MirrorType* operator->() const REQUIRES_SHARED(Locks::mutator_lock_) {
- return Get();
- }
-
- ALWAYS_INLINE MirrorType* Get() const REQUIRES_SHARED(Locks::mutator_lock_) {
return Decode();
}
+
ALWAYS_INLINE bool IsNull() const {
return reference_ == 0;
}
- ALWAYS_INLINE bool IsValid() const REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!kPoison || IsNull()) {
- return true;
- }
- return GetCookie() == TrimCookie(Thread::Current()->GetPoisonObjectCookie());
+ // Decode makes sure that the object pointer is valid.
+ ALWAYS_INLINE MirrorType* Decode() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ AssertValid();
+ return DecodeUnchecked();
}
- ALWAYS_INLINE void AssertValid() const REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kPoison) {
- CHECK(IsValid()) << "Stale object pointer, expected cookie "
- << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie();
- }
- }
+ ALWAYS_INLINE bool IsValid() const REQUIRES_SHARED(Locks::mutator_lock_);
+
+ ALWAYS_INLINE void AssertValid() const REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE bool operator==(const ObjPtr& ptr) const REQUIRES_SHARED(Locks::mutator_lock_) {
return Decode() == ptr.Decode();
@@ -124,22 +114,8 @@
return reference_ >> kCookieShift;
}
- ALWAYS_INLINE static uintptr_t Encode(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_) {
- uintptr_t ref = reinterpret_cast<uintptr_t>(ptr);
- if (kPoison && ref != 0) {
- DCHECK_LE(ref, 0xFFFFFFFFU);
- ref >>= kObjectAlignmentShift;
- // Put cookie in high bits.
- Thread* self = Thread::Current();
- DCHECK(self != nullptr);
- ref |= self->GetPoisonObjectCookie() << kCookieShift;
- }
- return ref;
- }
-
// Decode makes sure that the object pointer is valid.
- ALWAYS_INLINE MirrorType* Decode() const REQUIRES_SHARED(Locks::mutator_lock_) {
- AssertValid();
+ ALWAYS_INLINE MirrorType* DecodeUnchecked() const REQUIRES_SHARED(Locks::mutator_lock_) {
if (kPoison) {
return reinterpret_cast<MirrorType*>(
static_cast<uintptr_t>(static_cast<uint32_t>(reference_ << kObjectAlignmentShift)));
@@ -148,12 +124,16 @@
}
}
+ ALWAYS_INLINE static uintptr_t Encode(MirrorType* ptr) REQUIRES_SHARED(Locks::mutator_lock_);
// The encoded reference and cookie.
uintptr_t reference_;
};
+template<class MirrorType, bool kPoison = kIsDebugBuild>
+static inline ObjPtr<MirrorType, kPoison> MakeObjPtr(MirrorType* ptr) {
+ return ObjPtr<MirrorType, kPoison>(ptr);
+}
-} // namespace mirror
} // namespace art
-#endif // ART_RUNTIME_MIRROR_OBJ_PTR_H_
+#endif // ART_RUNTIME_OBJ_PTR_H_
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index f04d41d..0be79ef 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -192,6 +192,13 @@
} else {
StringAppendF(&extras, " \"%.16s... (%d chars)", utf8.c_str(), s->GetLength());
}
+ } else if (ref->IsReferenceInstance()) {
+ mirror::Object* referent = ref->AsReference()->GetReferent();
+ if (referent == nullptr) {
+ extras = " (referent is null)";
+ } else {
+ extras = StringPrintf(" (referent is a %s)", PrettyTypeOf(referent).c_str());
+ }
}
os << StringPrintf(" %5d: ", idx) << ref << " " << className << extras << "\n";
}
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index fae8e72..819e17a 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,11 +16,15 @@
#include "reference_table.h"
+#include "class_linker.h"
#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
#include "mirror/string.h"
#include "primitive.h"
+#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -28,6 +32,39 @@
class ReferenceTableTest : public CommonRuntimeTest {};
+static mirror::Object* CreateWeakReference(mirror::Object* referent)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ StackHandleScope<3> scope(self);
+ Handle<mirror::Object> h_referent(scope.NewHandle<mirror::Object>(referent));
+
+ Handle<mirror::Class> h_ref_class(scope.NewHandle<mirror::Class>(
+ class_linker->FindClass(self,
+ "Ljava/lang/ref/WeakReference;",
+ ScopedNullHandle<mirror::ClassLoader>())));
+ CHECK(h_ref_class.Get() != nullptr);
+ CHECK(class_linker->EnsureInitialized(self, h_ref_class, true, true));
+
+ Handle<mirror::Object> h_ref_instance(scope.NewHandle<mirror::Object>(
+ h_ref_class->AllocObject(self)));
+ CHECK(h_ref_instance.Get() != nullptr);
+
+ ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod(
+ "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
+ CHECK(constructor != nullptr);
+
+ uint32_t args[2];
+ args[0] = PointerToLowMemUInt32(h_ref_instance.Get());
+ args[1] = PointerToLowMemUInt32(h_referent.Get());
+ JValue result;
+ constructor->Invoke(self, args, sizeof(uint32_t), &result, constructor->GetShorty());
+ CHECK(!self->IsExceptionPending());
+
+ return h_ref_instance.Get();
+}
+
TEST_F(ReferenceTableTest, Basics) {
ScopedObjectAccess soa(Thread::Current());
mirror::Object* o1 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello");
@@ -104,6 +141,29 @@
std::string::npos) << oss.str();
}
}
+
+ // Add a reference and check that the type of the referent is dumped.
+ {
+ mirror::Object* empty_reference = CreateWeakReference(nullptr);
+ ASSERT_TRUE(empty_reference->IsReferenceInstance());
+ rt.Add(empty_reference);
+ std::ostringstream oss;
+ rt.Dump(oss);
+ EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is null)"), std::string::npos)
+ << oss.str();
+ }
+
+ {
+ mirror::Object* string_referent = mirror::String::AllocFromModifiedUtf8(Thread::Current(), "A");
+ mirror::Object* non_empty_reference = CreateWeakReference(string_referent);
+ ASSERT_TRUE(non_empty_reference->IsReferenceInstance());
+ rt.Add(non_empty_reference);
+ std::ostringstream oss;
+ rt.Dump(oss);
+ EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is a java.lang.String)"),
+ std::string::npos)
+ << oss.str();
+ }
}
} // namespace art
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index f54d4ca..d7db8a4 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -23,14 +23,17 @@
#include "common_throws.h"
#include "jvalue.h"
#include "mirror/object-inl.h"
+#include "obj_ptr-inl.h"
#include "primitive.h"
#include "utils.h"
namespace art {
inline bool ConvertPrimitiveValue(bool unbox_for_result,
- Primitive::Type srcType, Primitive::Type dstType,
- const JValue& src, JValue* dst) {
+ Primitive::Type srcType,
+ Primitive::Type dstType,
+ const JValue& src,
+ JValue* dst) {
DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
if (LIKELY(srcType == dstType)) {
dst->SetJ(src.GetJ());
@@ -100,11 +103,11 @@
return false;
}
-inline bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
+inline bool VerifyObjectIsClass(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c) {
if (UNLIKELY(o == nullptr)) {
ThrowNullPointerException("null receiver");
return false;
- } else if (UNLIKELY(!o->InstanceOf(c))) {
+ } else if (UNLIKELY(!o->InstanceOf(c.Decode()))) {
InvalidReceiverError(o, c);
return false;
}
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 30b10d8..7c0f2b5 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -72,8 +72,8 @@
num_bytes_ += 4;
}
- void Append(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- Append(StackReference<mirror::Object>::FromMirrorPtr(obj).AsVRegValue());
+ void Append(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ Append(StackReference<mirror::Object>::FromMirrorPtr(obj.Decode()).AsVRegValue());
}
void AppendWide(uint64_t value) {
@@ -95,7 +95,8 @@
}
void BuildArgArrayFromVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
- mirror::Object* receiver, va_list ap)
+ ObjPtr<mirror::Object> receiver,
+ va_list ap)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Set receiver if non-null (method is not static)
if (receiver != nullptr) {
@@ -131,7 +132,7 @@
}
void BuildArgArrayFromJValues(const ScopedObjectAccessAlreadyRunnable& soa,
- mirror::Object* receiver, jvalue* args)
+ ObjPtr<mirror::Object> receiver, jvalue* args)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Set receiver if non-null (method is not static)
if (receiver != nullptr) {
@@ -212,8 +213,9 @@
PrettyDescriptor(found_descriptor).c_str()).c_str());
}
- bool BuildArgArrayFromObjectArray(mirror::Object* receiver,
- mirror::ObjectArray<mirror::Object>* args, ArtMethod* m)
+ bool BuildArgArrayFromObjectArray(ObjPtr<mirror::Object> receiver,
+ ObjPtr<mirror::ObjectArray<mirror::Object>> args,
+ ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile::TypeList* classes = m->GetParameterTypeList();
// Set receiver if non-null (method is not static)
@@ -221,13 +223,13 @@
Append(receiver);
}
for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
- mirror::Object* arg = args->Get(args_offset);
+ ObjPtr<mirror::Object> arg(args->Get(args_offset));
if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* dst_class =
+ ObjPtr<mirror::Class> dst_class(
m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
true /* resolve */,
- pointer_size);
+ pointer_size));
if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
ThrowIllegalArgumentException(
StringPrintf("method %s argument %zd has type %s, got %s",
@@ -240,15 +242,15 @@
}
#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
- if (LIKELY(arg != nullptr && arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
+ if (LIKELY(arg != nullptr && arg->GetClass()->DescriptorEquals(match_descriptor))) { \
ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
- append(primitive_field-> get_fn(arg));
+ append(primitive_field-> get_fn(arg.Decode()));
#define DO_ARG(match_descriptor, get_fn, append) \
} else if (LIKELY(arg != nullptr && \
arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
- append(primitive_field-> get_fn(arg));
+ append(primitive_field-> get_fn(arg.Decode()));
#define DO_FAIL(expected) \
} else { \
@@ -362,9 +364,9 @@
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (uint32_t i = 0; i < num_params; i++) {
uint16_t type_idx = params->GetTypeItem(i).type_idx_;
- mirror::Class* param_type = m->GetClassFromTypeIndex(type_idx,
- true /* resolve*/,
- pointer_size);
+ ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
+ true /* resolve*/,
+ pointer_size));
if (param_type == nullptr) {
CHECK(self->IsExceptionPending());
LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
@@ -376,7 +378,7 @@
// TODO: There is a compaction bug here since GetClassFromTypeIdx can cause thread suspension,
// this is a hard to fix problem since the args can contain Object*, we need to save and
// restore them by using a visitor similar to the ones used in the trampoline entrypoints.
- mirror::Object* argument =
+ ObjPtr<mirror::Object> argument =
(reinterpret_cast<StackReference<mirror::Object>*>(&args[i + offset]))->AsMirrorPtr();
if (argument != nullptr && !argument->InstanceOf(param_type)) {
LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
@@ -423,7 +425,7 @@
}
}
-static ArtMethod* FindVirtualMethod(mirror::Object* receiver, ArtMethod* method)
+static ArtMethod* FindVirtualMethod(ObjPtr<mirror::Object> receiver, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method, kRuntimePointerSize);
}
@@ -457,7 +459,7 @@
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
}
- mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
+ ObjPtr<mirror::Object> receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
const char* shorty =
method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
@@ -488,7 +490,7 @@
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
}
- mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
+ ObjPtr<mirror::Object> receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
const char* shorty =
method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
@@ -513,7 +515,7 @@
return JValue();
}
- mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
+ ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object*>(obj);
ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
@@ -545,7 +547,7 @@
return JValue();
}
- mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
+ ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object*>(obj);
ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
@@ -578,21 +580,21 @@
return nullptr;
}
- auto* executable = soa.Decode<mirror::Executable*>(javaMethod);
+ ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable*>(javaMethod);
const bool accessible = executable->IsAccessible();
ArtMethod* m = executable->GetArtMethod();
- mirror::Class* declaring_class = m->GetDeclaringClass();
+ ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass();
if (UNLIKELY(!declaring_class->IsInitialized())) {
StackHandleScope<1> hs(soa.Self());
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
+ Handle<mirror::Class> h_class(hs.NewHandle(declaring_class.Decode()));
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), h_class, true, true)) {
return nullptr;
}
declaring_class = h_class.Get();
}
- mirror::Object* receiver = nullptr;
+ ObjPtr<mirror::Object> receiver;
if (!m->IsStatic()) {
// Replace calls to String.<init> with equivalent StringFactory call.
if (declaring_class->IsStringClass() && m->IsConstructor()) {
@@ -623,7 +625,7 @@
}
// If method is not set to be accessible, verify it can be accessed by the caller.
- mirror::Class* calling_class = nullptr;
+ ObjPtr<mirror::Class> calling_class;
if (!accessible && !VerifyAccess(soa.Self(),
receiver,
declaring_class,
@@ -674,10 +676,11 @@
}
// Box if necessary and return.
- return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result));
+ return soa.AddLocalReference<jobject>(
+ BoxPrimitive(Primitive::GetType(shorty[0]), result).Decode());
}
-mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) {
+ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value) {
if (src_class == Primitive::kPrimNot) {
return value.GetL();
}
@@ -750,8 +753,9 @@
return "result";
}
-static bool UnboxPrimitive(mirror::Object* o,
- mirror::Class* dst_class, ArtField* f,
+static bool UnboxPrimitive(ObjPtr<mirror::Object> o,
+ ObjPtr<mirror::Class> dst_class,
+ ArtField* f,
JValue* unboxed_value)
REQUIRES_SHARED(Locks::mutator_lock_) {
bool unbox_for_result = (f == nullptr);
@@ -769,7 +773,7 @@
}
return false;
}
- unboxed_value->SetL(o);
+ unboxed_value->SetL(o.Decode());
return true;
}
if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
@@ -791,34 +795,34 @@
}
JValue boxed_value;
- mirror::Class* klass = o->GetClass();
- mirror::Class* src_class = nullptr;
+ ObjPtr<mirror::Class> klass = o->GetClass();
+ ObjPtr<mirror::Class> src_class = nullptr;
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
if (klass->DescriptorEquals("Ljava/lang/Boolean;")) {
src_class = class_linker->FindPrimitiveClass('Z');
- boxed_value.SetZ(primitive_field->GetBoolean(o));
+ boxed_value.SetZ(primitive_field->GetBoolean(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Byte;")) {
src_class = class_linker->FindPrimitiveClass('B');
- boxed_value.SetB(primitive_field->GetByte(o));
+ boxed_value.SetB(primitive_field->GetByte(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Character;")) {
src_class = class_linker->FindPrimitiveClass('C');
- boxed_value.SetC(primitive_field->GetChar(o));
+ boxed_value.SetC(primitive_field->GetChar(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Float;")) {
src_class = class_linker->FindPrimitiveClass('F');
- boxed_value.SetF(primitive_field->GetFloat(o));
+ boxed_value.SetF(primitive_field->GetFloat(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Double;")) {
src_class = class_linker->FindPrimitiveClass('D');
- boxed_value.SetD(primitive_field->GetDouble(o));
+ boxed_value.SetD(primitive_field->GetDouble(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Integer;")) {
src_class = class_linker->FindPrimitiveClass('I');
- boxed_value.SetI(primitive_field->GetInt(o));
+ boxed_value.SetI(primitive_field->GetInt(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Long;")) {
src_class = class_linker->FindPrimitiveClass('J');
- boxed_value.SetJ(primitive_field->GetLong(o));
+ boxed_value.SetJ(primitive_field->GetLong(o.Decode()));
} else if (klass->DescriptorEquals("Ljava/lang/Short;")) {
src_class = class_linker->FindPrimitiveClass('S');
- boxed_value.SetS(primitive_field->GetShort(o));
+ boxed_value.SetS(primitive_field->GetShort(o.Decode()));
} else {
std::string temp;
ThrowIllegalArgumentException(
@@ -833,28 +837,36 @@
boxed_value, unboxed_value);
}
-bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, ArtField* f,
+bool UnboxPrimitiveForField(ObjPtr<mirror::Object> o,
+ ObjPtr<mirror::Class> dst_class,
+ ArtField* f,
JValue* unboxed_value) {
DCHECK(f != nullptr);
return UnboxPrimitive(o, dst_class, f, unboxed_value);
}
-bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue* unboxed_value) {
+bool UnboxPrimitiveForResult(ObjPtr<mirror::Object> o,
+ ObjPtr<mirror::Class> dst_class,
+ JValue* unboxed_value) {
return UnboxPrimitive(o, dst_class, nullptr, unboxed_value);
}
-mirror::Class* GetCallingClass(Thread* self, size_t num_frames) {
+ObjPtr<mirror::Class> GetCallingClass(Thread* self, size_t num_frames) {
NthCallerVisitor visitor(self, num_frames);
visitor.WalkStack();
return visitor.caller != nullptr ? visitor.caller->GetDeclaringClass() : nullptr;
}
-bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
- uint32_t access_flags, mirror::Class** calling_class, size_t num_frames) {
+bool VerifyAccess(Thread* self,
+ ObjPtr<mirror::Object> obj,
+ ObjPtr<mirror::Class> declaring_class,
+ uint32_t access_flags,
+ ObjPtr<mirror::Class>* calling_class,
+ size_t num_frames) {
if ((access_flags & kAccPublic) != 0) {
return true;
}
- auto* klass = GetCallingClass(self, num_frames);
+ ObjPtr<mirror::Class> klass = GetCallingClass(self, num_frames);
if (UNLIKELY(klass == nullptr)) {
// The caller is an attached native thread.
return false;
@@ -863,10 +875,10 @@
return VerifyAccess(obj, declaring_class, access_flags, klass);
}
-bool VerifyAccess(mirror::Object* obj,
- mirror::Class* declaring_class,
+bool VerifyAccess(ObjPtr<mirror::Object> obj,
+ ObjPtr<mirror::Class> declaring_class,
uint32_t access_flags,
- mirror::Class* calling_class) {
+ ObjPtr<mirror::Class> calling_class) {
if (calling_class == declaring_class) {
return true;
}
@@ -876,16 +888,16 @@
}
if ((access_flags & kAccProtected) != 0) {
if (obj != nullptr && !obj->InstanceOf(calling_class) &&
- !declaring_class->IsInSamePackage(calling_class)) {
+ !declaring_class->IsInSamePackage(calling_class.Decode())) {
return false;
- } else if (declaring_class->IsAssignableFrom(calling_class)) {
+ } else if (declaring_class->IsAssignableFrom(calling_class.Decode())) {
return true;
}
}
- return declaring_class->IsInSamePackage(calling_class);
+ return declaring_class->IsInSamePackage(calling_class.Decode());
}
-void InvalidReceiverError(mirror::Object* o, mirror::Class* c) {
+void InvalidReceiverError(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c) {
std::string expected_class_name(PrettyDescriptor(c));
std::string actual_class_name(PrettyTypeOf(o));
ThrowIllegalArgumentException(StringPrintf("Expected receiver of type %s, but got %s",
@@ -895,18 +907,18 @@
// This only works if there's one reference which points to the object in obj.
// Will need to be fixed if there's cases where it's not.
-void UpdateReference(Thread* self, jobject obj, mirror::Object* result) {
+void UpdateReference(Thread* self, jobject obj, ObjPtr<mirror::Object> result) {
IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
IndirectRefKind kind = GetIndirectRefKind(ref);
if (kind == kLocal) {
- self->GetJniEnv()->locals.Update(obj, result);
+ self->GetJniEnv()->locals.Update(obj, result.Decode());
} else if (kind == kHandleScopeOrInvalid) {
LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid";
} else if (kind == kGlobal) {
- self->GetJniEnv()->vm->UpdateGlobal(self, ref, result);
+ self->GetJniEnv()->vm->UpdateGlobal(self, ref, result.Decode());
} else {
DCHECK_EQ(kind, kWeakGlobal);
- self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result);
+ self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result.Decode());
}
}
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 208b533..6e5ef71 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -19,6 +19,7 @@
#include "base/mutex.h"
#include "jni.h"
+#include "obj_ptr.h"
#include "primitive.h"
namespace art {
@@ -32,62 +33,85 @@
class ScopedObjectAccessAlreadyRunnable;
class ShadowFrame;
-mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value)
+ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value)
REQUIRES_SHARED(Locks::mutator_lock_);
-bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, ArtField* f,
+
+bool UnboxPrimitiveForField(ObjPtr<mirror::Object> o,
+ ObjPtr<mirror::Class> dst_class,
+ ArtField* f,
JValue* unboxed_value)
REQUIRES_SHARED(Locks::mutator_lock_);
-bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue* unboxed_value)
+
+bool UnboxPrimitiveForResult(ObjPtr<mirror::Object> o,
+ ObjPtr<mirror::Class> dst_class,
+ JValue* unboxed_value)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result,
- Primitive::Type src_class, Primitive::Type dst_class,
- const JValue& src, JValue* dst)
+ Primitive::Type src_class,
+ Primitive::Type dst_class,
+ const JValue& src,
+ JValue* dst)
REQUIRES_SHARED(Locks::mutator_lock_);
-JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
+ jobject obj,
+ jmethodID mid,
va_list args)
REQUIRES_SHARED(Locks::mutator_lock_);
-JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
+JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
+ jobject obj,
+ jmethodID mid,
jvalue* args)
REQUIRES_SHARED(Locks::mutator_lock_);
JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
- jobject obj, jmethodID mid, jvalue* args)
+ jobject obj,
+ jmethodID mid,
+ jvalue* args)
REQUIRES_SHARED(Locks::mutator_lock_);
JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
- jobject obj, jmethodID mid, va_list args)
+ jobject obj,
+ jmethodID mid,
+ va_list args)
REQUIRES_SHARED(Locks::mutator_lock_);
// num_frames is number of frames we look up for access check.
-jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver,
- jobject args, size_t num_frames = 1)
+jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa,
+ jobject method,
+ jobject receiver,
+ jobject args,
+ size_t num_frames = 1)
REQUIRES_SHARED(Locks::mutator_lock_);
-ALWAYS_INLINE bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c)
+ALWAYS_INLINE bool VerifyObjectIsClass(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c)
REQUIRES_SHARED(Locks::mutator_lock_);
-bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
- uint32_t access_flags, mirror::Class** calling_class, size_t num_frames)
+bool VerifyAccess(Thread* self,
+ ObjPtr<mirror::Object> obj,
+ ObjPtr<mirror::Class> declaring_class,
+ uint32_t access_flags,
+ ObjPtr<mirror::Class>* calling_class,
+ size_t num_frames)
REQUIRES_SHARED(Locks::mutator_lock_);
// This version takes a known calling class.
-bool VerifyAccess(mirror::Object* obj,
- mirror::Class* declaring_class,
+bool VerifyAccess(ObjPtr<mirror::Object> obj,
+ ObjPtr<mirror::Class> declaring_class,
uint32_t access_flags,
- mirror::Class* calling_class)
+ ObjPtr<mirror::Class> calling_class)
REQUIRES_SHARED(Locks::mutator_lock_);
// Get the calling class by using a stack visitor, may return null for unattached native threads.
-mirror::Class* GetCallingClass(Thread* self, size_t num_frames)
+ObjPtr<mirror::Class> GetCallingClass(Thread* self, size_t num_frames)
REQUIRES_SHARED(Locks::mutator_lock_);
-void InvalidReceiverError(mirror::Object* o, mirror::Class* c)
+void InvalidReceiverError(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c)
REQUIRES_SHARED(Locks::mutator_lock_);
-void UpdateReference(Thread* self, jobject obj, mirror::Object* result)
+void UpdateReference(Thread* self, jobject obj, ObjPtr<mirror::Object> result)
REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace art
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 298a974..bb6eb79 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -59,6 +59,8 @@
if (UNLIKELY(TestAllFlags())) {
CheckSuspend();
}
+ // Invalidate the current thread's object pointers (ObjPtr) to catch possible moving GC bugs due
+ // to missing handles.
PoisonObjectPointers();
}
@@ -173,6 +175,9 @@
inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
AssertThreadSuspensionIsAllowable();
+ if (kIsDebugBuild) {
+ PoisonObjectPointers();
+ }
DCHECK_EQ(this, Thread::Current());
// Change to non-runnable state, thereby appearing suspended to the system.
TransitionToSuspendedAndRunCheckpoints(new_state);
@@ -303,6 +308,12 @@
tlsPtr_.thread_local_alloc_stack_top = nullptr;
}
+inline void Thread::PoisonObjectPointersIfDebug() {
+ if (kIsDebugBuild) {
+ Thread::Current()->PoisonObjectPointers();
+ }
+}
+
} // namespace art
#endif // ART_RUNTIME_THREAD_INL_H_
diff --git a/runtime/thread.h b/runtime/thread.h
index fb6bde6..55f1489 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -475,6 +475,8 @@
++poison_object_cookie_;
}
+ ALWAYS_INLINE static void PoisonObjectPointersIfDebug();
+
ALWAYS_INLINE uintptr_t GetPoisonObjectCookie() const {
return poison_object_cookie_;
}
diff --git a/runtime/utils.cc b/runtime/utils.cc
index b52e2f2..0803ca7 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -37,6 +37,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "oat_quick_method_header.h"
+#include "obj_ptr-inl.h"
#include "os.h"
#include "scoped_thread_state_change.h"
#include "utf-inl.h"
@@ -270,14 +271,14 @@
}
}
-std::string PrettyDescriptor(mirror::String* java_descriptor) {
+std::string PrettyStringDescriptor(ObjPtr<mirror::String> java_descriptor) {
if (java_descriptor == nullptr) {
return "null";
}
return PrettyDescriptor(java_descriptor->ToModifiedUtf8().c_str());
}
-std::string PrettyDescriptor(mirror::Class* klass) {
+std::string PrettyDescriptor(ObjPtr<mirror::Class> klass) {
if (klass == nullptr) {
return "null";
}
@@ -456,7 +457,7 @@
return result;
}
-std::string PrettyTypeOf(mirror::Object* obj) {
+std::string PrettyTypeOf(ObjPtr<mirror::Object> obj) {
if (obj == nullptr) {
return "null";
}
@@ -471,7 +472,7 @@
return result;
}
-std::string PrettyClass(mirror::Class* c) {
+std::string PrettyClass(ObjPtr<mirror::Class> c) {
if (c == nullptr) {
return "null";
}
@@ -482,7 +483,7 @@
return result;
}
-std::string PrettyClassAndClassLoader(mirror::Class* c) {
+std::string PrettyClassAndClassLoader(ObjPtr<mirror::Class> c) {
if (c == nullptr) {
return "null";
}
diff --git a/runtime/utils.h b/runtime/utils.h
index e65b947..ea9e8f7 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -33,6 +33,7 @@
#include "base/mutex.h"
#include "base/stringpiece.h"
#include "globals.h"
+#include "obj_ptr.h"
#include "primitive.h"
class BacktraceMap;
@@ -135,10 +136,10 @@
// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
// "[[I" would be "int[][]", "[Ljava/lang/String;" would be
// "java.lang.String[]", and so forth.
-std::string PrettyDescriptor(mirror::String* descriptor)
+std::string PrettyStringDescriptor(ObjPtr<mirror::String> descriptor)
REQUIRES_SHARED(Locks::mutator_lock_);
std::string PrettyDescriptor(const char* descriptor);
-std::string PrettyDescriptor(mirror::Class* klass)
+std::string PrettyDescriptor(ObjPtr<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
std::string PrettyDescriptor(Primitive::Type type);
@@ -158,7 +159,7 @@
// So given an instance of java.lang.String, the output would
// be "java.lang.String". Given an array of int, the output would be "int[]".
// Given String.class, the output would be "java.lang.Class<java.lang.String>".
-std::string PrettyTypeOf(mirror::Object* obj)
+std::string PrettyTypeOf(ObjPtr<mirror::Object> obj)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a human-readable form of the type at an index in the specified dex file.
@@ -167,11 +168,11 @@
// Returns a human-readable form of the name of the given class.
// Given String.class, the output would be "java.lang.Class<java.lang.String>".
-std::string PrettyClass(mirror::Class* c)
+std::string PrettyClass(ObjPtr<mirror::Class> c)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a human-readable form of the name of the given class with its class loader.
-std::string PrettyClassAndClassLoader(mirror::Class* c)
+std::string PrettyClassAndClassLoader(ObjPtr<mirror::Class> c)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
diff --git a/tools/jfuzz/run_jfuzz_test_nightly.py b/tools/jfuzz/run_jfuzz_test_nightly.py
index cd338fb..29595f2 100755
--- a/tools/jfuzz/run_jfuzz_test_nightly.py
+++ b/tools/jfuzz/run_jfuzz_test_nightly.py
@@ -16,9 +16,14 @@
import argparse
import os
+import re
+import shutil
import subprocess
import sys
+from glob import glob
+
+from tempfile import mkdtemp
from tempfile import TemporaryFile
# Default arguments for run_jfuzz_test.py.
@@ -51,15 +56,29 @@
for proc in processes:
proc.kill()
# Output results.
+ output_dirs = []
for i, output_file in enumerate(output_files):
output_file.seek(0)
output_str = output_file.read().decode('ascii')
output_file.close()
+ # Extract output directory. Example match: 'Directory : /tmp/tmp8ltpfjng'.
+ directory_match = re.search(r'Directory[^:]*: ([^\n]+)\n', output_str)
+ if directory_match:
+ output_dirs.append(directory_match.group(1))
print('Tester', i)
if output_str.find(SUCCESS_STRING) == NOT_FOUND:
print(output_str)
else:
print(SUCCESS_STRING)
+ # Gather divergences.
+ global_out_dir = mkdtemp('jfuzz_nightly')
+ divergence_nr = 1
+ for out_dir in output_dirs:
+ for divergence_dir in glob(out_dir + '/divergence*/'):
+ shutil.copytree(divergence_dir,
+ global_out_dir + '/divergence' + str(divergence_nr))
+ divergence_nr += 1
+ print('Global output directory:', global_out_dir)
if __name__ == '__main__':
main(sys.argv)