Merge "ART: Fix possible soft+hard failure in verifier" into lmp-mr1-dev
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index d887acd..02252ab 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -54,7 +54,7 @@
include $(CLEAR_VARS)
LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
LOCAL_MODULE_TAGS := optional
- LOCAL_SRC_FILES := $$(art_source)
+ LOCAL_SRC_FILES := $$(art_source) ../sigchainlib/sigchain.cc
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes)
LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries)
@@ -65,9 +65,15 @@
endif
LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS)
+ # Mac OS linker doesn't understand --export-dynamic/--version-script.
+ ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host)
+ LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
+ endif
+
ifeq ($$(art_target_or_host),target)
$(call set-target-local-clang-vars)
$(call set-target-local-cflags-vars,$(6))
+ LOCAL_SHARED_LIBRARIES += libdl
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
@@ -76,7 +82,7 @@
else
LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
endif
- LOCAL_LDLIBS += -lpthread
+ LOCAL_LDLIBS += -lpthread -ldl
endif
ifeq ($$(art_ndebug_or_debug),ndebug)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a7d852b..807c5cc 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -72,6 +72,7 @@
runtime/barrier_test.cc \
runtime/base/bit_field_test.cc \
runtime/base/bit_vector_test.cc \
+ runtime/base/hash_set_test.cc \
runtime/base/hex_dump_test.cc \
runtime/base/histogram_test.cc \
runtime/base/mutex_test.cc \
@@ -278,7 +279,7 @@
.PHONY: $$(gtest_rule)
$$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps)
- $(hide) ($$(call ART_TEST_SKIP,$$@) && LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) $$< && $$(call ART_TEST_PASSED,$$@)) \
+ $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \
|| $$(call ART_TEST_FAILED,$$@)
ART_TEST_HOST_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule)
@@ -326,7 +327,7 @@
LOCAL_MODULE_TAGS := tests
endif
LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
- LOCAL_SRC_FILES := $$(art_gtest_filename)
+ LOCAL_SRC_FILES := $$(art_gtest_filename) sigchainlib/sigchain.cc
LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 022ec6b..d278884 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -242,8 +242,14 @@
CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) ==
resolved_method) << PrettyMethod(resolved_method);
int stats_flags = kFlagMethodResolved;
- GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method,
- &stats_flags, target_method, direct_code, direct_method);
+ GetCodeAndMethodForDirectCall(/*out*/invoke_type,
+ kDirect, // Sharp type
+ false, // The dex cache is guaranteed to be available
+ referrer_class, resolved_method,
+ /*out*/&stats_flags,
+ target_method,
+ /*out*/direct_code,
+ /*out*/direct_method);
DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method);
if (*invoke_type == kDirect) {
stats_flags |= kFlagsMethodResolvedVirtualMadeDirect;
@@ -272,8 +278,14 @@
CHECK(called_method != NULL);
CHECK(!called_method->IsAbstract());
int stats_flags = kFlagMethodResolved;
- GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method,
- &stats_flags, target_method, direct_code, direct_method);
+ GetCodeAndMethodForDirectCall(/*out*/invoke_type,
+ kDirect, // Sharp type
+ true, // The dex cache may not be available
+ referrer_class, called_method,
+ /*out*/&stats_flags,
+ target_method,
+ /*out*/direct_code,
+ /*out*/direct_method);
DCHECK_NE(*invoke_type, kSuper);
if (*invoke_type == kDirect) {
stats_flags |= kFlagsMethodResolvedPreciseTypeDevirtualization;
@@ -288,8 +300,14 @@
// Sharpening failed so generate a regular resolved method dispatch.
int stats_flags = kFlagMethodResolved;
- GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method,
- &stats_flags, target_method, direct_code, direct_method);
+ GetCodeAndMethodForDirectCall(/*out*/invoke_type,
+ *invoke_type, // Sharp type
+ false, // The dex cache is guaranteed to be available
+ referrer_class, resolved_method,
+ /*out*/&stats_flags,
+ target_method,
+ /*out*/direct_code,
+ /*out*/direct_method);
return stats_flags;
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fa2a560..9a44ade 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -934,6 +934,10 @@
if (resolved_class == nullptr) {
return false;
}
+ if (GetCompilerOptions().GetCompilePic()) {
+ // Do not allow a direct class pointer to be used when compiling for position-independent
+ return false;
+ }
*out_is_finalizable = resolved_class->IsFinalizable();
const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
const bool support_boot_image_fixup = GetSupportBootImageFixup();
@@ -1089,7 +1093,7 @@
void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
bool no_guarantee_of_dex_cache_entry,
- mirror::Class* referrer_class,
+ const mirror::Class* referrer_class,
mirror::ArtMethod* method,
int* stats_flags,
MethodReference* target_method,
@@ -1101,7 +1105,7 @@
// invoked, so this can be passed to the out-of-line runtime support code.
*direct_code = 0;
*direct_method = 0;
- bool use_dex_cache = false;
+ bool use_dex_cache = GetCompilerOptions().GetCompilePic(); // Off by default
const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
// TODO This is somewhat hacky. We should refactor all of this invoke codepath.
const bool force_relocations = (compiling_boot ||
@@ -1116,7 +1120,7 @@
return;
}
// TODO: support patching on all architectures.
- use_dex_cache = force_relocations && !support_boot_image_fixup_;
+ use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
}
bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
if (!use_dex_cache) {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a165901..598e196 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -633,11 +633,12 @@
public: // TODO make private or eliminate.
// Compute constant code and method pointers when possible.
- void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
+ void GetCodeAndMethodForDirectCall(/*out*/InvokeType* type,
+ InvokeType sharp_type,
bool no_guarantee_of_dex_cache_entry,
- mirror::Class* referrer_class,
+ const mirror::Class* referrer_class,
mirror::ArtMethod* method,
- int* stats_flags,
+ /*out*/int* stats_flags,
MethodReference* target_method,
uintptr_t* direct_code, uintptr_t* direct_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index c0f91d16..03b9371 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -27,7 +27,7 @@
kSpace, // Maximize space savings.
kBalanced, // Try to get the best performance return on compilation investment.
kSpeed, // Maximize runtime performance.
- kEverything // Force compilation (Note: excludes compilaton of class initializers).
+ kEverything, // Force compilation (Note: excludes compilaton of class initializers).
};
// Guide heuristics to determine whether to compile method if profile data not available.
@@ -58,7 +58,8 @@
include_debug_symbols_(kDefaultIncludeDebugSymbols),
implicit_null_checks_(false),
implicit_so_checks_(false),
- implicit_suspend_checks_(false)
+ implicit_suspend_checks_(false),
+ compile_pic_(false)
#ifdef ART_SEA_IR_MODE
, sea_ir_mode_(false)
#endif
@@ -76,7 +77,8 @@
bool include_debug_symbols,
bool implicit_null_checks,
bool implicit_so_checks,
- bool implicit_suspend_checks
+ bool implicit_suspend_checks,
+ bool compile_pic
#ifdef ART_SEA_IR_MODE
, bool sea_ir_mode
#endif
@@ -93,7 +95,8 @@
include_debug_symbols_(include_debug_symbols),
implicit_null_checks_(implicit_null_checks),
implicit_so_checks_(implicit_so_checks),
- implicit_suspend_checks_(implicit_suspend_checks)
+ implicit_suspend_checks_(implicit_suspend_checks),
+ compile_pic_(compile_pic)
#ifdef ART_SEA_IR_MODE
, sea_ir_mode_(sea_ir_mode)
#endif
@@ -196,6 +199,11 @@
return include_patch_information_;
}
+ // Should the code be compiled as position independent?
+ bool GetCompilePic() const {
+ return compile_pic_;
+ }
+
private:
CompilerFilter compiler_filter_;
size_t huge_method_threshold_;
@@ -211,6 +219,7 @@
bool implicit_null_checks_;
bool implicit_so_checks_;
bool implicit_suspend_checks_;
+ bool compile_pic_;
#ifdef ART_SEA_IR_MODE
bool sea_ir_mode_;
#endif
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
index e4c957a..e8ccd67 100644
--- a/compiler/elf_patcher.cc
+++ b/compiler/elf_patcher.cc
@@ -44,7 +44,7 @@
const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file == nullptr) {
CHECK(Runtime::Current()->IsCompiler());
- oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+ oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, error_msg);
if (oat_file == nullptr) {
*error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
error_msg->c_str());
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 355036b..0aef512 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -98,7 +98,7 @@
{
ImageWriter writer(*compiler_driver_.get());
bool success_image = writer.Write(image_file.GetFilename(), requested_image_base,
- dup_oat->GetPath(), dup_oat->GetPath());
+ dup_oat->GetPath(), dup_oat->GetPath(), /*compile_pic*/false);
ASSERT_TRUE(success_image);
bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
ASSERT_TRUE(success_fixup);
@@ -210,7 +210,8 @@
oat_file_begin,
oat_data_begin,
oat_data_end,
- oat_file_end);
+ oat_file_end,
+ /*compile_pic*/false);
ASSERT_TRUE(image_header.IsValid());
char* magic = const_cast<char*>(image_header.GetMagic());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c6fc115..0b304eb 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -71,11 +71,13 @@
bool ImageWriter::Write(const std::string& image_filename,
uintptr_t image_begin,
const std::string& oat_filename,
- const std::string& oat_location) {
+ const std::string& oat_location,
+ bool compile_pic) {
CHECK(!image_filename.empty());
CHECK_NE(image_begin, 0U);
image_begin_ = reinterpret_cast<byte*>(image_begin);
+ compile_pic_ = compile_pic;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -319,7 +321,8 @@
// Remove the undesired classes from the class roots.
for (const std::string& it : non_image_classes) {
- class_linker->RemoveClass(it.c_str(), NULL);
+ bool result = class_linker->RemoveClass(it.c_str(), NULL);
+ DCHECK(result);
}
// Clear references to removed classes from the DexCaches.
@@ -449,6 +452,8 @@
ObjectArray<Object>::Alloc(self, object_array_class.Get(), ImageHeader::kImageRootsMax)));
image_roots->Set<false>(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod());
image_roots->Set<false>(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod());
+ image_roots->Set<false>(ImageHeader::kImtUnimplementedMethod,
+ runtime->GetImtUnimplementedMethod());
image_roots->Set<false>(ImageHeader::kDefaultImt, runtime->GetDefaultImt());
image_roots->Set<false>(ImageHeader::kCalleeSaveMethod,
runtime->GetCalleeSaveMethod(Runtime::kSaveAll));
@@ -569,7 +574,8 @@
PointerToLowMemUInt32(oat_file_begin),
PointerToLowMemUInt32(oat_data_begin_),
PointerToLowMemUInt32(oat_data_end),
- PointerToLowMemUInt32(oat_file_end));
+ PointerToLowMemUInt32(oat_file_end),
+ compile_pic_);
memcpy(image_->Begin(), &image_header, sizeof(image_header));
// Note that image_end_ is left at end of used space
@@ -685,7 +691,7 @@
const byte* ImageWriter::GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) {
DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() &&
- !method->IsAbstract()) << PrettyMethod(method);
+ !method->IsImtUnimplementedMethod() && !method->IsAbstract()) << PrettyMethod(method);
// Use original code if it exists. Otherwise, set the code pointer to the resolution
// trampoline.
@@ -716,9 +722,11 @@
const byte* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) {
// Calculate the quick entry point following the same logic as FixupMethod() below.
// The resolution method has a special trampoline to call.
- if (UNLIKELY(method == Runtime::Current()->GetResolutionMethod())) {
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(method == runtime->GetResolutionMethod())) {
return GetOatAddress(quick_resolution_trampoline_offset_);
- } else if (UNLIKELY(method == Runtime::Current()->GetImtConflictMethod())) {
+ } else if (UNLIKELY(method == runtime->GetImtConflictMethod() ||
+ method == runtime->GetImtUnimplementedMethod())) {
return GetOatAddress(quick_imt_conflict_trampoline_offset_);
} else {
// We assume all methods have code. If they don't currently then we set them to the use the
@@ -738,12 +746,14 @@
// oat_begin_
// The resolution method has a special trampoline to call.
- if (UNLIKELY(orig == Runtime::Current()->GetResolutionMethod())) {
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
#if defined(ART_USE_PORTABLE_COMPILER)
copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
#endif
copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
- } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
+ } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
+ orig == runtime->GetImtUnimplementedMethod())) {
#if defined(ART_USE_PORTABLE_COMPILER)
copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
#endif
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e8bcf7f..61365fe 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -24,6 +24,7 @@
#include <set>
#include <string>
+#include "base/macros.h"
#include "driver/compiler_driver.h"
#include "mem_map.h"
#include "oat_file.h"
@@ -35,21 +36,23 @@
namespace art {
// Write a Space built during compilation for use during execution.
-class ImageWriter {
+class ImageWriter FINAL {
public:
explicit ImageWriter(const CompilerDriver& compiler_driver)
: compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
- quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {}
+ quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
+ compile_pic_(false) {}
~ImageWriter() {}
bool Write(const std::string& image_filename,
uintptr_t image_begin,
const std::string& oat_filename,
- const std::string& oat_location)
+ const std::string& oat_location,
+ bool compile_pic)
LOCKS_EXCLUDED(Locks::mutator_lock_);
uintptr_t GetOatDataBegin() {
@@ -97,8 +100,8 @@
// different .o ELF objects.
DCHECK_LT(offset, oat_file_->Size());
#endif
- if (offset == 0) {
- return NULL;
+ if (offset == 0u) {
+ return nullptr;
}
return oat_data_begin_ + offset;
}
@@ -199,6 +202,7 @@
uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
+ bool compile_pic_;
friend class FixupVisitor;
friend class FixupClassVisitor;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 11d1728..7c1f6c5 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -135,8 +135,8 @@
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
std::string error_msg;
- std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false,
- &error_msg));
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
+ nullptr, false, &error_msg));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index a06b5c5..0bab429 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -24,10 +24,11 @@
LOCAL_MODULE := dalvikvm
LOCAL_MODULE_TAGS := optional
LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc
+LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc
LOCAL_CFLAGS := $(dalvikvm_cflags)
LOCAL_C_INCLUDES := art/runtime
-LOCAL_SHARED_LIBRARIES := libdl libnativehelper
+LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper
+LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
LOCAL_MULTILIB := both
@@ -50,11 +51,15 @@
LOCAL_MODULE_TAGS := optional
LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc
+LOCAL_SRC_FILES := dalvikvm.cc ../sigchainlib/sigchain.cc
LOCAL_CFLAGS := $(dalvikvm_cflags)
LOCAL_C_INCLUDES := art/runtime
LOCAL_SHARED_LIBRARIES := libnativehelper
LOCAL_LDFLAGS := -ldl -lpthread
+# Mac OS linker doesn't understand --export-dynamic.
+ifneq ($(HOST_OS),darwin)
+ LOCAL_LDFLAGS += -Wl,--export-dynamic
+endif
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
LOCAL_IS_HOST_MODULE := true
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6d7f115..833a678 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -155,6 +155,9 @@
UsageError(" Example: --instruction-set-features=div");
UsageError(" Default: default");
UsageError("");
+ UsageError(" --compile-pic: Force indirect use of code, methods, and classes");
+ UsageError(" Default: disabled");
+ UsageError("");
UsageError(" --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
UsageError(" set.");
UsageError(" Example: --compiler-backend=Portable");
@@ -453,7 +456,8 @@
{
// ImageWriter is scoped so it can free memory before doing FixupElf
ImageWriter image_writer(compiler);
- if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
+ if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location,
+ compiler_options_->GetCompilePic())) {
LOG(ERROR) << "Failed to create image file " << image_filename;
return false;
}
@@ -465,10 +469,14 @@
PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
return false;
}
- if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
- LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
- return false;
+ // Do not fix up the ELF file if we are --compile-pic
+ if (!compiler_options_->GetCompilePic()) {
+ if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ return false;
+ }
}
+
return true;
}
@@ -782,7 +790,7 @@
*parsed_value = value;
}
-static int dex2oat(int argc, char** argv) {
+static void b13564922() {
#if defined(__linux__) && defined(__arm__)
int major, minor;
struct utsname uts;
@@ -800,6 +808,10 @@
}
}
#endif
+}
+
+static int dex2oat(int argc, char** argv) {
+ b13564922();
original_argc = argc;
original_argv = argv;
@@ -838,6 +850,7 @@
? Compiler::kPortable
: Compiler::kQuick;
const char* compiler_filter_string = nullptr;
+ bool compile_pic = false;
int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
@@ -964,6 +977,8 @@
}
} else if (option.starts_with("--compiler-filter=")) {
compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+ } else if (option == "--compile-pic") {
+ compile_pic = true;
} else if (option.starts_with("--huge-method-max=")) {
const char* threshold = option.substr(strlen("--huge-method-max=")).data();
if (!ParseInt(threshold, &huge_method_threshold)) {
@@ -1203,7 +1218,8 @@
include_debug_symbols,
implicit_null_checks,
implicit_so_checks,
- implicit_suspend_checks
+ implicit_suspend_checks,
+ compile_pic
#ifdef ART_SEA_IR_MODE
, compiler_options.sea_ir_ =
true;
@@ -1275,6 +1291,7 @@
&method_inliner_map,
thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
+ timings.EndTiming();
return EXIT_FAILURE;
}
std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat);
@@ -1303,6 +1320,7 @@
if (image_classes.get() == nullptr) {
LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
"': " << error_msg;
+ timings.EndTiming();
return EXIT_FAILURE;
}
} else if (image) {
@@ -1321,11 +1339,13 @@
if (zip_archive.get() == nullptr) {
LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
<< error_msg;
+ timings.EndTiming();
return EXIT_FAILURE;
}
if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) {
LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
<< "': " << error_msg;
+ timings.EndTiming();
return EXIT_FAILURE;
}
ATRACE_END();
@@ -1333,6 +1353,7 @@
size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files);
if (failure_count > 0) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+ timings.EndTiming();
return EXIT_FAILURE;
}
}
@@ -1382,17 +1403,20 @@
new SafeMap<std::string, std::string>());
// Insert some compiler things.
- std::ostringstream oss;
- for (int i = 0; i < argc; ++i) {
- if (i > 0) {
- oss << ' ';
+ {
+ std::ostringstream oss;
+ for (int i = 0; i < argc; ++i) {
+ if (i > 0) {
+ oss << ' ';
+ }
+ oss << argv[i];
}
- oss << argv[i];
+ key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+ oss.str(""); // Reset.
+ oss << kRuntimeISA;
+ key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
+ key_value_store->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
}
- key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
- oss.str(""); // Reset.
- oss << kRuntimeISA;
- key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
android_root,
@@ -1411,6 +1435,7 @@
key_value_store.get()));
if (compiler.get() == nullptr) {
LOG(ERROR) << "Failed to create oat file: " << oat_location;
+ timings.EndTiming();
return EXIT_FAILURE;
}
@@ -1474,6 +1499,7 @@
oat_location,
*compiler.get());
if (!image_creation_success) {
+ timings.EndTiming();
return EXIT_FAILURE;
}
VLOG(compiler) << "Image written successfully: " << image_filename;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 83c7871..8f93a66 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -108,6 +108,7 @@
const char* image_roots_descriptions_[] = {
"kResolutionMethod",
"kImtConflictMethod",
+ "kImtUnimplementedMethod",
"kDefaultImt",
"kCalleeSaveMethod",
"kRefsOnlySaveMethod",
@@ -278,8 +279,9 @@
LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
<< "': " << error_msg;
} else {
+ const char* descriptor = m->GetDeclaringClassDescriptor();
const DexFile::ClassDef* class_def =
- dex_file->FindClassDef(m->GetDeclaringClassDescriptor());
+ dex_file->FindClassDef(descriptor, ComputeModifiedUtf8Hash(descriptor));
if (class_def != nullptr) {
uint16_t class_def_index = dex_file->GetIndexForClassDef(*class_def);
const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
@@ -983,6 +985,8 @@
os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
+ os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
+
{
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
@@ -1033,7 +1037,7 @@
std::string error_msg;
const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file == nullptr) {
- oat_file = OatFile::Open(oat_location, oat_location, nullptr, false, &error_msg);
+ oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
if (oat_file == nullptr) {
os << "NOT FOUND: " << error_msg << "\n";
return false;
@@ -1315,7 +1319,7 @@
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
method->IsResolutionMethod() || method->IsImtConflictMethod() ||
- method->IsClassInitializer()) {
+ method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method);
DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method);
} else {
@@ -1761,7 +1765,7 @@
if (oat_filename != nullptr) {
std::string error_msg;
OatFile* oat_file =
- OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+ OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 74f6779..b9637d0 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -71,7 +71,7 @@
bool has_system = false;
bool has_cache = false;
// image_location = /system/framework/boot.art
- // system_image_location = /system/framework/<image_isa>/boot.art
+ // system_image_filename = /system/framework/<image_isa>/boot.art
std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
if (OS::FileExists(system_filename.c_str())) {
has_system = true;
@@ -130,6 +130,7 @@
<< " for location " << image_location;
return false;
}
+
int64_t image_len = input_image->GetLength();
if (image_len < 0) {
LOG(ERROR) << "Error while getting image length";
@@ -142,6 +143,10 @@
return false;
}
+ /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+ // Nothing special to do right now since the image always needs to get patched.
+ // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
// Set up the runtime
RuntimeOptions options;
NoopCompilerCallbacks callbacks;
@@ -186,9 +191,11 @@
return true;
}
-bool PatchOat::Patch(const File* input_oat, const std::string& image_location, off_t delta,
+bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
File* output_oat, File* output_image, InstructionSet isa,
- TimingLogger* timings) {
+ TimingLogger* timings,
+ bool output_oat_opened_from_fd,
+ bool new_oat_out) {
CHECK(Runtime::Current() == nullptr);
CHECK(output_image != nullptr);
CHECK_GE(output_image->Fd(), 0);
@@ -231,6 +238,10 @@
LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath();
}
+ /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
+ // Nothing special to do right now since the image always needs to get patched.
+ // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
+
// Set up the runtime
RuntimeOptions options;
NoopCompilerCallbacks callbacks;
@@ -260,17 +271,37 @@
}
gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
- std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
if (elf.get() == nullptr) {
LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
return false;
}
+ bool skip_patching_oat = false;
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
+ return false;
+ } else if (is_oat_pic == PIC) {
+ // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching.
+ if (!ReplaceOatFileWithSymlink(input_oat->GetPath(),
+ output_oat->GetPath(),
+ output_oat_opened_from_fd,
+ new_oat_out)) {
+ // Errors already logged by above call.
+ return false;
+ }
+ // Don't patch the OAT, since we just symlinked it. Image still needs patching.
+ skip_patching_oat = true;
+ } else {
+ CHECK(is_oat_pic == NOT_PIC);
+ }
+
PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
delta, timings);
t.NewTiming("Patching files");
- if (!p.PatchElf()) {
+ if (!skip_patching_oat && !p.PatchElf()) {
LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
return false;
}
@@ -280,10 +311,12 @@
}
t.NewTiming("Writing files");
- if (!p.WriteElf(output_oat)) {
+ if (!skip_patching_oat && !p.WriteElf(output_oat)) {
+ LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath();
return false;
}
if (!p.WriteImage(output_image)) {
+ LOG(ERROR) << "Failed to write image file " << input_image->GetPath();
return false;
}
return true;
@@ -323,6 +356,83 @@
}
}
+bool PatchOat::IsImagePic(const ImageHeader& image_header, const std::string& image_path) {
+ if (!image_header.CompilePic()) {
+ if (kIsDebugBuild) {
+ LOG(INFO) << "image at location " << image_path << " was *not* compiled pic";
+ }
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "image at location " << image_path << " was compiled PIC";
+ }
+
+ return true;
+}
+
+PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) {
+ if (oat_in == nullptr) {
+ LOG(ERROR) << "No ELF input oat fie available";
+ return ERROR_OAT_FILE;
+ }
+
+ const std::string& file_path = oat_in->GetFile().GetPath();
+
+ const OatHeader* oat_header = GetOatHeader(oat_in);
+ if (oat_header == nullptr) {
+ LOG(ERROR) << "Failed to find oat header in oat file " << file_path;
+ return ERROR_OAT_FILE;
+ }
+
+ if (!oat_header->IsValid()) {
+ LOG(ERROR) << "Elf file " << file_path << " has an invalid oat header";
+ return ERROR_OAT_FILE;
+ }
+
+ bool is_pic = oat_header->IsPic();
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Oat file at " << file_path << " is " << (is_pic ? "PIC" : "not pic");
+ }
+
+ return is_pic ? PIC : NOT_PIC;
+}
+
+bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+ const std::string& output_oat_filename,
+ bool output_oat_opened_from_fd,
+ bool new_oat_out) {
+ // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD.
+ if (output_oat_opened_from_fd) {
+ // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC?
+ LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC";
+ return false;
+ }
+
+ // Image was PIC. Create symlink where the oat is supposed to go.
+ if (!new_oat_out) {
+ LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite";
+ return false;
+ }
+
+ // Delete the original file, since we won't need it.
+ TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
+
+ // Create a symlink from the old oat to the new oat
+ if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) {
+ int err = errno;
+ LOG(ERROR) << "Failed to create symlink at " << output_oat_filename
+ << " error(" << err << "): " << strerror(err);
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename;
+ }
+
+ return true;
+}
+
bool PatchOat::PatchImage() {
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
CHECK_GT(image_->Size(), sizeof(ImageHeader));
@@ -388,6 +498,16 @@
}
}
+const OatHeader* PatchOat::GetOatHeader(const ElfFile* elf_file) {
+ auto rodata_sec = elf_file->FindSectionByName(".rodata");
+ if (rodata_sec == nullptr) {
+ return nullptr;
+ }
+
+ OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf_file->Begin() + rodata_sec->sh_offset);
+ return oat_header;
+}
+
// Called by BitmapCallback
void PatchOat::VisitObject(mirror::Object* object) {
mirror::Object* copy = RelocatedCopyOf(object);
@@ -441,7 +561,8 @@
}
}
-bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings) {
+bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings,
+ bool output_oat_opened_from_fd, bool new_oat_out) {
CHECK(input_oat != nullptr);
CHECK(output_oat != nullptr);
CHECK_GE(input_oat->Fd(), 0);
@@ -449,13 +570,28 @@
TimingLogger::ScopedTiming t("Setup Oat File Patching", timings);
std::string error_msg;
- std::unique_ptr<ElfFile> elf(ElfFile::Open(const_cast<File*>(input_oat),
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat,
PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg));
if (elf.get() == nullptr) {
LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
return false;
}
+ MaybePic is_oat_pic = IsOatPic(elf.get());
+ if (is_oat_pic >= ERROR_FIRST) {
+ // Error logged by IsOatPic
+ return false;
+ } else if (is_oat_pic == PIC) {
+ // Do not need to do ELF-file patching. Create a symlink and skip the rest.
+ // Any errors will be logged by the function call.
+ return ReplaceOatFileWithSymlink(input_oat->GetPath(),
+ output_oat->GetPath(),
+ output_oat_opened_from_fd,
+ new_oat_out);
+ } else {
+ CHECK(is_oat_pic == NOT_PIC);
+ }
+
PatchOat p(elf.release(), delta, timings);
t.NewTiming("Patch Oat file");
if (!p.PatchElf()) {
@@ -1045,11 +1181,17 @@
input_oat_filename = "input-oat-file";
}
input_oat.reset(new File(input_oat_fd, input_oat_filename));
+ if (input_oat == nullptr) {
+ // Unlikely, but ensure exhaustive logging in non-0 exit code case
+ LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
+ }
} else {
CHECK(!input_oat_filename.empty());
input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
- if (input_oat.get() == nullptr) {
- LOG(ERROR) << "Could not open input oat file: " << strerror(errno);
+ if (input_oat == nullptr) {
+ int err = errno;
+ LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
+ << ": " << strerror(err) << "(" << err << ")";
}
}
@@ -1058,12 +1200,22 @@
output_oat_filename = "output-oat-file";
}
output_oat.reset(new File(output_oat_fd, output_oat_filename));
+ if (output_oat == nullptr) {
+ // Unlikely, but ensure exhaustive logging in non-0 exit code case
+ LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
+ }
} else {
CHECK(!output_oat_filename.empty());
output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
+ if (output_oat == nullptr) {
+ int err = errno;
+ LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
+ << ": " << strerror(err) << "(" << err << ")";
+ }
}
}
+ // TODO: get rid of this.
auto cleanup = [&output_image_filename, &output_oat_filename,
&new_oat_out, &new_image_out, &timings, &dump_timings](bool success) {
timings.EndTiming();
@@ -1080,14 +1232,29 @@
if (dump_timings) {
LOG(INFO) << Dumpable<TimingLogger>(timings);
}
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Cleaning up.. success? " << success;
+ }
};
- if ((have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) ||
- (have_image_files && output_image.get() == nullptr)) {
+ if (have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) {
+ LOG(ERROR) << "Failed to open input/output oat files";
+ cleanup(false);
+ return EXIT_FAILURE;
+ } else if (have_image_files && output_image.get() == nullptr) {
+ LOG(ERROR) << "Failed to open output image file";
cleanup(false);
return EXIT_FAILURE;
}
+ if (debug) {
+ LOG(INFO) << "moving offset by " << base_delta
+ << " (0x" << std::hex << base_delta << ") bytes or "
+ << std::dec << (base_delta/kPageSize) << " pages.";
+ }
+
+ // TODO: is it going to be promatic to unlink a file that was flock-ed?
ScopedFlock output_oat_lock;
if (lock_output) {
std::string error_msg;
@@ -1098,24 +1265,28 @@
}
}
- if (debug) {
- LOG(INFO) << "moving offset by " << base_delta
- << " (0x" << std::hex << base_delta << ") bytes or "
- << std::dec << (base_delta/kPageSize) << " pages.";
- }
-
bool ret;
if (have_image_files && have_oat_files) {
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta,
- output_oat.get(), output_image.get(), isa, &timings);
+ output_oat.get(), output_image.get(), isa, &timings,
+ output_oat_fd >= 0, // was it opened from FD?
+ new_oat_out);
} else if (have_oat_files) {
TimingLogger::ScopedTiming pt("patch oat", &timings);
- ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings);
- } else {
+ ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
+ output_oat_fd >= 0, // was it opened from FD?
+ new_oat_out);
+ } else if (have_image_files) {
TimingLogger::ScopedTiming pt("patch image", &timings);
- CHECK(have_image_files);
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
+ } else {
+ CHECK(false);
+ ret = true;
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Exiting with return ... " << ret;
}
cleanup(ret);
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 6960d3b..21041fb 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -30,6 +30,7 @@
namespace art {
class ImageHeader;
+class OatHeader;
namespace mirror {
class Object;
@@ -40,14 +41,21 @@
class PatchOat {
public:
- static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings);
+ // Patch only the oat file
+ static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings,
+ bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ?
+ bool new_oat_out); // Output oat was a new file created by us?
+ // Patch only the image (art file)
static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa,
TimingLogger* timings);
- static bool Patch(const File* oat_in, const std::string& art_location,
+ // Patch both the image and the oat file
+ static bool Patch(File* oat_in, const std::string& art_location,
off_t delta, File* oat_out, File* art_out, InstructionSet isa,
- TimingLogger* timings);
+ TimingLogger* timings,
+ bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ?
+ bool new_oat_out); // Output oat was a new file created by us?
private:
// Takes ownership only of the ElfFile. All other pointers are only borrowed.
@@ -63,6 +71,26 @@
delta_(delta), timings_(timings) {}
~PatchOat() {}
+ // Was the .art image at image_path made with --compile-pic ?
+ static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path);
+
+ enum MaybePic {
+ NOT_PIC, // Code not pic. Patch as usual.
+ PIC, // Code was pic. Create symlink; skip OAT patching.
+ ERROR_OAT_FILE, // Failed to symlink oat file
+ ERROR_FIRST = ERROR_OAT_FILE,
+ };
+
+ // Was the .oat image at oat_in made with --compile-pic ?
+ static MaybePic IsOatPic(const ElfFile* oat_in);
+
+ // Attempt to replace the file with a symlink
+ // Returns false if it fails
+ static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename,
+ const std::string& output_oat_filename,
+ bool output_oat_opened_from_fd,
+ bool new_oat_out); // Output oat was newly created?
+
static void BitmapCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
reinterpret_cast<PatchOat*>(arg)->VisitObject(obj);
@@ -90,6 +118,9 @@
mirror::Object* RelocatedCopyOf(mirror::Object*);
mirror::Object* RelocatedAddressOf(mirror::Object* obj);
+ // Look up the oat header from any elf file.
+ static const OatHeader* GetOatHeader(const ElfFile* elf_file);
+
// Walks through the old image and patches the mmap'd copy of it to the new offset. It does not
// change the heap.
class PatchVisitor {
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index a7adb02..da5ac29 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -91,7 +91,7 @@
// Tracking allocator, tracks how much memory is used.
template<class T, AllocatorTag kTag>
-class TrackingAllocatorImpl {
+class TrackingAllocatorImpl : public std::allocator<T> {
public:
typedef typename std::allocator<T>::value_type value_type;
typedef typename std::allocator<T>::size_type size_type;
diff --git a/runtime/base/hash_map.h b/runtime/base/hash_map.h
new file mode 100644
index 0000000..c0f903f
--- /dev/null
+++ b/runtime/base/hash_map.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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_BASE_HASH_MAP_H_
+#define ART_RUNTIME_BASE_HASH_MAP_H_
+
+#include <utility>
+
+#include "hash_set.h"
+
+namespace art {
+
+template <typename Fn>
+class HashMapWrapper {
+ public:
+ // Hash function.
+ template <class Key, class Value>
+ size_t operator()(const std::pair<Key, Value>& pair) const {
+ return fn_(pair.first);
+ }
+ template <class Key>
+ size_t operator()(const Key& key) const {
+ return fn_(key);
+ }
+ template <class Key, class Value>
+ bool operator()(const std::pair<Key, Value>& a, const std::pair<Key, Value>& b) const {
+ return fn_(a.first, b.first);
+ }
+ template <class Key, class Value, class Element>
+ bool operator()(const std::pair<Key, Value>& a, const Element& element) const {
+ return fn_(a.first, element);
+ }
+
+ private:
+ Fn fn_;
+};
+
+template <class Key, class Value, class EmptyFn = DefaultEmptyFn<Key>,
+ class HashFn = std::hash<Key>, class Pred = std::equal_to<Key>,
+ class Alloc = std::allocator<std::pair<Key, Value>>>
+class HashMap : public HashSet<std::pair<Key, Value>, EmptyFn, HashMapWrapper<HashFn>,
+ HashMapWrapper<Pred>, Alloc> {
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_HASH_MAP_H_
diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h
new file mode 100644
index 0000000..992e5b1
--- /dev/null
+++ b/runtime/base/hash_set.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 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_BASE_HASH_SET_H_
+#define ART_RUNTIME_BASE_HASH_SET_H_
+
+#include <functional>
+#include <memory>
+#include <stdint.h>
+#include <utility>
+
+#include "logging.h"
+
+namespace art {
+
+// Returns true if an item is empty.
+template <class T>
+class DefaultEmptyFn {
+ public:
+ void MakeEmpty(T& item) const {
+ item = T();
+ }
+ bool IsEmpty(const T& item) const {
+ return item == T();
+ }
+};
+
+template <class T>
+class DefaultEmptyFn<T*> {
+ public:
+ void MakeEmpty(T*& item) const {
+ item = nullptr;
+ }
+ bool IsEmpty(const T*& item) const {
+ return item == nullptr;
+ }
+};
+
+// Low memory version of a hash set, uses less memory than std::unordered_set since elements aren't
+// boxed. Uses linear probing.
+// EmptyFn needs to implement two functions MakeEmpty(T& item) and IsEmpty(const T& item)
+template <class T, class EmptyFn = DefaultEmptyFn<T>, class HashFn = std::hash<T>,
+ class Pred = std::equal_to<T>, class Alloc = std::allocator<T>>
+class HashSet {
+ public:
+ static constexpr double kDefaultMinLoadFactor = 0.5;
+ static constexpr double kDefaultMaxLoadFactor = 0.9;
+ static constexpr size_t kMinBuckets = 1000;
+
+ class Iterator {
+ public:
+ Iterator(const Iterator&) = default;
+ Iterator(HashSet* hash_set, size_t index) : hash_set_(hash_set), index_(index) {
+ }
+ Iterator& operator=(const Iterator&) = default;
+ bool operator==(const Iterator& other) const {
+ return hash_set_ == other.hash_set_ && index_ == other.index_;
+ }
+ bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+ Iterator operator++() { // Value after modification.
+ index_ = NextNonEmptySlot(index_);
+ return *this;
+ }
+ Iterator operator++(int) {
+ Iterator temp = *this;
+ index_ = NextNonEmptySlot(index_);
+ return temp;
+ }
+ T& operator*() {
+ DCHECK(!hash_set_->IsFreeSlot(GetIndex()));
+ return hash_set_->ElementForIndex(index_);
+ }
+ const T& operator*() const {
+ DCHECK(!hash_set_->IsFreeSlot(GetIndex()));
+ return hash_set_->ElementForIndex(index_);
+ }
+ T* operator->() {
+ return &**this;
+ }
+ const T* operator->() const {
+ return &**this;
+ }
+ // TODO: Operator -- --(int)
+
+ private:
+ HashSet* hash_set_;
+ size_t index_;
+
+ size_t GetIndex() const {
+ return index_;
+ }
+ size_t NextNonEmptySlot(size_t index) const {
+ const size_t num_buckets = hash_set_->NumBuckets();
+ DCHECK_LT(index, num_buckets);
+ do {
+ ++index;
+ } while (index < num_buckets && hash_set_->IsFreeSlot(index));
+ return index;
+ }
+
+ friend class HashSet;
+ };
+
+ void Clear() {
+ DeallocateStorage();
+ AllocateStorage(1);
+ num_elements_ = 0;
+ elements_until_expand_ = 0;
+ }
+ HashSet() : num_elements_(0), num_buckets_(0), data_(nullptr),
+ min_load_factor_(kDefaultMinLoadFactor), max_load_factor_(kDefaultMaxLoadFactor) {
+ Clear();
+ }
+ HashSet(const HashSet& other) : num_elements_(0), num_buckets_(0), data_(nullptr) {
+ *this = other;
+ }
+ HashSet(HashSet&& other) : num_elements_(0), num_buckets_(0), data_(nullptr) {
+ *this = std::move(other);
+ }
+ ~HashSet() {
+ DeallocateStorage();
+ }
+ HashSet& operator=(HashSet&& other) {
+ std::swap(data_, other.data_);
+ std::swap(num_buckets_, other.num_buckets_);
+ std::swap(num_elements_, other.num_elements_);
+ std::swap(elements_until_expand_, other.elements_until_expand_);
+ std::swap(min_load_factor_, other.min_load_factor_);
+ std::swap(max_load_factor_, other.max_load_factor_);
+ return *this;
+ }
+ HashSet& operator=(const HashSet& other) {
+ DeallocateStorage();
+ AllocateStorage(other.NumBuckets());
+ for (size_t i = 0; i < num_buckets_; ++i) {
+ ElementForIndex(i) = other.data_[i];
+ }
+ num_elements_ = other.num_elements_;
+ elements_until_expand_ = other.elements_until_expand_;
+ min_load_factor_ = other.min_load_factor_;
+ max_load_factor_ = other.max_load_factor_;
+ return *this;
+ }
+ // Lower case for c++11 for each.
+ Iterator begin() {
+ Iterator ret(this, 0);
+ if (num_buckets_ != 0 && IsFreeSlot(ret.GetIndex())) {
+ ++ret; // Skip all the empty slots.
+ }
+ return ret;
+ }
+ // Lower case for c++11 for each.
+ Iterator end() {
+ return Iterator(this, NumBuckets());
+ }
+ bool Empty() {
+ return begin() == end();
+ }
+ // Erase algorithm:
+ // Make an empty slot where the iterator is pointing.
+ // Scan fowards until we hit another empty slot.
+ // If an element inbetween doesn't rehash to the range from the current empty slot to the
+ // iterator. It must be before the empty slot, in that case we can move it to the empty slot
+ // and set the empty slot to be the location we just moved from.
+ // Relies on maintaining the invariant that there's no empty slots from the 'ideal' index of an
+ // element to its actual location/index.
+ Iterator Erase(Iterator it) {
+ // empty_index is the index that will become empty.
+ size_t empty_index = it.GetIndex();
+ DCHECK(!IsFreeSlot(empty_index));
+ size_t next_index = empty_index;
+ bool filled = false; // True if we filled the empty index.
+ while (true) {
+ next_index = NextIndex(next_index);
+ T& next_element = ElementForIndex(next_index);
+ // If the next element is empty, we are done. Make sure to clear the current empty index.
+ if (emptyfn_.IsEmpty(next_element)) {
+ emptyfn_.MakeEmpty(ElementForIndex(empty_index));
+ break;
+ }
+ // Otherwise try to see if the next element can fill the current empty index.
+ const size_t next_hash = hashfn_(next_element);
+ // Calculate the ideal index, if it is within empty_index + 1 to next_index then there is
+ // nothing we can do.
+ size_t next_ideal_index = IndexForHash(next_hash);
+ // Loop around if needed for our check.
+ size_t unwrapped_next_index = next_index;
+ if (unwrapped_next_index < empty_index) {
+ unwrapped_next_index += NumBuckets();
+ }
+ // Loop around if needed for our check.
+ size_t unwrapped_next_ideal_index = next_ideal_index;
+ if (unwrapped_next_ideal_index < empty_index) {
+ unwrapped_next_ideal_index += NumBuckets();
+ }
+ if (unwrapped_next_ideal_index <= empty_index ||
+ unwrapped_next_ideal_index > unwrapped_next_index) {
+ // If the target index isn't within our current range it must have been probed from before
+ // the empty index.
+ ElementForIndex(empty_index) = std::move(next_element);
+ filled = true; // TODO: Optimize
+ empty_index = next_index;
+ }
+ }
+ --num_elements_;
+ // If we didn't fill the slot then we need go to the next non free slot.
+ if (!filled) {
+ ++it;
+ }
+ return it;
+ }
+ // Find an element, returns end() if not found.
+ // Allows custom K types, example of when this is useful.
+ // Set of Class* sorted by name, want to find a class with a name but can't allocate a dummy
+ // object in the heap for performance solution.
+ template <typename K>
+ Iterator Find(const K& element) {
+ return FindWithHash(element, hashfn_(element));
+ }
+ template <typename K>
+ Iterator FindWithHash(const K& element, size_t hash) {
+ DCHECK_EQ(hashfn_(element), hash);
+ size_t index = IndexForHash(hash);
+ while (true) {
+ T& slot = ElementForIndex(index);
+ if (emptyfn_.IsEmpty(slot)) {
+ return end();
+ }
+ if (pred_(slot, element)) {
+ return Iterator(this, index);
+ }
+ index = NextIndex(index);
+ }
+ }
+ // Insert an element, allows duplicates.
+ void Insert(const T& element) {
+ InsertWithHash(element, hashfn_(element));
+ }
+ void InsertWithHash(const T& element, size_t hash) {
+ DCHECK_EQ(hash, hashfn_(element));
+ if (num_elements_ >= elements_until_expand_) {
+ Expand();
+ DCHECK_LT(num_elements_, elements_until_expand_);
+ }
+ const size_t index = FirstAvailableSlot(IndexForHash(hash));
+ data_[index] = element;
+ ++num_elements_;
+ }
+ size_t Size() const {
+ return num_elements_;
+ }
+ void ShrinkToMaximumLoad() {
+ Resize(Size() / max_load_factor_);
+ }
+ // To distance that inserted elements were probed. Used for measuring how good hash functions
+ // are.
+ size_t TotalProbeDistance() const {
+ size_t total = 0;
+ for (size_t i = 0; i < NumBuckets(); ++i) {
+ const T& element = ElementForIndex(i);
+ if (!emptyfn_.IsEmpty(element)) {
+ size_t ideal_location = IndexForHash(hashfn_(element));
+ if (ideal_location > i) {
+ total += i + NumBuckets() - ideal_location;
+ } else {
+ total += i - ideal_location;
+ }
+ }
+ }
+ return total;
+ }
+ // Calculate the current load factor and return it.
+ double CalculateLoadFactor() const {
+ return static_cast<double>(Size()) / static_cast<double>(NumBuckets());
+ }
+ // Make sure that everything reinserts in the right spot. Returns the number of errors.
+ size_t Verify() {
+ size_t errors = 0;
+ for (size_t i = 0; i < num_buckets_; ++i) {
+ T& element = data_[i];
+ if (!emptyfn_.IsEmpty(element)) {
+ T temp;
+ emptyfn_.MakeEmpty(temp);
+ std::swap(temp, element);
+ size_t first_slot = FirstAvailableSlot(IndexForHash(hashfn_(temp)));
+ if (i != first_slot) {
+ LOG(ERROR) << "Element " << i << " should be in slot " << first_slot;
+ ++errors;
+ }
+ std::swap(temp, element);
+ }
+ }
+ return errors;
+ }
+
+ private:
+ T& ElementForIndex(size_t index) {
+ DCHECK_LT(index, NumBuckets());
+ DCHECK(data_ != nullptr);
+ return data_[index];
+ }
+ const T& ElementForIndex(size_t index) const {
+ DCHECK_LT(index, NumBuckets());
+ DCHECK(data_ != nullptr);
+ return data_[index];
+ }
+ size_t IndexForHash(size_t hash) const {
+ return hash % num_buckets_;
+ }
+ size_t NextIndex(size_t index) const {
+ if (UNLIKELY(++index >= num_buckets_)) {
+ DCHECK_EQ(index, NumBuckets());
+ return 0;
+ }
+ return index;
+ }
+ bool IsFreeSlot(size_t index) const {
+ return emptyfn_.IsEmpty(ElementForIndex(index));
+ }
+ size_t NumBuckets() const {
+ return num_buckets_;
+ }
+ // Allocate a number of buckets.
+ void AllocateStorage(size_t num_buckets) {
+ num_buckets_ = num_buckets;
+ data_ = allocfn_.allocate(num_buckets_);
+ for (size_t i = 0; i < num_buckets_; ++i) {
+ allocfn_.construct(allocfn_.address(data_[i]));
+ emptyfn_.MakeEmpty(data_[i]);
+ }
+ }
+ void DeallocateStorage() {
+ if (num_buckets_ != 0) {
+ for (size_t i = 0; i < NumBuckets(); ++i) {
+ allocfn_.destroy(allocfn_.address(data_[i]));
+ }
+ allocfn_.deallocate(data_, NumBuckets());
+ data_ = nullptr;
+ num_buckets_ = 0;
+ }
+ }
+ // Expand the set based on the load factors.
+ void Expand() {
+ size_t min_index = static_cast<size_t>(Size() / min_load_factor_);
+ if (min_index < kMinBuckets) {
+ min_index = kMinBuckets;
+ }
+ // Resize based on the minimum load factor.
+ Resize(min_index);
+ // When we hit elements_until_expand_, we are at the max load factor and must expand again.
+ elements_until_expand_ = NumBuckets() * max_load_factor_;
+ }
+ // Expand / shrink the table to the new specified size.
+ void Resize(size_t new_size) {
+ DCHECK_GE(new_size, Size());
+ T* old_data = data_;
+ size_t old_num_buckets = num_buckets_;
+ // Reinsert all of the old elements.
+ AllocateStorage(new_size);
+ for (size_t i = 0; i < old_num_buckets; ++i) {
+ T& element = old_data[i];
+ if (!emptyfn_.IsEmpty(element)) {
+ data_[FirstAvailableSlot(IndexForHash(hashfn_(element)))] = std::move(element);
+ }
+ allocfn_.destroy(allocfn_.address(element));
+ }
+ allocfn_.deallocate(old_data, old_num_buckets);
+ }
+ ALWAYS_INLINE size_t FirstAvailableSlot(size_t index) const {
+ while (!emptyfn_.IsEmpty(data_[index])) {
+ index = NextIndex(index);
+ }
+ return index;
+ }
+
+ Alloc allocfn_; // Allocator function.
+ HashFn hashfn_; // Hashing function.
+ EmptyFn emptyfn_; // IsEmpty/SetEmpty function.
+ Pred pred_; // Equals function.
+ size_t num_elements_; // Number of inserted elements.
+ size_t num_buckets_; // Number of hash table buckets.
+ size_t elements_until_expand_; // Maxmimum number of elements until we expand the table.
+ T* data_; // Backing storage.
+ double min_load_factor_;
+ double max_load_factor_;
+
+ friend class Iterator;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_HASH_SET_H_
diff --git a/runtime/base/hash_set_test.cc b/runtime/base/hash_set_test.cc
new file mode 100644
index 0000000..5f498d9
--- /dev/null
+++ b/runtime/base/hash_set_test.cc
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 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 "hash_set.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+
+#include "common_runtime_test.h"
+#include "hash_map.h"
+
+namespace art {
+
+struct IsEmptyFnString {
+ void MakeEmpty(std::string& item) const {
+ item.clear();
+ }
+ bool IsEmpty(const std::string& item) const {
+ return item.empty();
+ }
+};
+
+class HashSetTest : public CommonRuntimeTest {
+ public:
+ HashSetTest() : seed_(97421), unique_number_(0) {
+ }
+ std::string RandomString(size_t len) {
+ std::ostringstream oss;
+ for (size_t i = 0; i < len; ++i) {
+ oss << static_cast<char>('A' + PRand() % 64);
+ }
+ static_assert(' ' < 'A', "space must be less than a");
+ oss << " " << unique_number_++; // Relies on ' ' < 'A'
+ return oss.str();
+ }
+ void SetSeed(size_t seed) {
+ seed_ = seed;
+ }
+ size_t PRand() { // Pseudo random.
+ seed_ = seed_ * 1103515245 + 12345;
+ return seed_;
+ }
+
+ private:
+ size_t seed_;
+ size_t unique_number_;
+};
+
+TEST_F(HashSetTest, TestSmoke) {
+ HashSet<std::string, IsEmptyFnString> hash_set;
+ const std::string test_string = "hello world 1234";
+ ASSERT_TRUE(hash_set.Empty());
+ ASSERT_EQ(hash_set.Size(), 0U);
+ hash_set.Insert(test_string);
+ auto it = hash_set.Find(test_string);
+ ASSERT_EQ(*it, test_string);
+ auto after_it = hash_set.Erase(it);
+ ASSERT_TRUE(after_it == hash_set.end());
+ ASSERT_TRUE(hash_set.Empty());
+ ASSERT_EQ(hash_set.Size(), 0U);
+ it = hash_set.Find(test_string);
+ ASSERT_TRUE(it == hash_set.end());
+}
+
+TEST_F(HashSetTest, TestInsertAndErase) {
+ HashSet<std::string, IsEmptyFnString> hash_set;
+ static constexpr size_t count = 1000;
+ std::vector<std::string> strings;
+ for (size_t i = 0; i < count; ++i) {
+ // Insert a bunch of elements and make sure we can find them.
+ strings.push_back(RandomString(10));
+ hash_set.Insert(strings[i]);
+ auto it = hash_set.Find(strings[i]);
+ ASSERT_TRUE(it != hash_set.end());
+ ASSERT_EQ(*it, strings[i]);
+ }
+ ASSERT_EQ(strings.size(), hash_set.Size());
+ // Try to erase the odd strings.
+ for (size_t i = 1; i < count; i += 2) {
+ auto it = hash_set.Find(strings[i]);
+ ASSERT_TRUE(it != hash_set.end());
+ ASSERT_EQ(*it, strings[i]);
+ hash_set.Erase(it);
+ }
+ // Test removed.
+ for (size_t i = 1; i < count; i += 2) {
+ auto it = hash_set.Find(strings[i]);
+ ASSERT_TRUE(it == hash_set.end());
+ }
+ for (size_t i = 0; i < count; i += 2) {
+ auto it = hash_set.Find(strings[i]);
+ ASSERT_TRUE(it != hash_set.end());
+ ASSERT_EQ(*it, strings[i]);
+ }
+}
+
+TEST_F(HashSetTest, TestIterator) {
+ HashSet<std::string, IsEmptyFnString> hash_set;
+ ASSERT_TRUE(hash_set.begin() == hash_set.end());
+ static constexpr size_t count = 1000;
+ std::vector<std::string> strings;
+ for (size_t i = 0; i < count; ++i) {
+ // Insert a bunch of elements and make sure we can find them.
+ strings.push_back(RandomString(10));
+ hash_set.Insert(strings[i]);
+ }
+ // Make sure we visit each string exactly once.
+ std::map<std::string, size_t> found_count;
+ for (const std::string& s : hash_set) {
+ ++found_count[s];
+ }
+ for (size_t i = 0; i < count; ++i) {
+ ASSERT_EQ(found_count[strings[i]], 1U);
+ }
+ found_count.clear();
+ // Remove all the elements with iterator erase.
+ for (auto it = hash_set.begin(); it != hash_set.end();) {
+ ++found_count[*it];
+ it = hash_set.Erase(it);
+ ASSERT_EQ(hash_set.Verify(), 0U);
+ }
+ for (size_t i = 0; i < count; ++i) {
+ ASSERT_EQ(found_count[strings[i]], 1U);
+ }
+}
+
+TEST_F(HashSetTest, TestSwap) {
+ HashSet<std::string, IsEmptyFnString> hash_seta, hash_setb;
+ std::vector<std::string> strings;
+ static constexpr size_t count = 1000;
+ for (size_t i = 0; i < count; ++i) {
+ strings.push_back(RandomString(10));
+ hash_seta.Insert(strings[i]);
+ }
+ std::swap(hash_seta, hash_setb);
+ hash_seta.Insert("TEST");
+ hash_setb.Insert("TEST2");
+ for (size_t i = 0; i < count; ++i) {
+ strings.push_back(RandomString(10));
+ hash_seta.Insert(strings[i]);
+ }
+}
+
+TEST_F(HashSetTest, TestStress) {
+ HashSet<std::string, IsEmptyFnString> hash_set;
+ std::unordered_multiset<std::string> std_set;
+ std::vector<std::string> strings;
+ static constexpr size_t string_count = 2000;
+ static constexpr size_t operations = 100000;
+ static constexpr size_t target_size = 5000;
+ for (size_t i = 0; i < string_count; ++i) {
+ strings.push_back(RandomString(i % 10 + 1));
+ }
+ const size_t seed = time(nullptr);
+ SetSeed(seed);
+ LOG(INFO) << "Starting stress test with seed " << seed;
+ for (size_t i = 0; i < operations; ++i) {
+ ASSERT_EQ(hash_set.Size(), std_set.size());
+ size_t delta = std::abs(static_cast<ssize_t>(target_size) -
+ static_cast<ssize_t>(hash_set.Size()));
+ size_t n = PRand();
+ if (n % target_size == 0) {
+ hash_set.Clear();
+ std_set.clear();
+ ASSERT_TRUE(hash_set.Empty());
+ ASSERT_TRUE(std_set.empty());
+ } else if (n % target_size < delta) {
+ // Skew towards adding elements until we are at the desired size.
+ const std::string& s = strings[PRand() % string_count];
+ hash_set.Insert(s);
+ std_set.insert(s);
+ ASSERT_EQ(*hash_set.Find(s), *std_set.find(s));
+ } else {
+ const std::string& s = strings[PRand() % string_count];
+ auto it1 = hash_set.Find(s);
+ auto it2 = std_set.find(s);
+ ASSERT_EQ(it1 == hash_set.end(), it2 == std_set.end());
+ if (it1 != hash_set.end()) {
+ ASSERT_EQ(*it1, *it2);
+ hash_set.Erase(it1);
+ std_set.erase(it2);
+ }
+ }
+ }
+}
+
+struct IsEmptyStringPair {
+ void MakeEmpty(std::pair<std::string, int>& pair) const {
+ pair.first.clear();
+ }
+ bool IsEmpty(const std::pair<std::string, int>& pair) const {
+ return pair.first.empty();
+ }
+};
+
+TEST_F(HashSetTest, TestHashMap) {
+ HashMap<std::string, int, IsEmptyStringPair> hash_map;
+ hash_map.Insert(std::make_pair(std::string("abcd"), 123));
+ hash_map.Insert(std::make_pair(std::string("abcd"), 124));
+ hash_map.Insert(std::make_pair(std::string("bags"), 444));
+ auto it = hash_map.Find(std::string("abcd"));
+ ASSERT_EQ(it->second, 123);
+ hash_map.Erase(it);
+ it = hash_map.Find(std::string("abcd"));
+ ASSERT_EQ(it->second, 124);
+}
+
+} // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d718367..900bc9d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -128,15 +128,6 @@
}
}
-static size_t Hash(const char* s) {
- // This is the java.lang.String hashcode for convenience, not interoperability.
- size_t hash = 0;
- for (; *s != '\0'; ++s) {
- hash = hash * 31 + *s;
- }
- return hash;
-}
-
const char* ClassLinker::class_roots_descriptors_[] = {
"Ljava/lang/Class;",
"Ljava/lang/Object;",
@@ -372,6 +363,7 @@
Runtime* runtime = Runtime::Current();
runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
+ runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
runtime->SetDefaultImt(runtime->CreateDefaultImt(this));
// Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
@@ -964,7 +956,7 @@
uint32_t dex_location_checksum,
const char* oat_location,
std::string* error_msg) {
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
error_msg));
if (oat_file.get() == nullptr) {
@@ -1036,7 +1028,7 @@
error_msgs->push_back(error_msg);
return nullptr;
}
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
&error_msg));
if (oat_file.get() == nullptr) {
@@ -1097,9 +1089,13 @@
image_patch_delta = image_header->GetPatchDelta();
}
const OatHeader& oat_header = oat_file->GetOatHeader();
- bool ret = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
- && (oat_header.GetImagePatchDelta() == image_patch_delta)
- && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+ bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum);
+
+ // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks.
+ if (!oat_file->IsPic()) {
+ ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta)
+ && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin);
+ }
if (!ret) {
*error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
oat_file->GetLocation().c_str(),
@@ -1287,11 +1283,12 @@
bool odex_checksum_verified = false;
bool have_system_odex = false;
{
- // There is a high probability that these both these oat files map similar/the same address
+ // There is a high probability that both these oat files map similar/the same address
// spaces so we must scope them like this so they each gets its turn.
std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
+ nullptr,
executable, &odex_error_msg));
- if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
+ if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
&odex_checksum_verified,
&odex_error_msg)) {
error_msgs->push_back(odex_error_msg);
@@ -1313,8 +1310,9 @@
bool cache_checksum_verified = false;
if (have_dalvik_cache) {
std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
+ nullptr,
executable, &cache_error_msg));
- if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
+ if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
&cache_checksum_verified,
&cache_error_msg)) {
error_msgs->push_back(cache_error_msg);
@@ -1390,7 +1388,7 @@
InstructionSet isa,
std::string* error_msg) {
// We open it non-executable
- std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, false, error_msg));
+ std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
if (output.get() == nullptr) {
return nullptr;
}
@@ -1409,13 +1407,15 @@
const std::string& image_location,
InstructionSet isa,
std::string* error_msg) {
- if (!Runtime::Current()->GetHeap()->HasImageSpace()) {
+ Runtime* runtime = Runtime::Current();
+ DCHECK(runtime != nullptr);
+ if (!runtime->GetHeap()->HasImageSpace()) {
// We don't have an image space so there is no point in trying to patchoat.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
<< "running without an image. Attempting to use oat file for interpretation.";
return GetInterpretedOnlyOat(input_oat, isa, error_msg);
}
- if (!Runtime::Current()->IsDex2OatEnabled()) {
+ if (!runtime->IsDex2OatEnabled()) {
// We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
// input_oat but make sure we only do interpretation on it's dex files.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
@@ -1423,7 +1423,7 @@
return GetInterpretedOnlyOat(input_oat, isa, error_msg);
}
Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
- std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+ std::string patchoat(runtime->GetPatchoatExecutable());
std::string isa_arg("--instruction-set=");
isa_arg += GetInstructionSetString(isa);
@@ -1445,10 +1445,11 @@
LOG(INFO) << "Relocate Oat File: " << command_line;
bool success = Exec(argv, error_msg);
if (success) {
- std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr,
- !Runtime::Current()->IsCompiler(), error_msg));
+ std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
+ !runtime->IsCompiler(), error_msg));
bool checksum_verified = false;
- if (output.get() != nullptr && CheckOatFile(output.get(), isa, &checksum_verified, error_msg)) {
+ if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
+ error_msg)) {
return output.release();
} else if (output.get() != nullptr) {
*error_msg = StringPrintf("Patching of oat file '%s' succeeded "
@@ -1459,7 +1460,7 @@
"but was unable to open output file '%s': %s",
input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
}
- } else if (!Runtime::Current()->IsCompiler()) {
+ } else if (!runtime->IsCompiler()) {
// patchoat failed which means we probably don't have enough room to place the output oat file,
// instead of failing we should just run the interpreter from the dex files in the input oat.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
@@ -1473,32 +1474,14 @@
return nullptr;
}
-int32_t ClassLinker::GetRequiredDelta(const OatFile* oat_file, InstructionSet isa) {
- Runtime* runtime = Runtime::Current();
- int32_t real_patch_delta;
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
- CHECK(image_space != nullptr);
- if (isa == Runtime::Current()->GetInstructionSet()) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- real_patch_delta = image_header.GetPatchDelta();
- } else {
- std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
- image_space->GetImageLocation().c_str(), isa));
- real_patch_delta = image_header->GetPatchDelta();
- }
- const OatHeader& oat_header = oat_file->GetOatHeader();
- return real_patch_delta - oat_header.GetImagePatchDelta();
-}
-
-bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
bool* checksum_verified,
std::string* error_msg) {
std::string compound_msg("Oat file failed to verify: ");
- Runtime* runtime = Runtime::Current();
uint32_t real_image_checksum;
void* real_image_oat_offset;
int32_t real_patch_delta;
- const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ const gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
if (image_space == nullptr) {
*error_msg = "No image space present";
return false;
@@ -1525,19 +1508,28 @@
real_image_checksum, oat_image_checksum);
}
- void* oat_image_oat_offset =
- reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
- bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
- if (!offset_verified) {
- compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
- real_image_oat_offset, oat_image_oat_offset);
- }
+ bool offset_verified;
+ bool patch_delta_verified;
- int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
- bool patch_delta_verified = oat_patch_delta == real_patch_delta;
- if (!patch_delta_verified) {
- compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
- real_patch_delta, oat_patch_delta);
+ if (!oat_file->IsPic()) {
+ void* oat_image_oat_offset =
+ reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
+ offset_verified = oat_image_oat_offset == real_image_oat_offset;
+ if (!offset_verified) {
+ compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
+ real_image_oat_offset, oat_image_oat_offset);
+ }
+
+ int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+ patch_delta_verified = oat_patch_delta == real_patch_delta;
+ if (!patch_delta_verified) {
+ compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
+ real_patch_delta, oat_patch_delta);
+ }
+ } else {
+ // If an oat file is PIC, we ignore offset and patching delta.
+ offset_verified = true;
+ patch_delta_verified = true;
}
bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
@@ -1554,7 +1546,7 @@
return oat_file;
}
- return OatFile::Open(oat_location, oat_location, nullptr, !Runtime::Current()->IsCompiler(),
+ return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
error_msg);
}
@@ -1675,25 +1667,24 @@
void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
- for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
- it.second.VisitRoot(callback, arg, 0, kRootStickyClass);
+ for (GcRoot<mirror::Class>& root : class_table_) {
+ root.VisitRoot(callback, arg, 0, kRootStickyClass);
+ }
+ for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
+ root.VisitRoot(callback, arg, 0, kRootStickyClass);
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
- for (auto& pair : new_class_roots_) {
- mirror::Class* old_ref = pair.second.Read<kWithoutReadBarrier>();
- pair.second.VisitRoot(callback, arg, 0, kRootStickyClass);
- mirror::Class* new_ref = pair.second.Read<kWithoutReadBarrier>();
+ for (auto& root : new_class_roots_) {
+ mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
+ root.VisitRoot(callback, arg, 0, kRootStickyClass);
+ mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
if (UNLIKELY(new_ref != old_ref)) {
// Uh ohes, GC moved a root in the log. Need to search the class_table and update the
// corresponding object. This is slow, but luckily for us, this may only happen with a
// concurrent moving GC.
- for (auto it = class_table_.lower_bound(pair.first), end = class_table_.end();
- it != end && it->first == pair.first; ++it) {
- // If the class stored matches the old class, update it to the new value.
- if (old_ref == it->second.Read<kWithoutReadBarrier>()) {
- it->second = GcRoot<mirror::Class>(new_ref);
- }
- }
+ auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
+ class_table_.Erase(it);
+ class_table_.Insert(GcRoot<mirror::Class>(new_ref));
}
}
}
@@ -1751,9 +1742,13 @@
}
// TODO: why isn't this a ReaderMutexLock?
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
- mirror::Class* c = it.second.Read();
- if (!visitor(c, arg)) {
+ for (GcRoot<mirror::Class>& root : class_table_) {
+ if (!visitor(root.Read(), arg)) {
+ return;
+ }
+ }
+ for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
+ if (!visitor(root.Read(), arg)) {
return;
}
}
@@ -1809,7 +1804,7 @@
size_t class_table_size;
{
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- class_table_size = class_table_.size();
+ class_table_size = class_table_.Size() + pre_zygote_class_table_.Size();
}
mirror::Class* class_type = mirror::Class::GetJavaLangClass();
mirror::Class* array_of_class = FindArrayClass(self, &class_type);
@@ -1952,7 +1947,8 @@
}
CHECK(h_class->IsRetired());
// Get the updated class from class table.
- klass = LookupClass(descriptor, h_class.Get()->GetClassLoader());
+ klass = LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor),
+ h_class.Get()->GetClassLoader());
}
// Wait for the class if it has not already been linked.
@@ -1986,21 +1982,19 @@
// Search a collection of DexFiles for a descriptor
ClassPathEntry FindInClassPath(const char* descriptor,
- const std::vector<const DexFile*>& class_path) {
- for (size_t i = 0; i != class_path.size(); ++i) {
- const DexFile* dex_file = class_path[i];
- const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+ size_t hash, const std::vector<const DexFile*>& class_path) {
+ for (const DexFile* dex_file : class_path) {
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
return ClassPathEntry(dex_file, dex_class_def);
}
}
- // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
- return ClassPathEntry(static_cast<const DexFile*>(nullptr),
- static_cast<const DexFile::ClassDef*>(nullptr));
+ return ClassPathEntry(nullptr, nullptr);
}
mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self, const char* descriptor,
+ size_t hash,
Handle<mirror::ClassLoader> class_loader) {
if (class_loader->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
@@ -2008,14 +2002,14 @@
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
return nullptr;
}
- ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
// Check if this would be found in the parent boot class loader.
if (pair.second != nullptr) {
- mirror::Class* klass = LookupClass(descriptor, nullptr);
+ mirror::Class* klass = LookupClass(descriptor, hash, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
- klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
if (klass != nullptr) {
return klass;
@@ -2063,11 +2057,11 @@
break;
}
for (const DexFile* dex_file : *dex_files) {
- const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
RegisterDexFile(*dex_file);
- mirror::Class* klass =
- DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+ mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader, *dex_file,
+ *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
@@ -2094,19 +2088,21 @@
// for primitive classes that aren't backed by dex files.
return FindPrimitiveClass(descriptor[0]);
}
+ const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// Find the class in the loaded classes table.
- mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
+ mirror::Class* klass = LookupClass(descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
- return CreateArrayClass(self, descriptor, class_loader);
+ return CreateArrayClass(self, descriptor, hash, class_loader);
} else if (class_loader.Get() == nullptr) {
// The boot class loader, search the boot class path.
- ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
- return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+ return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
@@ -2118,16 +2114,17 @@
} else if (Runtime::Current()->UseCompileTimeClassPath()) {
// First try with the bootstrap class loader.
if (class_loader.Get() != nullptr) {
- klass = LookupClass(descriptor, nullptr);
+ klass = LookupClass(descriptor, hash, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
}
// If the lookup failed search the boot class path. We don't perform a recursive call to avoid
// a NoClassDefFoundError being allocated.
- ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
- return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+ return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
}
// Next try the compile time class path.
const std::vector<const DexFile*>* class_path;
@@ -2137,13 +2134,13 @@
soa.AddLocalReference<jobject>(class_loader.Get()));
class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
}
- pair = FindInClassPath(descriptor, *class_path);
+ pair = FindInClassPath(descriptor, hash, *class_path);
if (pair.second != nullptr) {
- return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
+ return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
}
} else {
ScopedObjectAccessUnchecked soa(self);
- mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, class_loader);
+ mirror::Class* klass = FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader);
if (klass != nullptr) {
return klass;
}
@@ -2182,14 +2179,12 @@
return nullptr;
}
-mirror::Class* ClassLinker::DefineClass(const char* descriptor,
+mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
- Thread* self = Thread::Current();
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
- bool should_allocate = false;
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
@@ -2208,14 +2203,10 @@
klass.Assign(GetClassRoot(kJavaLangReflectArtField));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
- } else {
- should_allocate = true;
}
- } else {
- should_allocate = true;
}
- if (should_allocate) {
+ if (klass.Get() == nullptr) {
// Allocate a class with the status of not ready.
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
@@ -2240,7 +2231,7 @@
klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
- mirror::Class* existing = InsertClass(descriptor, klass.Get(), Hash(descriptor));
+ mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
@@ -3035,7 +3026,8 @@
primitive_class->SetPrimitiveType(type);
primitive_class->SetStatus(mirror::Class::kStatusInitialized, self);
const char* descriptor = Primitive::Descriptor(type);
- mirror::Class* existing = InsertClass(descriptor, primitive_class, Hash(descriptor));
+ mirror::Class* existing = InsertClass(descriptor, primitive_class,
+ ComputeModifiedUtf8Hash(descriptor));
CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed";
return primitive_class;
}
@@ -3053,7 +3045,7 @@
// array class; that always comes from the base element class.
//
// Returns nullptr with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader) {
// Identify the underlying component type
CHECK_EQ('[', descriptor[0]);
@@ -3062,7 +3054,8 @@
if (component_type.Get() == nullptr) {
DCHECK(self->IsExceptionPending());
// We need to accept erroneous classes as component types.
- component_type.Assign(LookupClass(descriptor + 1, class_loader.Get()));
+ const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
+ component_type.Assign(LookupClass(descriptor + 1, component_hash, class_loader.Get()));
if (component_type.Get() == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
@@ -3092,7 +3085,7 @@
// class to the hash table --- necessary because of possible races with
// other threads.)
if (class_loader.Get() != component_type->GetClassLoader()) {
- mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader());
+ mirror::Class* new_class = LookupClass(descriptor, hash, component_type->GetClassLoader());
if (new_class != nullptr) {
return new_class;
}
@@ -3142,7 +3135,11 @@
new_class->SetPrimitiveType(Primitive::kPrimNot);
new_class->SetClassLoader(component_type->GetClassLoader());
new_class->SetStatus(mirror::Class::kStatusLoaded, self);
- new_class->PopulateEmbeddedImtAndVTable();
+ {
+ StackHandleScope<mirror::Class::kImtSize> hs(self,
+ Runtime::Current()->GetImtUnimplementedMethod());
+ new_class->PopulateEmbeddedImtAndVTable(&hs);
+ }
new_class->SetStatus(mirror::Class::kStatusInitialized, self);
// don't need to set new_class->SetObjectSize(..)
// because Object::SizeOf delegates to Array::SizeOf
@@ -3177,7 +3174,7 @@
new_class->SetAccessFlags(access_flags);
- mirror::Class* existing = InsertClass(descriptor, new_class.Get(), Hash(descriptor));
+ mirror::Class* existing = InsertClass(descriptor, new_class.Get(), hash);
if (existing == nullptr) {
return new_class.Get();
}
@@ -3230,8 +3227,7 @@
LOG(INFO) << "Loaded class " << descriptor << source;
}
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- mirror::Class* existing =
- LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
+ mirror::Class* existing = LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
if (existing != nullptr) {
return existing;
}
@@ -3241,13 +3237,13 @@
// is in the image.
existing = LookupClassFromImage(descriptor);
if (existing != nullptr) {
- CHECK(klass == existing);
+ CHECK_EQ(klass, existing);
}
}
VerifyObject(klass);
- class_table_.insert(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+ class_table_.InsertWithHash(GcRoot<mirror::Class>(klass), hash);
if (log_new_class_table_roots_) {
- new_class_roots_.push_back(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+ new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
}
return nullptr;
}
@@ -3255,27 +3251,18 @@
mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass,
size_t hash) {
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- mirror::Class* existing =
- LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
-
- if (existing == nullptr) {
+ auto existing_it = class_table_.FindWithHash(std::make_pair(descriptor, klass->GetClassLoader()),
+ hash);
+ if (existing_it == class_table_.end()) {
CHECK(klass->IsProxyClass());
return nullptr;
}
+ mirror::Class* existing = existing_it->Read();
CHECK_NE(existing, klass) << descriptor;
CHECK(!existing->IsResolved()) << descriptor;
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor;
- for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
- it != end && it->first == hash; ++it) {
- mirror::Class* klass = it->second.Read();
- if (klass == existing) {
- class_table_.erase(it);
- break;
- }
- }
-
CHECK(!klass->IsTemp()) << descriptor;
if (kIsDebugBuild && klass->GetClassLoader() == nullptr &&
dex_cache_image_class_lookup_required_) {
@@ -3283,37 +3270,38 @@
// is in the image.
existing = LookupClassFromImage(descriptor);
if (existing != nullptr) {
- CHECK(klass == existing) << descriptor;
+ CHECK_EQ(klass, existing) << descriptor;
}
}
VerifyObject(klass);
- class_table_.insert(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+ // Update the element in the hash set.
+ *existing_it = GcRoot<mirror::Class>(klass);
if (log_new_class_table_roots_) {
- new_class_roots_.push_back(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+ new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
}
return existing;
}
-bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) {
- size_t hash = Hash(descriptor);
+bool ClassLinker::RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader) {
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
- it != end && it->first == hash;
- ++it) {
- mirror::Class* klass = it->second.Read();
- if (klass->GetClassLoader() == class_loader && klass->DescriptorEquals(descriptor)) {
- class_table_.erase(it);
- return true;
- }
+ auto pair = std::make_pair(descriptor, class_loader);
+ auto it = class_table_.Find(pair);
+ if (it != class_table_.end()) {
+ class_table_.Erase(it);
+ return true;
+ }
+ it = pre_zygote_class_table_.Find(pair);
+ if (it != pre_zygote_class_table_.end()) {
+ pre_zygote_class_table_.Erase(it);
+ return true;
}
return false;
}
-mirror::Class* ClassLinker::LookupClass(const char* descriptor,
- const mirror::ClassLoader* class_loader) {
- size_t hash = Hash(descriptor);
+mirror::Class* ClassLinker::LookupClass(const char* descriptor, size_t hash,
+ mirror::ClassLoader* class_loader) {
{
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
@@ -3342,26 +3330,17 @@
}
mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
- const mirror::ClassLoader* class_loader,
+ mirror::ClassLoader* class_loader,
size_t hash) {
- auto end = class_table_.end();
- for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) {
- mirror::Class* klass = it->second.Read();
- if (klass->GetClassLoader() == class_loader && klass->DescriptorEquals(descriptor)) {
- if (kIsDebugBuild) {
- // Check for duplicates in the table.
- for (++it; it != end && it->first == hash; ++it) {
- mirror::Class* klass2 = it->second.Read();
- CHECK(!(klass2->GetClassLoader() == class_loader &&
- klass2->DescriptorEquals(descriptor)))
- << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
- << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
- }
- }
- return klass;
+ auto descriptor_pair = std::make_pair(descriptor, class_loader);
+ auto it = pre_zygote_class_table_.FindWithHash(descriptor_pair, hash);
+ if (it == pre_zygote_class_table_.end()) {
+ it = class_table_.FindWithHash(descriptor_pair, hash);
+ if (it == class_table_.end()) {
+ return nullptr;
}
}
- return nullptr;
+ return it->Read();
}
static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches()
@@ -3390,15 +3369,15 @@
if (klass != nullptr) {
DCHECK(klass->GetClassLoader() == nullptr);
const char* descriptor = klass->GetDescriptor(&temp);
- size_t hash = Hash(descriptor);
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
mirror::Class* existing = LookupClassFromTableLocked(descriptor, nullptr, hash);
if (existing != nullptr) {
- CHECK(existing == klass) << PrettyClassAndClassLoader(existing) << " != "
+ CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != "
<< PrettyClassAndClassLoader(klass);
} else {
- class_table_.insert(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+ class_table_.Insert(GcRoot<mirror::Class>(klass));
if (log_new_class_table_roots_) {
- new_class_roots_.push_back(std::make_pair(hash, GcRoot<mirror::Class>(klass)));
+ new_class_roots_.push_back(GcRoot<mirror::Class>(klass));
}
}
}
@@ -3408,6 +3387,13 @@
self->EndAssertNoThreadSuspension(old_no_suspend_cause);
}
+void ClassLinker::MoveClassTableToPreZygote() {
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ DCHECK(pre_zygote_class_table_.Empty());
+ pre_zygote_class_table_ = std::move(class_table_);
+ class_table_.Clear();
+}
+
mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
Thread* self = Thread::Current();
const char* old_no_suspend_cause =
@@ -3440,14 +3426,32 @@
if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
- size_t hash = Hash(descriptor);
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
- it != end && it->first == hash; ++it) {
- mirror::Class* klass = it->second.Read();
- if (klass->DescriptorEquals(descriptor)) {
- result.push_back(klass);
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ while (true) {
+ auto it = class_table_.Find(descriptor);
+ if (it == class_table_.end()) {
+ break;
}
+ result.push_back(it->Read());
+ class_table_.Erase(it);
+ }
+ for (mirror::Class* k : result) {
+ class_table_.Insert(GcRoot<mirror::Class>(k));
+ }
+ size_t pre_zygote_start = result.size();
+ // Now handle the pre zygote table.
+ // Note: This dirties the pre-zygote table but shouldn't be an issue since LookupClasses is only
+ // called from the debugger.
+ while (true) {
+ auto it = pre_zygote_class_table_.Find(descriptor);
+ if (it == pre_zygote_class_table_.end()) {
+ break;
+ }
+ result.push_back(it->Read());
+ pre_zygote_class_table_.Erase(it);
+ }
+ for (size_t i = pre_zygote_start; i < result.size(); ++i) {
+ pre_zygote_class_table_.Insert(GcRoot<mirror::Class>(result[i]));
}
}
@@ -3872,7 +3876,8 @@
CHECK_EQ(klass.Get()->GetThrows(),
soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
}
- mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), Hash(descriptor.c_str()));
+ mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(),
+ ComputeModifiedUtf8Hash(descriptor.c_str()));
CHECK(existing == nullptr);
return klass.Get();
}
@@ -4144,13 +4149,28 @@
}
}
- if (klass->NumStaticFields() > 0) {
+ const size_t num_static_fields = klass->NumStaticFields();
+ if (num_static_fields > 0) {
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
CHECK(dex_class_def != nullptr);
const DexFile& dex_file = klass->GetDexFile();
StackHandleScope<2> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
+
+ // Eagerly fill in static fields so that the we don't have to do as many expensive
+ // Class::FindStaticField in ResolveField.
+ for (size_t i = 0; i < num_static_fields; ++i) {
+ mirror::ArtField* field = klass->GetStaticField(i);
+ const uint32_t field_idx = field->GetDexFieldIndex();
+ mirror::ArtField* resolved_field = dex_cache->GetResolvedField(field_idx);
+ if (resolved_field == nullptr) {
+ dex_cache->SetResolvedField(field_idx, field);
+ } else {
+ DCHECK_EQ(field, resolved_field);
+ }
+ }
+
EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader,
this, *dex_class_def);
if (it.HasNext()) {
@@ -4371,7 +4391,9 @@
if (!LinkSuperClass(klass)) {
return false;
}
- if (!LinkMethods(self, klass, interfaces)) {
+ StackHandleScope<mirror::Class::kImtSize> imt_handle_scope(
+ self, Runtime::Current()->GetImtUnimplementedMethod());
+ if (!LinkMethods(self, klass, interfaces, &imt_handle_scope)) {
return false;
}
if (!LinkInstanceFields(klass)) {
@@ -4391,7 +4413,7 @@
CHECK_EQ(klass->GetClassSize(), class_size) << PrettyDescriptor(klass.Get());
if (klass->ShouldHaveEmbeddedImtAndVTable()) {
- klass->PopulateEmbeddedImtAndVTable();
+ klass->PopulateEmbeddedImtAndVTable(&imt_handle_scope);
}
// This will notify waiters on klass that saw the not yet resolved
@@ -4401,7 +4423,7 @@
} else {
CHECK(!klass->IsResolved());
// Retire the temporary class and create the correctly sized resolved class.
- *new_class = klass->CopyOf(self, class_size);
+ *new_class = klass->CopyOf(self, class_size, &imt_handle_scope);
if (UNLIKELY(*new_class == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
klass->SetStatus(mirror::Class::kStatusError, self);
@@ -4415,7 +4437,8 @@
FixupTemporaryDeclaringClass(klass.Get(), new_class_h.Get());
- mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(), Hash(descriptor));
+ mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(),
+ ComputeModifiedUtf8Hash(descriptor));
CHECK(existing == nullptr || existing == klass.Get());
// This will notify waiters on temp class that saw the not yet resolved class in the
@@ -4535,7 +4558,8 @@
// Populate the class vtable and itable. Compute return type indices.
bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ StackHandleScope<mirror::Class::kImtSize>* out_imt) {
if (klass->IsInterface()) {
// No vtable.
size_t count = klass->NumVirtualMethods();
@@ -4546,22 +4570,127 @@
for (size_t i = 0; i < count; ++i) {
klass->GetVirtualMethodDuringLinking(i)->SetMethodIndex(i);
}
- // Link interface method tables
- return LinkInterfaceMethods(klass, interfaces);
- } else {
- // Link virtual and interface method tables
- return LinkVirtualMethods(self, klass) && LinkInterfaceMethods(klass, interfaces);
+ } else if (!LinkVirtualMethods(self, klass)) { // Link virtual methods first.
+ return false;
}
- return true;
+ return LinkInterfaceMethods(self, klass, interfaces, out_imt); // Link interface method last.
}
+// Comparator for name and signature of a method, used in finding overriding methods. Implementation
+// avoids the use of handles, if it didn't then rather than compare dex files we could compare dex
+// caches in the implementation below.
+class MethodNameAndSignatureComparator FINAL {
+ public:
+ explicit MethodNameAndSignatureComparator(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+ dex_file_(method->GetDexFile()), mid_(&dex_file_->GetMethodId(method->GetDexMethodIndex())),
+ name_(nullptr), name_len_(0) {
+ DCHECK(!method->IsProxyMethod()) << PrettyMethod(method);
+ }
+
+ const char* GetName() {
+ if (name_ == nullptr) {
+ name_ = dex_file_->StringDataAndUtf16LengthByIdx(mid_->name_idx_, &name_len_);
+ }
+ return name_;
+ }
+
+ bool HasSameNameAndSignature(mirror::ArtMethod* other)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(!other->IsProxyMethod()) << PrettyMethod(other);
+ const DexFile* other_dex_file = other->GetDexFile();
+ const DexFile::MethodId& other_mid = other_dex_file->GetMethodId(other->GetDexMethodIndex());
+ if (dex_file_ == other_dex_file) {
+ return mid_->name_idx_ == other_mid.name_idx_ && mid_->proto_idx_ == other_mid.proto_idx_;
+ }
+ GetName(); // Only used to make sure its calculated.
+ uint32_t other_name_len;
+ const char* other_name = other_dex_file->StringDataAndUtf16LengthByIdx(other_mid.name_idx_,
+ &other_name_len);
+ if (name_len_ != other_name_len || strcmp(name_, other_name) != 0) {
+ return false;
+ }
+ return dex_file_->GetMethodSignature(*mid_) == other_dex_file->GetMethodSignature(other_mid);
+ }
+
+ private:
+ // Dex file for the method to compare against.
+ const DexFile* const dex_file_;
+ // MethodId for the method to compare against.
+ const DexFile::MethodId* const mid_;
+ // Lazily computed name from the dex file's strings.
+ const char* name_;
+ // Lazily computed name length.
+ uint32_t name_len_;
+};
+
+class LinkVirtualHashTable {
+ public:
+ LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table)
+ : klass_(klass), hash_size_(hash_size), hash_table_(hash_table) {
+ std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_);
+ }
+ void Add(uint32_t virtual_method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking(virtual_method_index);
+ const char* name = local_method->GetName();
+ uint32_t hash = ComputeModifiedUtf8Hash(name);
+ uint32_t index = hash % hash_size_;
+ // Linear probe until we have an empty slot.
+ while (hash_table_[index] != invalid_index_) {
+ if (++index == hash_size_) {
+ index = 0;
+ }
+ }
+ hash_table_[index] = virtual_method_index;
+ }
+ uint32_t FindAndRemove(MethodNameAndSignatureComparator* comparator)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const char* name = comparator->GetName();
+ uint32_t hash = ComputeModifiedUtf8Hash(name);
+ size_t index = hash % hash_size_;
+ while (true) {
+ const uint32_t value = hash_table_[index];
+ // Since linear probe makes continuous blocks, hitting an invalid index means we are done
+ // the block and can safely assume not found.
+ if (value == invalid_index_) {
+ break;
+ }
+ if (value != removed_index_) { // This signifies not already overriden.
+ mirror::ArtMethod* virtual_method =
+ klass_->GetVirtualMethodDuringLinking(value);
+ if (comparator->HasSameNameAndSignature(virtual_method->GetInterfaceMethodIfProxy())) {
+ hash_table_[index] = removed_index_;
+ return value;
+ }
+ }
+ if (++index == hash_size_) {
+ index = 0;
+ }
+ }
+ return GetNotFoundIndex();
+ }
+ static uint32_t GetNotFoundIndex() {
+ return invalid_index_;
+ }
+
+ private:
+ static const uint32_t invalid_index_;
+ static const uint32_t removed_index_;
+
+ Handle<mirror::Class> klass_;
+ const size_t hash_size_;
+ uint32_t* const hash_table_;
+};
+
+const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits<uint32_t>::max();
+const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits<uint32_t>::max() - 1;
+
bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {
+ const size_t num_virtual_methods = klass->NumVirtualMethods();
if (klass->HasSuperClass()) {
- uint32_t max_count = klass->NumVirtualMethods() +
- klass->GetSuperClass()->GetVTableLength();
- size_t actual_count = klass->GetSuperClass()->GetVTableLength();
- CHECK_LE(actual_count, max_count);
- StackHandleScope<4> hs(self);
+ const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
+ const size_t max_count = num_virtual_methods + super_vtable_length;
+ StackHandleScope<2> hs(self);
Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass()));
Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
@@ -4570,54 +4699,90 @@
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
- int len = super_class->GetVTableLength();
- for (int i = 0; i < len; i++) {
- vtable->Set<false>(i, super_class->GetVTableEntry(i));
+ for (size_t i = 0; i < super_vtable_length; i++) {
+ vtable->SetWithoutChecks<false>(i, super_class->GetEmbeddedVTableEntry(i));
+ }
+ if (num_virtual_methods == 0) {
+ klass->SetVTable(vtable.Get());
+ return true;
}
} else {
- CHECK(super_class->GetVTable() != nullptr) << PrettyClass(super_class.Get());
- vtable = hs.NewHandle(super_class->GetVTable()->CopyOf(self, max_count));
+ mirror::ObjectArray<mirror::ArtMethod>* super_vtable = super_class->GetVTable();
+ CHECK(super_vtable != nullptr) << PrettyClass(super_class.Get());
+ if (num_virtual_methods == 0) {
+ klass->SetVTable(super_vtable);
+ return true;
+ }
+ vtable = hs.NewHandle(super_vtable->CopyOf(self, max_count));
if (UNLIKELY(vtable.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
}
-
- // See if any of our virtual methods override the superclass.
- MethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
- mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i);
- local_mh.ChangeMethod(local_method);
- size_t j = 0;
- for (; j < actual_count; ++j) {
- mirror::ArtMethod* super_method = vtable->Get(j);
- super_mh.ChangeMethod(super_method);
- if (local_mh.HasSameNameAndSignature(&super_mh)) {
- if (klass->CanAccessMember(super_method->GetDeclaringClass(),
- super_method->GetAccessFlags())) {
- if (super_method->IsFinal()) {
- ThrowLinkageError(klass.Get(), "Method %s overrides final method in class %s",
- PrettyMethod(local_method).c_str(),
- super_method->GetDeclaringClassDescriptor());
- return false;
- }
- vtable->Set<false>(j, local_method);
- local_method->SetMethodIndex(j);
- break;
- } else {
- LOG(WARNING) << "Before Android 4.1, method " << PrettyMethod(local_method)
- << " would have incorrectly overridden the package-private method in "
- << PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
+ // How the algorithm works:
+ // 1. Populate hash table by adding num_virtual_methods from klass. The values in the hash
+ // table are: invalid_index for unused slots, index super_vtable_length + i for a virtual
+ // method which has not been matched to a vtable method, and j if the virtual method at the
+ // index overrode the super virtual method at index j.
+ // 2. Loop through super virtual methods, if they overwrite, update hash table to j
+ // (j < super_vtable_length) to avoid redundant checks. (TODO maybe use this info for reducing
+ // the need for the initial vtable which we later shrink back down).
+ // 3. Add non overridden methods to the end of the vtable.
+ static constexpr size_t kMaxStackHash = 250;
+ const size_t hash_table_size = num_virtual_methods * 3;
+ uint32_t* hash_table_ptr;
+ std::unique_ptr<uint32_t[]> hash_heap_storage;
+ if (hash_table_size <= kMaxStackHash) {
+ hash_table_ptr = reinterpret_cast<uint32_t*>(
+ alloca(hash_table_size * sizeof(*hash_table_ptr)));
+ } else {
+ hash_heap_storage.reset(new uint32_t[hash_table_size]);
+ hash_table_ptr = hash_heap_storage.get();
+ }
+ LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr);
+ // Add virtual methods to the hash table.
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ hash_table.Add(i);
+ }
+ // Loop through each super vtable method and see if they are overriden by a method we added to
+ // the hash table.
+ for (size_t j = 0; j < super_vtable_length; ++j) {
+ // Search the hash table to see if we are overidden by any method.
+ mirror::ArtMethod* super_method = vtable->GetWithoutChecks(j);
+ MethodNameAndSignatureComparator super_method_name_comparator(
+ super_method->GetInterfaceMethodIfProxy());
+ uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator);
+ if (hash_index != hash_table.GetNotFoundIndex()) {
+ mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(hash_index);
+ if (klass->CanAccessMember(super_method->GetDeclaringClass(),
+ super_method->GetAccessFlags())) {
+ if (super_method->IsFinal()) {
+ ThrowLinkageError(klass.Get(), "Method %s overrides final method in class %s",
+ PrettyMethod(virtual_method).c_str(),
+ super_method->GetDeclaringClassDescriptor());
+ return false;
}
+ vtable->SetWithoutChecks<false>(j, virtual_method);
+ virtual_method->SetMethodIndex(j);
+ } else {
+ LOG(WARNING) << "Before Android 4.1, method " << PrettyMethod(virtual_method)
+ << " would have incorrectly overridden the package-private method in "
+ << PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
}
}
- if (j == actual_count) {
- // Not overriding, append.
- vtable->Set<false>(actual_count, local_method);
- local_method->SetMethodIndex(actual_count);
- actual_count += 1;
+ }
+ // Add the non overridden methods at the end.
+ size_t actual_count = super_vtable_length;
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i);
+ size_t method_idx = local_method->GetMethodIndexDuringLinking();
+ if (method_idx < super_vtable_length &&
+ local_method == vtable->GetWithoutChecks(method_idx)) {
+ continue;
}
+ vtable->SetWithoutChecks<false>(actual_count, local_method);
+ local_method->SetMethodIndex(actual_count);
+ ++actual_count;
}
if (!IsUint(16, actual_count)) {
ThrowClassFormatError(klass.Get(), "Too many methods defined on class: %zd", actual_count);
@@ -4635,72 +4800,72 @@
klass->SetVTable(vtable.Get());
} else {
CHECK_EQ(klass.Get(), GetClassRoot(kJavaLangObject));
- uint32_t num_virtual_methods = klass->NumVirtualMethods();
if (!IsUint(16, num_virtual_methods)) {
- ThrowClassFormatError(klass.Get(), "Too many methods: %d", num_virtual_methods);
+ ThrowClassFormatError(klass.Get(), "Too many methods: %d",
+ static_cast<int>(num_virtual_methods));
return false;
}
- StackHandleScope<1> hs(self);
- Handle<mirror::ObjectArray<mirror::ArtMethod>>
- vtable(hs.NewHandle(AllocArtMethodArray(self, num_virtual_methods)));
- if (UNLIKELY(vtable.Get() == nullptr)) {
+ mirror::ObjectArray<mirror::ArtMethod>* vtable = AllocArtMethodArray(self, num_virtual_methods);
+ if (UNLIKELY(vtable == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
for (size_t i = 0; i < num_virtual_methods; ++i) {
mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i);
- vtable->Set<false>(i, virtual_method);
+ vtable->SetWithoutChecks<false>(i, virtual_method);
virtual_method->SetMethodIndex(i & 0xFFFF);
}
- klass->SetVTable(vtable.Get());
+ klass->SetVTable(vtable);
}
return true;
}
-bool ClassLinker::LinkInterfaceMethods(Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
- Thread* const self = Thread::Current();
+bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ StackHandleScope<mirror::Class::kImtSize>* out_imt) {
+ StackHandleScope<2> hs(self);
Runtime* const runtime = Runtime::Current();
- // Set the imt table to be all conflicts by default.
- klass->SetImTable(runtime->GetDefaultImt());
- size_t super_ifcount;
- if (klass->HasSuperClass()) {
- super_ifcount = klass->GetSuperClass()->GetIfTableCount();
- } else {
- super_ifcount = 0;
- }
- uint32_t num_interfaces =
- interfaces.Get() == nullptr ? klass->NumDirectInterfaces() : interfaces->GetLength();
- size_t ifcount = super_ifcount + num_interfaces;
- for (size_t i = 0; i < num_interfaces; i++) {
- mirror::Class* interface =
- interfaces.Get() == nullptr ? mirror::Class::GetDirectInterface(self, klass, i) :
- interfaces->Get(i);
- ifcount += interface->GetIfTableCount();
- }
- if (ifcount == 0) {
- // Class implements no interfaces.
- DCHECK_EQ(klass->GetIfTableCount(), 0);
- DCHECK(klass->GetIfTable() == nullptr);
- return true;
- }
- if (ifcount == super_ifcount) {
+ const bool has_superclass = klass->HasSuperClass();
+ const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+ const bool have_interfaces = interfaces.Get() != nullptr;
+ const size_t num_interfaces =
+ have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
+ if (num_interfaces == 0) {
+ if (super_ifcount == 0) {
+ // Class implements no interfaces.
+ DCHECK_EQ(klass->GetIfTableCount(), 0);
+ DCHECK(klass->GetIfTable() == nullptr);
+ return true;
+ }
// Class implements same interfaces as parent, are any of these not marker interfaces?
bool has_non_marker_interface = false;
mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();
- for (size_t i = 0; i < ifcount; ++i) {
+ for (size_t i = 0; i < super_ifcount; ++i) {
if (super_iftable->GetMethodArrayCount(i) > 0) {
has_non_marker_interface = true;
break;
}
}
+ // Class just inherits marker interfaces from parent so recycle parent's iftable.
if (!has_non_marker_interface) {
- // Class just inherits marker interfaces from parent so recycle parent's iftable.
klass->SetIfTable(super_iftable);
return true;
}
}
- StackHandleScope<4> hs(self);
+ size_t ifcount = super_ifcount + num_interfaces;
+ for (size_t i = 0; i < num_interfaces; i++) {
+ mirror::Class* interface = have_interfaces ?
+ interfaces->GetWithoutChecks(i) : mirror::Class::GetDirectInterface(self, klass, i);
+ DCHECK(interface != nullptr);
+ if (UNLIKELY(!interface->IsInterface())) {
+ std::string temp;
+ ThrowIncompatibleClassChangeError(klass.Get(), "Class %s implements non-interface class %s",
+ PrettyDescriptor(klass.Get()).c_str(),
+ PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
+ return false;
+ }
+ ifcount += interface->GetIfTableCount();
+ }
Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
if (UNLIKELY(iftable.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
@@ -4716,17 +4881,8 @@
// Flatten the interface inheritance hierarchy.
size_t idx = super_ifcount;
for (size_t i = 0; i < num_interfaces; i++) {
- mirror::Class* interface =
- interfaces.Get() == nullptr ? mirror::Class::GetDirectInterface(self, klass, i) :
- interfaces->Get(i);
- DCHECK(interface != nullptr);
- if (!interface->IsInterface()) {
- std::string temp;
- ThrowIncompatibleClassChangeError(klass.Get(), "Class %s implements non-interface class %s",
- PrettyDescriptor(klass.Get()).c_str(),
- PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
- return false;
- }
+ mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
+ mirror::Class::GetDirectInterface(self, klass, i);
// Check if interface is already in iftable
bool duplicate = false;
for (size_t j = 0; j < idx; j++) {
@@ -4758,6 +4914,7 @@
}
// Shrink iftable in case duplicates were found
if (idx < ifcount) {
+ DCHECK_NE(num_interfaces, 0U);
iftable.Assign(down_cast<mirror::IfTable*>(iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
if (UNLIKELY(iftable.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
@@ -4765,41 +4922,94 @@
}
ifcount = idx;
} else {
- CHECK_EQ(idx, ifcount);
+ DCHECK_EQ(idx, ifcount);
}
klass->SetIfTable(iftable.Get());
-
// If we're an interface, we don't need the vtable pointers, so we're done.
if (klass->IsInterface()) {
return true;
}
- // Allocate imtable
- bool imtable_changed = false;
- Handle<mirror::ObjectArray<mirror::ArtMethod>> imtable(
- hs.NewHandle(AllocArtMethodArray(self, mirror::Class::kImtSize)));
- if (UNLIKELY(imtable.Get() == nullptr)) {
- CHECK(self->IsExceptionPending()); // OOME.
- return false;
- }
- MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- MethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
+ hs.NewHandle(klass->GetVTableDuringLinking()));
std::vector<mirror::ArtMethod*> miranda_list;
+ // Copy the IMT from the super class if possible.
+ bool extend_super_iftable = false;
+ if (has_superclass) {
+ mirror::Class* super_class = klass->GetSuperClass();
+ extend_super_iftable = true;
+ if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
+ for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+ out_imt->SetReference(i, super_class->GetEmbeddedImTableEntry(i));
+ }
+ } else {
+ // No imt in the super class, need to reconstruct from the iftable.
+ mirror::IfTable* if_table = super_class->GetIfTable();
+ mirror::ArtMethod* conflict_method = runtime->GetImtConflictMethod();
+ const size_t length = super_class->GetIfTableCount();
+ for (size_t i = 0; i < length; ++i) {
+ mirror::Class* interface = iftable->GetInterface(i);
+ const size_t num_virtuals = interface->NumVirtualMethods();
+ const size_t method_array_count = if_table->GetMethodArrayCount(i);
+ DCHECK_EQ(num_virtuals, method_array_count);
+ if (method_array_count == 0) {
+ continue;
+ }
+ mirror::ObjectArray<mirror::ArtMethod>* method_array = if_table->GetMethodArray(i);
+ for (size_t j = 0; j < num_virtuals; ++j) {
+ mirror::ArtMethod* method = method_array->GetWithoutChecks(j);
+ if (method->IsMiranda()) {
+ continue;
+ }
+ mirror::ArtMethod* interface_method = interface->GetVirtualMethod(j);
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ mirror::ArtMethod* imt_ref = out_imt->GetReference(imt_index)->AsArtMethod();
+ if (imt_ref == runtime->GetImtUnimplementedMethod()) {
+ out_imt->SetReference(imt_index, method);
+ } else if (imt_ref != conflict_method) {
+ out_imt->SetReference(imt_index, conflict_method);
+ }
+ }
+ }
+ }
+ }
for (size_t i = 0; i < ifcount; ++i) {
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
StackHandleScope<2> hs(self);
- Handle<mirror::ObjectArray<mirror::ArtMethod>>
- method_array(hs.NewHandle(AllocArtMethodArray(self, num_methods)));
+ const bool is_super = i < super_ifcount;
+ const bool super_interface = is_super && extend_super_iftable;
+ Handle<mirror::ObjectArray<mirror::ArtMethod>> method_array;
+ Handle<mirror::ObjectArray<mirror::ArtMethod>> input_array;
+ if (super_interface) {
+ mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable();
+ DCHECK(if_table != nullptr);
+ DCHECK(if_table->GetMethodArray(i) != nullptr);
+ // If we are working on a super interface, try extending the existing method array.
+ method_array = hs.NewHandle(if_table->GetMethodArray(i)->Clone(self)->
+ AsObjectArray<mirror::ArtMethod>());
+ // We are overwriting a super class interface, try to only virtual methods instead of the
+ // whole vtable.
+ input_array = hs.NewHandle(klass->GetVirtualMethods());
+ } else {
+ method_array = hs.NewHandle(AllocArtMethodArray(self, num_methods));
+ // A new interface, we need the whole vtable incase a new interface method is implemented
+ // in the whole superclass.
+ input_array = vtable;
+ }
if (UNLIKELY(method_array.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
iftable->SetMethodArray(i, method_array.Get());
- Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
- hs.NewHandle(klass->GetVTableDuringLinking()));
+ if (input_array.Get() == nullptr) {
+ // If the added virtual methods is empty, do nothing.
+ DCHECK(super_interface);
+ continue;
+ }
for (size_t j = 0; j < num_methods; ++j) {
mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j);
- interface_mh.ChangeMethod(interface_method);
+ MethodNameAndSignatureComparator interface_name_comparator(
+ interface_method->GetInterfaceMethodIfProxy());
int32_t k;
// For each method listed in the interface's method list, find the
// matching method in our class's method list. We want to favor the
@@ -4809,10 +5019,12 @@
// it -- otherwise it would use the same vtable slot. In .dex files
// those don't end up in the virtual method table, so it shouldn't
// matter which direction we go. We walk it backward anyway.)
- for (k = vtable->GetLength() - 1; k >= 0; --k) {
- mirror::ArtMethod* vtable_method = vtable->Get(k);
- vtable_mh.ChangeMethod(vtable_method);
- if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
+ for (k = input_array->GetLength() - 1; k >= 0; --k) {
+ mirror::ArtMethod* vtable_method = input_array->GetWithoutChecks(k);
+ mirror::ArtMethod* vtable_method_for_name_comparison =
+ vtable_method->GetInterfaceMethodIfProxy();
+ if (interface_name_comparator.HasSameNameAndSignature(
+ vtable_method_for_name_comparison)) {
if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
ThrowIllegalAccessError(
klass.Get(),
@@ -4821,53 +5033,52 @@
PrettyMethod(interface_method).c_str());
return false;
}
- method_array->Set<false>(j, vtable_method);
+ method_array->SetWithoutChecks<false>(j, vtable_method);
// Place method in imt if entry is empty, place conflict otherwise.
uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
- if (imtable->Get(imt_index) == nullptr) {
- imtable->Set<false>(imt_index, vtable_method);
- imtable_changed = true;
- } else {
- imtable->Set<false>(imt_index, runtime->GetImtConflictMethod());
+ mirror::ArtMethod* imt_ref = out_imt->GetReference(imt_index)->AsArtMethod();
+ mirror::ArtMethod* conflict_method = runtime->GetImtConflictMethod();
+ if (imt_ref == runtime->GetImtUnimplementedMethod()) {
+ out_imt->SetReference(imt_index, vtable_method);
+ } else if (imt_ref != conflict_method) {
+ // If we are not a conflict and we have the same signature and name as the imt entry,
+ // it must be that we overwrote a superclass vtable entry.
+ MethodNameAndSignatureComparator imt_ref_name_comparator(
+ imt_ref->GetInterfaceMethodIfProxy());
+ if (imt_ref_name_comparator.HasSameNameAndSignature(
+ vtable_method_for_name_comparison)) {
+ out_imt->SetReference(imt_index, vtable_method);
+ } else {
+ out_imt->SetReference(imt_index, conflict_method);
+ }
}
break;
}
}
- if (k < 0) {
- StackHandleScope<1> hs(self);
- auto miranda_method = hs.NewHandle<mirror::ArtMethod>(nullptr);
+ if (k < 0 && !super_interface) {
+ mirror::ArtMethod* miranda_method = nullptr;
for (mirror::ArtMethod* mir_method : miranda_list) {
- vtable_mh.ChangeMethod(mir_method);
- if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
- miranda_method.Assign(mir_method);
+ if (interface_name_comparator.HasSameNameAndSignature(
+ mir_method->GetInterfaceMethodIfProxy())) {
+ miranda_method = mir_method;
break;
}
}
- if (miranda_method.Get() == nullptr) {
+ if (miranda_method == nullptr) {
// Point the interface table at a phantom slot.
- miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_method->Clone(self)));
- if (UNLIKELY(miranda_method.Get() == nullptr)) {
+ miranda_method = down_cast<mirror::ArtMethod*>(interface_method->Clone(self));
+ if (UNLIKELY(miranda_method == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
// TODO: If a methods move then the miranda_list may hold stale references.
- miranda_list.push_back(miranda_method.Get());
+ miranda_list.push_back(miranda_method);
}
- method_array->Set<false>(j, miranda_method.Get());
+ method_array->SetWithoutChecks<false>(j, miranda_method);
}
}
}
}
- if (imtable_changed) {
- // Fill in empty entries in interface method table with conflict.
- mirror::ArtMethod* imt_conflict_method = runtime->GetImtConflictMethod();
- for (size_t i = 0; i < mirror::Class::kImtSize; i++) {
- if (imtable->Get(i) == nullptr) {
- imtable->Set<false>(i, imt_conflict_method);
- }
- }
- klass->SetImTable(imtable.Get());
- }
if (!miranda_list.empty()) {
int old_method_count = klass->NumVirtualMethods();
int new_method_count = old_method_count + miranda_list.size();
@@ -4883,10 +5094,6 @@
}
klass->SetVirtualMethods(virtuals);
- StackHandleScope<1> hs(self);
- Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
- hs.NewHandle(klass->GetVTableDuringLinking()));
- CHECK(vtable.Get() != nullptr);
int old_vtable_count = vtable->GetLength();
int new_vtable_count = old_vtable_count + miranda_list.size();
vtable.Assign(vtable->CopyOf(self, new_vtable_count));
@@ -4900,19 +5107,19 @@
method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
klass->SetVirtualMethod(old_method_count + i, method);
- vtable->Set<false>(old_vtable_count + i, method);
+ vtable->SetWithoutChecks<false>(old_vtable_count + i, method);
}
// TODO: do not assign to the vtable field until it is fully constructed.
klass->SetVTable(vtable.Get());
}
- mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking();
- for (int i = 0; i < vtable->GetLength(); ++i) {
- CHECK(vtable->Get(i) != nullptr);
+ if (kIsDebugBuild) {
+ mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking();
+ for (int i = 0; i < vtable->GetLength(); ++i) {
+ CHECK(vtable->GetWithoutChecks(i) != nullptr);
+ }
}
-// klass->DumpClass(std::cerr, Class::kDumpClassFullDetail);
-
return true;
}
@@ -5462,9 +5669,8 @@
std::vector<mirror::Class*> all_classes;
{
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (std::pair<const size_t, GcRoot<mirror::Class> >& it : class_table_) {
- mirror::Class* klass = it.second.Read();
- all_classes.push_back(klass);
+ for (GcRoot<mirror::Class>& it : class_table_) {
+ all_classes.push_back(it.Read());
}
}
@@ -5478,7 +5684,8 @@
MoveImageClassesToClassTable();
}
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- os << "Loaded classes: " << class_table_.size() << " allocated classes\n";
+ os << "Zygote loaded classes=" << pre_zygote_class_table_.Size() << " post zygote classes="
+ << class_table_.Size() << "\n";
}
size_t ClassLinker::NumLoadedClasses() {
@@ -5486,7 +5693,8 @@
MoveImageClassesToClassTable();
}
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- return class_table_.size();
+ // Only return non zygote classes since these are the ones which apps which care about.
+ return class_table_.Size();
}
pid_t ClassLinker::GetClassesLockOwner() {
@@ -5509,4 +5717,41 @@
class_roots->Set<false>(class_root, klass);
}
+std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& root)
+ const {
+ std::string temp;
+ return ComputeModifiedUtf8Hash(root.Read()->GetDescriptor(&temp));
+}
+
+bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
+ const GcRoot<mirror::Class>& b) {
+ if (a.Read()->GetClassLoader() != b.Read()->GetClassLoader()) {
+ return false;
+ }
+ std::string temp;
+ return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp));
+}
+
+std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(
+ const std::pair<const char*, mirror::ClassLoader*>& element) const {
+ return ComputeModifiedUtf8Hash(element.first);
+}
+
+bool ClassLinker::ClassDescriptorHashEquals::operator()(
+ const GcRoot<mirror::Class>& a, const std::pair<const char*, mirror::ClassLoader*>& b) {
+ if (a.Read()->GetClassLoader() != b.second) {
+ return false;
+ }
+ return a.Read()->DescriptorEquals(b.first);
+}
+
+bool ClassLinker::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a,
+ const char* descriptor) {
+ return a.Read()->DescriptorEquals(descriptor);
+}
+
+std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descriptor) const {
+ return ComputeModifiedUtf8Hash(descriptor);
+}
+
} // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 107a4b2..7fc394a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -22,6 +22,7 @@
#include <vector>
#include "base/allocator.h"
+#include "base/hash_set.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "dex_file.h"
@@ -49,8 +50,9 @@
class InternTable;
template<class T> class ObjectLock;
+class Runtime;
class ScopedObjectAccessAlreadyRunnable;
-template<class T> class Handle;
+template<size_t kNumReferences> class PACKED(4) StackHandleScope;
typedef bool (ClassVisitor)(mirror::Class* c, void* arg);
@@ -74,9 +76,10 @@
Handle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Find a class in the path class loader, loading it if necessary.
+ // Find a class in the path class loader, loading it if necessary. Hash function is supposed to
+ // be ComputeModifiedUtf8Hash(descriptor).
mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self, const char* descriptor,
+ Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -93,14 +96,15 @@
bool IsInitialized() const;
// Define a new a class based on a ClassDef from a DexFile
- mirror::Class* DefineClass(const char* descriptor,
+ mirror::Class* DefineClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
// by the given 'class_loader'.
- mirror::Class* LookupClass(const char* descriptor, const mirror::ClassLoader* class_loader)
+ mirror::Class* LookupClass(const char* descriptor, size_t hash,
+ mirror::ClassLoader* class_loader)
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -113,7 +117,7 @@
// General class unloading is not supported, this is used to prune
// unwanted classes during image writing.
- bool RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader)
+ bool RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader)
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -402,6 +406,17 @@
return class_roots;
}
+ // Move all of the image classes into the class table for faster lookups.
+ void MoveImageClassesToClassTable()
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Move the class table to the pre-zygote table to reduce memory usage. This works by ensuring
+ // that no more classes are ever added to the pre zygote table which makes it that the pages
+ // always remain shared dirty instead of private dirty.
+ void MoveClassTableToPreZygote()
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -431,7 +446,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Class* CreateArrayClass(Thread* self, const char* descriptor,
+ mirror::Class* CreateArrayClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -513,14 +528,16 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool LinkMethods(Thread* self, Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ StackHandleScope<mirror::Class::kImtSize>* out_imt)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkInterfaceMethods(Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+ bool LinkInterfaceMethods(Thread* const self, Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ StackHandleScope<mirror::Class::kImtSize>* out_imt)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size)
@@ -586,9 +603,8 @@
std::string* error_msg)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- bool CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+ bool CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
bool* checksum_verified, std::string* error_msg);
- int32_t GetRequiredDelta(const OatFile* oat_file, InstructionSet isa);
// Note: will not register the oat file.
const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
@@ -643,7 +659,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* LookupClassFromTableLocked(const char* descriptor,
- const mirror::ClassLoader* class_loader,
+ mirror::ClassLoader* class_loader,
size_t hash)
SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
@@ -651,9 +667,6 @@
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* LookupClassFromImage(const char* descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -676,15 +689,43 @@
std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
+ class ClassDescriptorHashEquals {
+ public:
+ // Same class loader and descriptor.
+ std::size_t operator()(const GcRoot<mirror::Class>& root) const NO_THREAD_SAFETY_ANALYSIS;
+ bool operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b)
+ NO_THREAD_SAFETY_ANALYSIS;
+ // Same class loader and descriptor.
+ std::size_t operator()(const std::pair<const char*, mirror::ClassLoader*>& element) const
+ NO_THREAD_SAFETY_ANALYSIS;
+ bool operator()(const GcRoot<mirror::Class>& a,
+ const std::pair<const char*, mirror::ClassLoader*>& b)
+ NO_THREAD_SAFETY_ANALYSIS;
+ // Same descriptor.
+ bool operator()(const GcRoot<mirror::Class>& a, const char* descriptor)
+ NO_THREAD_SAFETY_ANALYSIS;
+ std::size_t operator()(const char* descriptor) const NO_THREAD_SAFETY_ANALYSIS;
+ };
+ class GcRootEmptyFn {
+ public:
+ void MakeEmpty(GcRoot<mirror::Class>& item) const {
+ item = GcRoot<mirror::Class>();
+ }
+ bool IsEmpty(const GcRoot<mirror::Class>& item) const {
+ return item.IsNull();
+ }
+ };
- // multimap from a string hash code of a class descriptor to
- // mirror::Class* instances. Results should be compared for a matching
- // Class::descriptor_ and Class::class_loader_.
- typedef AllocationTrackingMultiMap<size_t, GcRoot<mirror::Class>, kAllocatorTagClassTable> Table;
+ // hash set which hashes class descriptor, and compares descriptors nad class loaders. Results
+ // should be compared for a matching Class descriptor and class loader.
+ typedef HashSet<GcRoot<mirror::Class>, GcRootEmptyFn, ClassDescriptorHashEquals,
+ ClassDescriptorHashEquals, TrackingAllocator<GcRoot<mirror::Class>, kAllocatorTagClassTable>>
+ Table;
// This contains strong roots. To enable concurrent root scanning of
// the class table, be careful to use a read barrier when accessing this.
Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
- std::vector<std::pair<size_t, GcRoot<mirror::Class>>> new_class_roots_;
+ Table pre_zygote_class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
+ std::vector<GcRoot<mirror::Class>> new_class_roots_;
// Do we need to search dex caches to find image classes?
bool dex_cache_image_class_lookup_required_;
@@ -734,7 +775,8 @@
};
GcRoot<mirror::ObjectArray<mirror::Class>> class_roots_;
- mirror::Class* GetClassRoot(ClassRoot class_root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE mirror::Class* GetClassRoot(ClassRoot class_root)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetClassRoot(ClassRoot class_root, mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index dc845c7..1f52c46 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2364,7 +2364,7 @@
}
void Dbg::ResumeVM() {
- Runtime::Current()->GetThreadList()->UndoDebuggerSuspensions();
+ Runtime::Current()->GetThreadList()->ResumeAllForDebugger();
}
JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index eaab1f4..131de2c 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -246,7 +246,9 @@
*/
static int64_t LastDebuggerActivity();
- static void UndoDebuggerSuspensions();
+ static void UndoDebuggerSuspensions()
+ LOCKS_EXCLUDED(Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
/*
* Class, Object, Array
@@ -459,7 +461,9 @@
static void SuspendVM()
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
- static void ResumeVM();
+ static void ResumeVM()
+ LOCKS_EXCLUDED(Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
static JDWP::JdwpError SuspendThread(JDWP::ObjectId thread_id, bool request_suspension = true)
LOCKS_EXCLUDED(Locks::mutator_lock_,
Locks::thread_list_lock_,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index c783ee4..31d79bf 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -413,11 +413,12 @@
return atoi(version);
}
-const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const {
+const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor, size_t hash) const {
+ DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash);
// If we have an index lookup the descriptor via that as its constant time to search.
Index* index = class_def_index_.LoadSequentiallyConsistent();
if (index != nullptr) {
- auto it = index->find(descriptor);
+ auto it = index->FindWithHash(descriptor, hash);
return (it == index->end()) ? nullptr : it->second;
}
// Fast path for rate no class defs case.
@@ -449,11 +450,11 @@
MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
// Are we the first ones building the index?
if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
- index = new Index(num_class_defs);
+ index = new Index;
for (uint32_t i = 0; i < num_class_defs; ++i) {
const ClassDef& class_def = GetClassDef(i);
const char* descriptor = GetClassDescriptor(class_def);
- index->insert(std::make_pair(descriptor, &class_def));
+ index->Insert(std::make_pair(descriptor, &class_def));
}
class_def_index_.StoreSequentiallyConsistent(index);
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 1306f11..54c4b09 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -22,6 +22,7 @@
#include <unordered_map>
#include <vector>
+#include "base/hash_map.h"
#include "base/logging.h"
#include "base/mutex.h" // For Locks::mutator_lock_.
#include "globals.h"
@@ -648,8 +649,9 @@
return StringByTypeIdx(class_def.class_idx_);
}
- // Looks up a class definition by its class descriptor.
- const ClassDef* FindClassDef(const char* descriptor) const;
+ // Looks up a class definition by its class descriptor. Hash must be
+ // ComputeModifiedUtf8Hash(descriptor).
+ const ClassDef* FindClassDef(const char* descriptor, size_t hash) const;
// Looks up a class definition by its type index.
const ClassDef* FindClassDef(uint16_t type_idx) const;
@@ -985,17 +987,30 @@
// Number of misses finding a class def from a descriptor.
mutable Atomic<uint32_t> find_class_def_misses_;
+ struct UTF16EmptyFn {
+ void MakeEmpty(std::pair<const char*, const ClassDef*>& pair) const {
+ pair.first = nullptr;
+ pair.second = nullptr;
+ }
+ bool IsEmpty(const std::pair<const char*, const ClassDef*>& pair) const {
+ if (pair.first == nullptr) {
+ DCHECK(pair.second == nullptr);
+ return true;
+ }
+ return false;
+ }
+ };
struct UTF16HashCmp {
// Hash function.
size_t operator()(const char* key) const {
- return ComputeUtf8Hash(key);
+ return ComputeModifiedUtf8Hash(key);
}
// std::equal function.
bool operator()(const char* a, const char* b) const {
return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0;
}
};
- typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
+ typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, UTF16HashCmp, UTF16HashCmp> Index;
mutable Atomic<Index*> class_def_index_;
mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
};
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0ab6394..6ec0712 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -106,7 +106,7 @@
delete entry;
}
-ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
+ElfFile::ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
: file_(file),
writable_(writable),
program_header_only_(program_header_only),
@@ -124,13 +124,15 @@
symtab_symbol_table_(nullptr),
dynsym_symbol_table_(nullptr),
jit_elf_image_(nullptr),
- jit_gdb_entry_(nullptr) {
+ jit_gdb_entry_(nullptr),
+ requested_base_(requested_base) {
CHECK(file != nullptr);
}
ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
- std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only));
+ std::string* error_msg, uint8_t* requested_base) {
+ std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only,
+ requested_base));
int prot;
int flags;
if (writable) {
@@ -147,7 +149,8 @@
}
ElfFile* ElfFile::Open(File* file, int prot, int flags, std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false));
+ std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false,
+ /*requested_base*/nullptr));
if (!elf_file->Setup(prot, flags, error_msg)) {
return nullptr;
}
@@ -757,6 +760,8 @@
}
const Elf32_Sym* sym = FindDynamicSymbol(symbol_name);
if (sym != nullptr) {
+ // TODO: we need to change this to calculate base_address_ in ::Open,
+ // otherwise it will be wrongly 0 if ::Load has not yet been called.
return base_address_ + sym->st_value;
} else {
return nullptr;
@@ -1094,12 +1099,16 @@
}
size_t file_length = static_cast<size_t>(temp_file_length);
if (!reserved) {
- byte* reserve_base = ((program_header->p_vaddr != 0) ?
- reinterpret_cast<byte*>(program_header->p_vaddr) : nullptr);
+ uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
+ uint8_t* reserve_base_override = reserve_base;
+ // Override the base (e.g. when compiling with --compile-pic)
+ if (requested_base_ != nullptr) {
+ reserve_base_override = requested_base_;
+ }
std::string reservation_name("ElfFile reservation for ");
reservation_name += file_->GetPath();
std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
- reserve_base,
+ reserve_base_override,
GetLoadedSize(), PROT_NONE, false,
error_msg));
if (reserve.get() == nullptr) {
@@ -1108,9 +1117,15 @@
return false;
}
reserved = true;
- if (reserve_base == nullptr) {
- base_address_ = reserve->Begin();
- }
+
+ // Base address is the difference of actual mapped location and the p_vaddr
+ base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
+ - reinterpret_cast<uintptr_t>(reserve_base));
+ // By adding the p_vaddr of a section/symbol to base_address_ we will always get the
+ // dynamic memory address of where that object is actually mapped
+ //
+ // TODO: base_address_ needs to be calculated in ::Open, otherwise
+ // FindDynamicSymbolAddress returns the wrong values until Load is called.
segments_.push_back(reserve.release());
}
// empty segment, nothing to map
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 985be76..9c0bc9a 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -40,7 +40,8 @@
// ELFObjectFile.
class ElfFile {
public:
- static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
+ static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+ uint8_t* requested_base = nullptr); // TODO: move arg to before error_msg.
// Open with specific mmap flags, Always maps in the whole file, not just the
// program header sections.
static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
@@ -52,10 +53,12 @@
return *file_;
}
+ // The start of the memory map address range for this ELF file.
byte* Begin() const {
return map_->Begin();
}
+ // The end of the memory map address range for this ELF file.
byte* End() const {
return map_->End();
}
@@ -109,7 +112,7 @@
bool Load(bool executable, std::string* error_msg);
private:
- ElfFile(File* file, bool writable, bool program_header_only);
+ ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
bool Setup(int prot, int flags, std::string* error_msg);
@@ -200,6 +203,9 @@
JITCodeEntry* jit_gdb_entry_;
std::unique_ptr<ElfFile> gdb_file_mapping_;
void GdbJITSupport();
+
+ // When not-null, override the base vaddr we memory map LOAD segments into.
+ uint8_t* requested_base_;
};
} // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index ccbedc0..c0b7b06 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -429,7 +429,14 @@
case kInterface: {
uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
mirror::ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(imt_index);
- if (!imt_method->IsImtConflictMethod()) {
+ if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {
+ if (kIsDebugBuild) {
+ mirror::Class* klass = (*this_object)->GetClass();
+ mirror::ArtMethod* method = klass->FindVirtualMethodForInterface(resolved_method);
+ CHECK_EQ(imt_method, method) << PrettyMethod(resolved_method) << " / " <<
+ PrettyMethod(imt_method) << " / " << PrettyMethod(method) << " / " <<
+ PrettyClass(klass);
+ }
return imt_method;
} else {
mirror::ArtMethod* interface_method =
@@ -529,8 +536,7 @@
mirror::Object* this_object,
mirror::ArtMethod* referrer,
bool access_check, InvokeType type) {
- bool is_direct = type == kStatic || type == kDirect;
- if (UNLIKELY(this_object == NULL && !is_direct)) {
+ if (UNLIKELY(this_object == NULL && type != kStatic)) {
return NULL;
}
mirror::ArtMethod* resolved_method =
@@ -555,7 +561,7 @@
}
if (type == kInterface) { // Most common form of slow path dispatch.
return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- } else if (is_direct) {
+ } else if (type == kStatic || type == kDirect) {
return resolved_method;
} else if (type == kSuper) {
return referrer->GetDeclaringClass()->GetSuperClass()
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index fbeea85..6d44d89 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -54,9 +54,8 @@
static CardTable* Create(const byte* heap_begin, size_t heap_capacity);
// Set the card associated with the given address to GC_CARD_DIRTY.
- void MarkCard(const void *addr) {
- byte* card_addr = CardFromAddr(addr);
- *card_addr = kCardDirty;
+ ALWAYS_INLINE void MarkCard(const void *addr) {
+ *CardFromAddr(addr) = kCardDirty;
}
// Is the object on a dirty card?
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6af98cf..0b0200f 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -53,6 +53,7 @@
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "heap-inl.h"
#include "image.h"
+#include "intern_table.h"
#include "mirror/art_field-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object.h"
@@ -1908,6 +1909,8 @@
if (have_zygote_space_) {
return;
}
+ Runtime::Current()->GetInternTable()->SwapPostZygoteWithPreZygote();
+ Runtime::Current()->GetClassLinker()->MoveClassTableToPreZygote();
VLOG(heap) << "Starting PreZygoteFork";
// Trim the pages at the end of the non moving space.
non_moving_space_->Trim();
@@ -2153,6 +2156,13 @@
} else {
LOG(FATAL) << "Invalid current allocator " << current_allocator_;
}
+ if (IsGcConcurrent()) {
+ // Disable concurrent GC check so that we don't have spammy JNI requests.
+ // This gets recalculated in GrowForUtilization. It is important that it is disabled /
+ // calculated in the same thread so that there aren't any races that can cause it to become
+ // permanantly disabled. b/17942071
+ concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
+ }
CHECK(collector != nullptr)
<< "Could not find garbage collector with collector_type="
<< static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type;
@@ -2960,9 +2970,6 @@
self->IsHandlingStackOverflow()) {
return;
}
- // We already have a request pending, no reason to start more until we update
- // concurrent_start_bytes_.
- concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
JNIEnv* env = self->GetJniEnv();
DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cf297bd..24f4f17 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -372,18 +372,18 @@
// Must be called if a field of an Object in the heap changes, and before any GC safe-point.
// The call is not needed if NULL is stored in the field.
- void WriteBarrierField(const mirror::Object* dst, MemberOffset /*offset*/,
- const mirror::Object* /*new_value*/) {
+ ALWAYS_INLINE void WriteBarrierField(const mirror::Object* dst, MemberOffset /*offset*/,
+ const mirror::Object* /*new_value*/) {
card_table_->MarkCard(dst);
}
// Write barrier for array operations that update many field positions
- void WriteBarrierArray(const mirror::Object* dst, int /*start_offset*/,
- size_t /*length TODO: element_count or byte_count?*/) {
+ ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst, int /*start_offset*/,
+ size_t /*length TODO: element_count or byte_count?*/) {
card_table_->MarkCard(dst);
}
- void WriteBarrierEveryFieldOf(const mirror::Object* obj) {
+ ALWAYS_INLINE void WriteBarrierEveryFieldOf(const mirror::Object* obj) {
card_table_->MarkCard(obj);
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 59630fe..dcb6392 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -17,6 +17,7 @@
#include "image_space.h"
#include <dirent.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
#include <random>
@@ -143,9 +144,7 @@
}
std::string oat_file_option_string("--oat-file=");
- oat_file_option_string += image_filename;
- oat_file_option_string.erase(oat_file_option_string.size() - 3);
- oat_file_option_string += "oat";
+ oat_file_option_string += ImageHeader::GetOatLocationFromImageLocation(image_filename);
arg_vector.push_back(oat_file_option_string);
Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&arg_vector);
@@ -377,6 +376,41 @@
return false;
}
+static constexpr uint64_t kLowSpaceValue = 50 * MB;
+static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
+
+// Read the free space of the cache partition and make a decision whether to keep the generated
+// image. This is to try to mitigate situations where the system might run out of space later.
+static bool CheckSpace(const std::string& cache_filename, std::string* error_msg) {
+ // Using statvfs vs statvfs64 because of b/18207376, and it is enough for all practical purposes.
+ struct statvfs buf;
+
+ int res = TEMP_FAILURE_RETRY(statvfs(cache_filename.c_str(), &buf));
+ if (res != 0) {
+ // Could not stat. Conservatively tell the system to delete the image.
+ *error_msg = "Could not stat the filesystem, assuming low-memory situation.";
+ return false;
+ }
+
+ uint64_t fs_overall_size = buf.f_bsize * static_cast<uint64_t>(buf.f_blocks);
+ // Zygote is privileged, but other things are not. Use bavail.
+ uint64_t fs_free_size = buf.f_bsize * static_cast<uint64_t>(buf.f_bavail);
+
+ // Take the overall size as an indicator for a tmpfs, which is being used for the decryption
+ // environment. We do not want to fail quickening the boot image there, as it is beneficial
+ // for time-to-UI.
+ if (fs_overall_size > kTmpFsSentinelValue) {
+ if (fs_free_size < kLowSpaceValue) {
+ *error_msg = StringPrintf("Low-memory situation: only %4.2f megabytes available after image"
+ " generation, need at least %" PRIu64 ".",
+ static_cast<double>(fs_free_size) / MB,
+ kLowSpaceValue / MB);
+ return false;
+ }
+ }
+ return true;
+}
+
ImageSpace* ImageSpace::Create(const char* image_location,
const InstructionSet image_isa,
std::string* error_msg) {
@@ -434,6 +468,11 @@
*error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s",
image_location, system_filename.c_str(),
cache_filename.c_str(), reason.c_str());
+ // We failed to create files, remove any possibly garbage output.
+ // Since ImageCreationAllowed was true above, we are the zygote
+ // and therefore the only process expected to generate these for
+ // the device.
+ PruneDexCache(image_isa);
return nullptr;
}
}
@@ -480,19 +519,23 @@
return space;
}
- // If the /system file exists, it should be up-to-date, don't try to generate it. Same if it is
- // a relocated copy from something in /system (i.e. checksum's match).
- // Otherwise, log a warning and fall through to GenerateImage.
if (relocated_version_used) {
- LOG(FATAL) << "Attempted to use relocated version of " << image_location << " "
- << "at " << cache_filename << " generated from " << system_filename << " "
- << "but image failed to load: " << error_msg;
+ // Something is wrong with the relocated copy (even though checksums match). Cleanup.
+ // This can happen if the .oat is corrupt, since the above only checks the .art checksums.
+ // TODO: Check the oat file validity earlier.
+ *error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s "
+ "but image failed to load: %s",
+ image_location, cache_filename.c_str(), system_filename.c_str(),
+ error_msg->c_str());
+ PruneDexCache(image_isa);
return nullptr;
} else if (is_system) {
+ // If the /system file exists, it should be up-to-date, don't try to generate it.
*error_msg = StringPrintf("Failed to load /system image '%s': %s",
image_filename->c_str(), error_msg->c_str());
return nullptr;
} else {
+ // Otherwise, log a warning and fall through to GenerateImage.
LOG(WARNING) << *error_msg;
}
}
@@ -508,8 +551,20 @@
} else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
*error_msg = StringPrintf("Failed to generate image '%s': %s",
cache_filename.c_str(), error_msg->c_str());
+ // We failed to create files, remove any possibly garbage output.
+ // Since ImageCreationAllowed was true above, we are the zygote
+ // and therefore the only process expected to generate these for
+ // the device.
+ PruneDexCache(image_isa);
return nullptr;
} else {
+ // Check whether there is enough space left over after we have generated the image.
+ if (!CheckSpace(cache_filename, error_msg)) {
+ // No. Delete the generated image and try to run out of the dex files.
+ PruneDexCache(image_isa);
+ return nullptr;
+ }
+
// Note that we must not use the file descriptor associated with
// ScopedFlock::GetFile to Init the image file. We want the file
// descriptor (and the associated exclusive lock) to be released when
@@ -629,6 +684,9 @@
runtime->SetResolutionMethod(down_cast<mirror::ArtMethod*>(resolution_method));
mirror::Object* imt_conflict_method = image_header.GetImageRoot(ImageHeader::kImtConflictMethod);
runtime->SetImtConflictMethod(down_cast<mirror::ArtMethod*>(imt_conflict_method));
+ mirror::Object* imt_unimplemented_method =
+ image_header.GetImageRoot(ImageHeader::kImtUnimplementedMethod);
+ runtime->SetImtUnimplementedMethod(down_cast<mirror::ArtMethod*>(imt_unimplemented_method));
mirror::Object* default_imt = image_header.GetImageRoot(ImageHeader::kDefaultImt);
runtime->SetDefaultImt(down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(default_imt));
@@ -653,7 +711,10 @@
const ImageHeader& image_header = GetImageHeader();
std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
+ CHECK(image_header.GetOatDataBegin() != nullptr);
+
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
+ image_header.GetOatFileBegin(),
!Runtime::Current()->IsCompiler(), error_msg);
if (oat_file == NULL) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
@@ -669,7 +730,7 @@
}
int32_t image_patch_delta = image_header.GetPatchDelta();
int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
- if (oat_patch_delta != image_patch_delta) {
+ if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
// We should have already relocated by this point. Bail out.
*error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
"in image %s", oat_patch_delta, image_patch_delta, GetName());
diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h
index 2661e54..a42ec08 100644
--- a/runtime/gc_root-inl.h
+++ b/runtime/gc_root-inl.h
@@ -25,14 +25,9 @@
template<class MirrorType>
template<ReadBarrierOption kReadBarrierOption>
-inline MirrorType* GcRoot<MirrorType>::Read() {
+inline MirrorType* GcRoot<MirrorType>::Read() const {
return ReadBarrier::BarrierForRoot<MirrorType, kReadBarrierOption>(&root_);
}
-template<class MirrorType>
-inline void GcRoot<MirrorType>::Assign(MirrorType* value) {
- root_ = value;
-}
-
} // namespace art
#endif // ART_RUNTIME_GC_ROOT_INL_H_
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 3928f5d..aee5586 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -27,11 +27,12 @@
class PACKED(4) GcRoot {
public:
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- ALWAYS_INLINE MirrorType* Read() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ALWAYS_INLINE void Assign(MirrorType* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) {
+ void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) const {
+ DCHECK(!IsNull());
callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
+ DCHECK(!IsNull());
}
// This is only used by IrtIterator.
@@ -52,7 +53,7 @@
}
private:
- MirrorType* root_;
+ mutable MirrorType* root_;
};
} // namespace art
diff --git a/runtime/handle.h b/runtime/handle.h
index f70faf4..f9864dc 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -58,7 +58,7 @@
}
ALWAYS_INLINE T* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return reference_->AsMirrorPtr();
+ return down_cast<T*>(reference_->AsMirrorPtr());
}
ALWAYS_INLINE jobject ToJObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -70,25 +70,25 @@
}
protected:
- StackReference<T>* reference_;
-
template<typename S>
explicit ConstHandle(StackReference<S>* reference)
- : reference_(reinterpret_cast<StackReference<T>*>(reference)) {
+ : reference_(reference) {
}
template<typename S>
explicit ConstHandle(const ConstHandle<S>& handle)
- : reference_(reinterpret_cast<StackReference<T>*>(handle.reference_)) {
+ : reference_(handle.reference_) {
}
- StackReference<T>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ StackReference<mirror::Object>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
return reference_;
}
- ALWAYS_INLINE const StackReference<T>* GetReference() const
+ ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return reference_;
}
+ StackReference<mirror::Object>* reference_;
+
private:
friend class BuildGenericJniFrameVisitor;
template<class S> friend class ConstHandle;
@@ -120,8 +120,8 @@
}
ALWAYS_INLINE T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- StackReference<T>* ref = ConstHandle<T>::GetReference();
- T* const old = ref->AsMirrorPtr();
+ StackReference<mirror::Object>* ref = Handle<T>::GetReference();
+ T* old = down_cast<T*>(ref->AsMirrorPtr());
ref->Assign(reference);
return old;
}
@@ -131,7 +131,6 @@
: ConstHandle<T>(handle) {
}
- protected:
template<typename S>
explicit Handle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: ConstHandle<T>(reference) {
@@ -152,7 +151,7 @@
}
private:
- StackReference<T> null_ref_;
+ StackReference<mirror::Object> null_ref_;
};
} // namespace art
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index 7bc811d..da28ed7 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -25,12 +25,12 @@
namespace art {
template<size_t kNumReferences>
-inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self)
+inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
: HandleScope(kNumReferences), self_(self), pos_(0) {
// TODO: Figure out how to use a compile assert.
DCHECK_EQ(&references_[0], &references_storage_[0]);
for (size_t i = 0; i < kNumReferences; ++i) {
- SetReference(i, nullptr);
+ SetReference(i, fill_value);
}
self_->PushHandleScope(this);
}
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 42ef779..5050872 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -120,6 +120,12 @@
}
protected:
+ // Return backing storage used for references.
+ ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const {
+ uintptr_t address = reinterpret_cast<uintptr_t>(this) + ReferencesOffset(sizeof(void*));
+ return reinterpret_cast<StackReference<mirror::Object>*>(address);
+ }
+
explicit HandleScope(size_t number_of_references) :
link_(nullptr), number_of_references_(number_of_references) {
}
@@ -150,56 +156,48 @@
}
private:
- T** obj_;
+ T** const obj_;
};
// Scoped handle storage of a fixed size that is usually stack allocated.
template<size_t kNumReferences>
class PACKED(4) StackHandleScope FINAL : public HandleScope {
public:
- explicit StackHandleScope(Thread* self);
- ~StackHandleScope();
-
- // Currently unused, using this GetReference instead of the one in HandleScope is preferred to
- // avoid compiler optimizations incorrectly optimizing out of bound array accesses.
- // TODO: Remove this when it is un-necessary.
- mirror::Object* GetReference(size_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- ALWAYS_INLINE {
- DCHECK_LT(i, number_of_references_);
- return references_storage_[i].AsMirrorPtr();
- }
-
- Handle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- ALWAYS_INLINE {
- DCHECK_LT(i, number_of_references_);
- return Handle<mirror::Object>(&references_storage_[i]);
- }
-
- void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- ALWAYS_INLINE {
- DCHECK_LT(i, number_of_references_);
- references_storage_[i].Assign(object);
- }
+ explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr);
+ ALWAYS_INLINE ~StackHandleScope();
template<class T>
- Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
SetReference(pos_, object);
- Handle<T> h(GetHandle(pos_));
+ Handle<T> h(GetHandle<T>(pos_));
pos_++;
return h;
}
template<class T>
- HandleWrapper<T> NewHandleWrapper(T** object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE HandleWrapper<T> NewHandleWrapper(T** object)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
SetReference(pos_, *object);
- Handle<T> h(GetHandle(pos_));
+ Handle<T> h(GetHandle<T>(pos_));
pos_++;
return HandleWrapper<T>(object, h);
}
+ ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK_LT(i, kNumReferences);
+ GetReferences()[i].Assign(object);
+ }
+
private:
- // References_storage_ needs to be first so that it appears in the same location as
- // HandleScope::references_.
+ template<class T>
+ ALWAYS_INLINE Handle<T> GetHandle(size_t i)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK_LT(i, kNumReferences);
+ return Handle<T>(&GetReferences()[i]);
+ }
+
+ // Reference storage needs to be first as expected by the HandleScope layout.
StackReference<mirror::Object> references_storage_[kNumReferences];
// The thread that the stack handle scope is a linked list upon. The stack handle scope will
diff --git a/runtime/image.cc b/runtime/image.cc
index 478b486..2a69128 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '0', '9', '\0' };
+const byte ImageHeader::kImageVersion[] = { '0', '1', '0', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -35,7 +35,8 @@
uint32_t oat_file_begin,
uint32_t oat_data_begin,
uint32_t oat_data_end,
- uint32_t oat_file_end)
+ uint32_t oat_file_end,
+ bool compile_pic)
: image_begin_(image_begin),
image_size_(image_size),
image_bitmap_offset_(image_bitmap_offset),
@@ -46,7 +47,8 @@
oat_data_end_(oat_data_end),
oat_file_end_(oat_file_end),
patch_delta_(0),
- image_roots_(image_roots) {
+ image_roots_(image_roots),
+ compile_pic_(compile_pic) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index 424a40b..a77aec4 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -28,7 +28,7 @@
// header of image files written by ImageWriter, read and validated by Space.
class PACKED(4) ImageHeader {
public:
- ImageHeader() {}
+ ImageHeader() : compile_pic_(0) {}
ImageHeader(uint32_t image_begin,
uint32_t image_size_,
@@ -39,7 +39,8 @@
uint32_t oat_file_begin,
uint32_t oat_data_begin,
uint32_t oat_data_end,
- uint32_t oat_file_end);
+ uint32_t oat_file_end,
+ bool compile_pic_);
bool IsValid() const;
const char* GetMagic() const;
@@ -105,6 +106,7 @@
enum ImageRoot {
kResolutionMethod,
kImtConflictMethod,
+ kImtUnimplementedMethod,
kDefaultImt,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
@@ -120,6 +122,10 @@
void RelocateImage(off_t delta);
+ bool CompilePic() const {
+ return compile_pic_ != 0;
+ }
+
private:
static const byte kImageMagic[4];
static const byte kImageVersion[4];
@@ -161,6 +167,9 @@
// Absolute address of an Object[] of objects needed to reinitialize from an image.
uint32_t image_roots_;
+ // Boolean (0 or 1) to denote if the image was compiled with --compile-pic option
+ const uint32_t compile_pic_;
+
friend class ImageWriter;
};
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index c66f99e..95c622a 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -29,56 +29,48 @@
namespace art {
InternTable::InternTable()
- : log_new_roots_(false), allow_new_interns_(true),
+ : image_added_to_intern_table_(false), log_new_roots_(false),
+ allow_new_interns_(true),
new_intern_condition_("New intern condition", *Locks::intern_table_lock_) {
}
size_t InternTable::Size() const {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
- return strong_interns_.size() + weak_interns_.size();
+ return strong_interns_.Size() + weak_interns_.Size();
}
size_t InternTable::StrongSize() const {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
- return strong_interns_.size();
+ return strong_interns_.Size();
}
size_t InternTable::WeakSize() const {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
- return weak_interns_.size();
+ return weak_interns_.Size();
}
void InternTable::DumpForSigQuit(std::ostream& os) const {
- MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
- os << "Intern table: " << strong_interns_.size() << " strong; "
- << weak_interns_.size() << " weak\n";
+ os << "Intern table: " << StrongSize() << " strong; " << WeakSize() << " weak\n";
}
void InternTable::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
- for (auto& strong_intern : strong_interns_) {
- const_cast<GcRoot<mirror::String>&>(strong_intern).
- VisitRoot(callback, arg, 0, kRootInternedString);
- DCHECK(!strong_intern.IsNull());
- }
+ strong_interns_.VisitRoots(callback, arg);
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_strong_intern_roots_) {
mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(callback, arg, 0, kRootInternedString);
mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
- if (UNLIKELY(new_ref != old_ref)) {
+ if (new_ref != old_ref) {
// The GC moved a root in the log. Need to search the strong interns and update the
// corresponding object. This is slow, but luckily for us, this may only happen with a
// concurrent moving GC.
- auto it = strong_interns_.find(GcRoot<mirror::String>(old_ref));
- DCHECK(it != strong_interns_.end());
- strong_interns_.erase(it);
- strong_interns_.insert(GcRoot<mirror::String>(new_ref));
+ strong_interns_.Remove(old_ref);
+ strong_interns_.Insert(new_ref);
}
}
}
-
if ((flags & kVisitRootFlagClearRootLog) != 0) {
new_strong_intern_roots_.clear();
}
@@ -91,21 +83,17 @@
}
mirror::String* InternTable::LookupStrong(mirror::String* s) {
- return Lookup(&strong_interns_, s);
+ return strong_interns_.Find(s);
}
mirror::String* InternTable::LookupWeak(mirror::String* s) {
- // Weak interns need a read barrier because they are weak roots.
- return Lookup(&weak_interns_, s);
+ return weak_interns_.Find(s);
}
-mirror::String* InternTable::Lookup(Table* table, mirror::String* s) {
- Locks::intern_table_lock_->AssertHeld(Thread::Current());
- auto it = table->find(GcRoot<mirror::String>(s));
- if (LIKELY(it != table->end())) {
- return const_cast<GcRoot<mirror::String>&>(*it).Read<kWithReadBarrier>();
- }
- return nullptr;
+void InternTable::SwapPostZygoteWithPreZygote() {
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+ weak_interns_.SwapPostZygoteWithPreZygote();
+ strong_interns_.SwapPostZygoteWithPreZygote();
}
mirror::String* InternTable::InsertStrong(mirror::String* s) {
@@ -116,7 +104,7 @@
if (log_new_roots_) {
new_strong_intern_roots_.push_back(GcRoot<mirror::String>(s));
}
- strong_interns_.insert(GcRoot<mirror::String>(s));
+ strong_interns_.Insert(s);
return s;
}
@@ -125,12 +113,12 @@
if (runtime->IsActiveTransaction()) {
runtime->RecordWeakStringInsertion(s);
}
- weak_interns_.insert(GcRoot<mirror::String>(s));
+ weak_interns_.Insert(s);
return s;
}
void InternTable::RemoveStrong(mirror::String* s) {
- Remove(&strong_interns_, s);
+ strong_interns_.Remove(s);
}
void InternTable::RemoveWeak(mirror::String* s) {
@@ -138,13 +126,7 @@
if (runtime->IsActiveTransaction()) {
runtime->RecordWeakStringRemoval(s);
}
- Remove(&weak_interns_, s);
-}
-
-void InternTable::Remove(Table* table, mirror::String* s) {
- auto it = table->find(GcRoot<mirror::String>(s));
- DCHECK(it != table->end());
- table->erase(it);
+ weak_interns_.Remove(s);
}
// Insert/remove methods used to undo changes made during an aborted transaction.
@@ -165,11 +147,39 @@
RemoveWeak(s);
}
-static mirror::String* LookupStringFromImage(mirror::String* s)
+void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space) {
+ MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+ if (!image_added_to_intern_table_) {
+ mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
+ for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ const size_t num_strings = dex_file->NumStringIds();
+ for (size_t j = 0; j < num_strings; ++j) {
+ mirror::String* image_string = dex_cache->GetResolvedString(j);
+ if (image_string != nullptr) {
+ mirror::String* found = LookupStrong(image_string);
+ if (found == nullptr) {
+ InsertStrong(image_string);
+ } else {
+ DCHECK_EQ(found, image_string);
+ }
+ }
+ }
+ }
+ image_added_to_intern_table_ = true;
+ }
+}
+
+mirror::String* InternTable::LookupStringFromImage(mirror::String* s)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (image_added_to_intern_table_) {
+ return nullptr;
+ }
gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
- if (image == NULL) {
- return NULL; // No image present.
+ if (image == nullptr) {
+ return nullptr; // No image present.
}
mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
@@ -179,15 +189,15 @@
const DexFile* dex_file = dex_cache->GetDexFile();
// Binary search the dex file for the string index.
const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
- if (string_id != NULL) {
+ if (string_id != nullptr) {
uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
mirror::String* image = dex_cache->GetResolvedString(string_idx);
- if (image != NULL) {
+ if (image != nullptr) {
return image;
}
}
}
- return NULL;
+ return nullptr;
}
void InternTable::AllowNewInterns() {
@@ -204,58 +214,36 @@
}
mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
+ if (s == nullptr) {
+ return nullptr;
+ }
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::intern_table_lock_);
-
- DCHECK(s != NULL);
-
while (UNLIKELY(!allow_new_interns_)) {
new_intern_condition_.WaitHoldingLocks(self);
}
-
- if (is_strong) {
- // Check the strong table for a match.
- mirror::String* strong = LookupStrong(s);
- if (strong != NULL) {
- return strong;
- }
-
- // Check the image for a match.
- mirror::String* image = LookupStringFromImage(s);
- if (image != NULL) {
- return InsertStrong(image);
- }
-
- // There is no match in the strong table, check the weak table.
- mirror::String* weak = LookupWeak(s);
- if (weak != NULL) {
- // A match was found in the weak table. Promote to the strong table.
- RemoveWeak(weak);
- return InsertStrong(weak);
- }
-
- // No match in the strong table or the weak table. Insert into the strong
- // table.
- return InsertStrong(s);
- }
-
// Check the strong table for a match.
mirror::String* strong = LookupStrong(s);
- if (strong != NULL) {
+ if (strong != nullptr) {
return strong;
}
// Check the image for a match.
mirror::String* image = LookupStringFromImage(s);
- if (image != NULL) {
- return InsertWeak(image);
+ if (image != nullptr) {
+ return is_strong ? InsertStrong(image) : InsertWeak(image);
}
- // Check the weak table for a match.
+ // There is no match in the strong table, check the weak table.
mirror::String* weak = LookupWeak(s);
- if (weak != NULL) {
+ if (weak != nullptr) {
+ if (is_strong) {
+ // A match was found in the weak table. Promote to the strong table.
+ RemoveWeak(weak);
+ return InsertStrong(weak);
+ }
return weak;
}
- // Insert into the weak table.
- return InsertWeak(s);
+ // No match in the strong table or the weak table. Insert into the strong / weak table.
+ return is_strong ? InsertStrong(s) : InsertWeak(s);
}
mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
@@ -270,56 +258,104 @@
}
mirror::String* InternTable::InternStrong(mirror::String* s) {
- if (s == nullptr) {
- return nullptr;
- }
return Insert(s, true);
}
mirror::String* InternTable::InternWeak(mirror::String* s) {
- if (s == nullptr) {
- return nullptr;
- }
return Insert(s, false);
}
bool InternTable::ContainsWeak(mirror::String* s) {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
- const mirror::String* found = LookupWeak(s);
- return found == s;
+ return LookupWeak(s) == s;
}
void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
- for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
+ weak_interns_.SweepWeaks(callback, arg);
+}
+
+std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) const {
+ if (kIsDebugBuild) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ }
+ return static_cast<size_t>(root.Read()->GetHashCode());
+}
+
+bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
+ const GcRoot<mirror::String>& b) const {
+ if (kIsDebugBuild) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ }
+ return a.Read()->Equals(b.Read());
+}
+
+void InternTable::Table::Remove(mirror::String* s) {
+ auto it = post_zygote_table_.Find(GcRoot<mirror::String>(s));
+ if (it != post_zygote_table_.end()) {
+ post_zygote_table_.Erase(it);
+ } else {
+ it = pre_zygote_table_.Find(GcRoot<mirror::String>(s));
+ DCHECK(it != pre_zygote_table_.end());
+ pre_zygote_table_.Erase(it);
+ }
+}
+
+mirror::String* InternTable::Table::Find(mirror::String* s) {
+ Locks::intern_table_lock_->AssertHeld(Thread::Current());
+ auto it = pre_zygote_table_.Find(GcRoot<mirror::String>(s));
+ if (it != pre_zygote_table_.end()) {
+ return it->Read();
+ }
+ it = post_zygote_table_.Find(GcRoot<mirror::String>(s));
+ if (it != post_zygote_table_.end()) {
+ return it->Read();
+ }
+ return nullptr;
+}
+
+void InternTable::Table::SwapPostZygoteWithPreZygote() {
+ CHECK(pre_zygote_table_.Empty());
+ std::swap(pre_zygote_table_, post_zygote_table_);
+ VLOG(heap) << "Swapping " << pre_zygote_table_.Size() << " interns to the pre zygote table";
+}
+
+void InternTable::Table::Insert(mirror::String* s) {
+ // Always insert the post zygote table, this gets swapped when we create the zygote to be the
+ // pre zygote table.
+ post_zygote_table_.Insert(GcRoot<mirror::String>(s));
+}
+
+void InternTable::Table::VisitRoots(RootCallback* callback, void* arg) {
+ for (auto& intern : pre_zygote_table_) {
+ intern.VisitRoot(callback, arg, 0, kRootInternedString);
+ }
+ for (auto& intern : post_zygote_table_) {
+ intern.VisitRoot(callback, arg, 0, kRootInternedString);
+ }
+}
+
+void InternTable::Table::SweepWeaks(IsMarkedCallback* callback, void* arg) {
+ SweepWeaks(&pre_zygote_table_, callback, arg);
+ SweepWeaks(&post_zygote_table_, callback, arg);
+}
+
+void InternTable::Table::SweepWeaks(UnorderedSet* set, IsMarkedCallback* callback, void* arg) {
+ for (auto it = set->begin(), end = set->end(); it != end;) {
// This does not need a read barrier because this is called by GC.
- GcRoot<mirror::String>& root = const_cast<GcRoot<mirror::String>&>(*it);
- mirror::Object* object = root.Read<kWithoutReadBarrier>();
+ mirror::Object* object = it->Read<kWithoutReadBarrier>();
mirror::Object* new_object = callback(object, arg);
if (new_object == nullptr) {
- it = weak_interns_.erase(it);
+ it = set->Erase(it);
} else {
- root.Assign(down_cast<mirror::String*>(new_object));
+ *it = GcRoot<mirror::String>(new_object->AsString());
++it;
}
}
}
-std::size_t InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& root) {
- if (kIsDebugBuild) {
- Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
- }
- return static_cast<size_t>(
- const_cast<GcRoot<mirror::String>&>(root).Read<kWithoutReadBarrier>()->GetHashCode());
-}
-
-bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
- const GcRoot<mirror::String>& b) {
- if (kIsDebugBuild) {
- Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
- }
- return const_cast<GcRoot<mirror::String>&>(a).Read<kWithoutReadBarrier>()->Equals(
- const_cast<GcRoot<mirror::String>&>(b).Read<kWithoutReadBarrier>());
+size_t InternTable::Table::Size() const {
+ return pre_zygote_table_.Size() + post_zygote_table_.Size();
}
} // namespace art
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index e3223c8..371d3f7 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -20,12 +20,19 @@
#include <unordered_set>
#include "base/allocator.h"
+#include "base/hash_set.h"
#include "base/mutex.h"
#include "gc_root.h"
#include "object_callbacks.h"
namespace art {
+namespace gc {
+namespace space {
+class ImageSpace;
+} // namespace space
+} // namespace gc
+
enum VisitRootFlags : uint8_t;
namespace mirror {
@@ -66,9 +73,12 @@
bool ContainsWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- size_t Size() const;
- size_t StrongSize() const;
- size_t WeakSize() const;
+ // Total number of interned strings.
+ size_t Size() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
+ // Total number of weakly live interned strings.
+ size_t StrongSize() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
+ // Total number of strongly live interned strings.
+ size_t WeakSize() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
void VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -78,29 +88,83 @@
void DisallowNewInterns() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
void AllowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Adds all of the resolved image strings from the image space into the intern table. The
+ // advantage of doing this is preventing expensive DexFile::FindStringId calls.
+ void AddImageStringsToTable(gc::space::ImageSpace* image_space)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::intern_table_lock_);
+ // Copy the post zygote tables to pre zygote to save memory by preventing dirty pages.
+ void SwapPostZygoteWithPreZygote()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::intern_table_lock_);
+
private:
class StringHashEquals {
public:
- std::size_t operator()(const GcRoot<mirror::String>& root) NO_THREAD_SAFETY_ANALYSIS;
- bool operator()(const GcRoot<mirror::String>& a, const GcRoot<mirror::String>& b)
+ std::size_t operator()(const GcRoot<mirror::String>& root) const NO_THREAD_SAFETY_ANALYSIS;
+ bool operator()(const GcRoot<mirror::String>& a, const GcRoot<mirror::String>& b) const
NO_THREAD_SAFETY_ANALYSIS;
};
- typedef std::unordered_set<GcRoot<mirror::String>, StringHashEquals, StringHashEquals,
- TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>> Table;
+ class GcRootEmptyFn {
+ public:
+ void MakeEmpty(GcRoot<mirror::String>& item) const {
+ item = GcRoot<mirror::String>();
+ }
+ bool IsEmpty(const GcRoot<mirror::String>& item) const {
+ return item.IsNull();
+ }
+ };
+ // Table which holds pre zygote and post zygote interned strings. There is one instance for
+ // weak interns and strong interns.
+ class Table {
+ public:
+ mirror::String* Find(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void Insert(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void Remove(mirror::String* s)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void VisitRoots(RootCallback* callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void SweepWeaks(IsMarkedCallback* callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ void SwapPostZygoteWithPreZygote() EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+ size_t Size() const EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
+ private:
+ typedef HashSet<GcRoot<mirror::String>, GcRootEmptyFn, StringHashEquals, StringHashEquals,
+ TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>> UnorderedSet;
+
+ void SweepWeaks(UnorderedSet* set, IsMarkedCallback* callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
+
+ // We call SwapPostZygoteWithPreZygote when we create the zygote to reduce private dirty pages
+ // caused by modifying the zygote intern table hash table. The pre zygote table are the
+ // interned strings which were interned before we created the zygote space. Post zygote is self
+ // explanatory.
+ UnorderedSet pre_zygote_table_;
+ UnorderedSet post_zygote_table_;
+ };
+
+ // Insert if non null, otherwise return nullptr.
mirror::String* Insert(mirror::String* s, bool is_strong)
LOCKS_EXCLUDED(Locks::intern_table_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::String* LookupStrong(mirror::String* s)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
mirror::String* LookupWeak(mirror::String* s)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::String* Lookup(Table* table, mirror::String* s)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
mirror::String* InsertStrong(mirror::String* s)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
mirror::String* InsertWeak(mirror::String* s)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
void RemoveStrong(mirror::String* s)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -108,14 +172,16 @@
void RemoveWeak(mirror::String* s)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
- void Remove(Table* table, mirror::String* s)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
// Transaction rollback access.
+ mirror::String* LookupStringFromImage(mirror::String* s)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
mirror::String* InsertStrongFromTransaction(mirror::String* s)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
mirror::String* InsertWeakFromTransaction(mirror::String* s)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
void RemoveStrongFromTransaction(mirror::String* s)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -125,6 +191,7 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
friend class Transaction;
+ bool image_added_to_intern_table_ GUARDED_BY(Locks::intern_table_lock_);
bool log_new_roots_ GUARDED_BY(Locks::intern_table_lock_);
bool allow_new_interns_ GUARDED_BY(Locks::intern_table_lock_);
ConditionVariable new_intern_condition_ GUARDED_BY(Locks::intern_table_lock_);
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index e49a408..10a017c 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -181,8 +181,14 @@
for (int i = 0; i < pEvent->modCount; i++) {
const JdwpEventMod* pMod = &pEvent->mods[i];
if (pMod->modKind == MK_LOCATION_ONLY) {
- /* should only be for Breakpoint, Step, and Exception */
- Dbg::WatchLocation(&pMod->locationOnly.loc, &req);
+ // Should only concern breakpoint, field access, field modification, step, and exception
+ // events.
+ // However breakpoint requires specific handling. Field access, field modification and step
+ // events need full deoptimization to be reported while exception event is reported during
+ // exception handling.
+ if (pEvent->eventKind == EK_BREAKPOINT) {
+ Dbg::WatchLocation(&pMod->locationOnly.loc, &req);
+ }
} else if (pMod->modKind == MK_STEP) {
/* should only be for EK_SINGLE_STEP; should only be one */
JdwpStepSize size = static_cast<JdwpStepSize>(pMod->step.size);
@@ -258,8 +264,10 @@
for (int i = 0; i < pEvent->modCount; i++) {
JdwpEventMod* pMod = &pEvent->mods[i];
if (pMod->modKind == MK_LOCATION_ONLY) {
- /* should only be for Breakpoint, Step, and Exception */
- Dbg::UnwatchLocation(&pMod->locationOnly.loc, &req);
+ // Like in RegisterEvent, we need specific handling for breakpoint only.
+ if (pEvent->eventKind == EK_BREAKPOINT) {
+ Dbg::UnwatchLocation(&pMod->locationOnly.loc, &req);
+ }
}
if (pMod->modKind == MK_STEP) {
/* should only be for EK_SINGLE_STEP; should only be one */
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index ad18d8a..b5be3e3 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -16,6 +16,7 @@
#include "object_registry.h"
+#include "handle_scope-inl.h"
#include "mirror/class.h"
#include "scoped_thread_state_change.h"
@@ -48,12 +49,17 @@
return 0;
}
+ Thread* const self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Object> obj_h(hs.NewHandle(o));
+
// Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock.
- int32_t identity_hash_code = o->IdentityHashCode();
- ScopedObjectAccessUnchecked soa(Thread::Current());
+ int32_t identity_hash_code = obj_h->IdentityHashCode();
+
+ ScopedObjectAccessUnchecked soa(self);
MutexLock mu(soa.Self(), lock_);
ObjectRegistryEntry* entry = nullptr;
- if (ContainsLocked(soa.Self(), o, identity_hash_code, &entry)) {
+ if (ContainsLocked(soa.Self(), obj_h.Get(), identity_hash_code, &entry)) {
// This object was already in our map.
++entry->reference_count;
} else {
@@ -68,7 +74,7 @@
// This object isn't in the registry yet, so add it.
JNIEnv* env = soa.Env();
- jobject local_reference = soa.AddLocalReference<jobject>(o);
+ jobject local_reference = soa.AddLocalReference<jobject>(obj_h.Get());
entry->jni_reference_type = JNIWeakGlobalRefType;
entry->jni_reference = env->NewWeakGlobalRef(local_reference);
@@ -82,17 +88,6 @@
return entry->id;
}
-bool ObjectRegistry::Contains(mirror::Object* o, ObjectRegistryEntry** out_entry) {
- if (o == nullptr) {
- return false;
- }
- // Call IdentityHashCode here to avoid a lock level violation between lock_ and monitor_lock.
- int32_t identity_hash_code = o->IdentityHashCode();
- Thread* self = Thread::Current();
- MutexLock mu(self, lock_);
- return ContainsLocked(self, o, identity_hash_code, out_entry);
-}
-
bool ObjectRegistry::ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code,
ObjectRegistryEntry** out_entry) {
DCHECK(o != nullptr);
diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h
index bc3530b..4770a7d 100644
--- a/runtime/jdwp/object_registry.h
+++ b/runtime/jdwp/object_registry.h
@@ -72,10 +72,6 @@
return reinterpret_cast<T>(InternalGet(id));
}
- bool Contains(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return Contains(o, nullptr);
- }
-
void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DisableCollection(JDWP::ObjectId id)
@@ -114,9 +110,6 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
- bool Contains(mirror::Object* o, ObjectRegistryEntry** out_entry)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
-
bool ContainsLocked(Thread* self, mirror::Object* o, int32_t identity_hash_code,
ObjectRegistryEntry** out_entry)
EXCLUSIVE_LOCKS_REQUIRED(lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7af88d6..75d8d91 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -50,7 +50,7 @@
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Array, length_));
}
@@ -90,7 +90,7 @@
// Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
// returns false.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool CheckIsValidIndex(int32_t index) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE bool CheckIsValidIndex(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
protected:
void ThrowArrayStoreException(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 00bed92..336d9ee 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -25,6 +25,8 @@
#include "jvalue.h"
#include "object-inl.h"
#include "primitive.h"
+#include "scoped_thread_state_change.h"
+#include "well_known_classes.h"
namespace art {
namespace mirror {
@@ -284,6 +286,14 @@
return GetDexCache()->GetDexFile();
}
+inline ArtField* ArtField::FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
+ jobject jlr_field) {
+ mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_reflect_Field_artField);
+ mirror::ArtField* field = f->GetObject(soa.Decode<mirror::Object*>(jlr_field))->AsArtField();
+ DCHECK(field != nullptr);
+ return field;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 3c7c6ce..7e20076 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -31,14 +31,6 @@
// TODO: Get global references for these
GcRoot<Class> ArtField::java_lang_reflect_ArtField_;
-ArtField* ArtField::FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
- jobject jlr_field) {
- mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_reflect_Field_artField);
- mirror::ArtField* field = f->GetObject(soa.Decode<mirror::Object*>(jlr_field))->AsArtField();
- DCHECK(field != nullptr);
- return field;
-}
-
void ArtField::SetClass(Class* java_lang_reflect_ArtField) {
CHECK(java_lang_reflect_ArtField_.IsNull());
CHECK(java_lang_reflect_ArtField != NULL);
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index f3dfa15..82d0a64 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -47,8 +47,8 @@
return sizeof(ArtField);
}
- static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
- jobject jlr_field)
+ ALWAYS_INLINE static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
+ jobject jlr_field)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Class* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 9782dde..b02f456 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -69,6 +69,10 @@
return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_));
}
+inline uint16_t ArtMethod::GetMethodIndexDuringLinking() {
+ return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_));
+}
+
inline uint32_t ArtMethod::GetDexMethodIndex() {
#ifdef ART_SEA_IR_MODE
// TODO: Re-add this check for (PORTABLE + SMALL + ) SEA IR when PORTABLE IS fixed!
@@ -329,6 +333,14 @@
return result;
}
+
+inline bool ArtMethod::IsImtUnimplementedMethod() {
+ bool result = this == Runtime::Current()->GetImtUnimplementedMethod();
+ // Check that if we do think it is phony it looks like the imt unimplemented method.
+ DCHECK(!result || IsRuntimeMethod());
+ return result;
+}
+
inline uintptr_t ArtMethod::NativePcOffset(const uintptr_t pc) {
const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
return pc - reinterpret_cast<uintptr_t>(code);
@@ -399,7 +411,7 @@
}
inline const DexFile* ArtMethod::GetDexFile() {
- return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ return GetDexCache()->GetDexFile();
}
inline const char* ArtMethod::GetDeclaringClassDescriptor() {
@@ -519,11 +531,15 @@
return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache();
}
+inline bool ArtMethod::IsProxyMethod() {
+ return GetDeclaringClass()->IsProxyClass();
+}
+
inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy() {
- mirror::Class* klass = GetDeclaringClass();
- if (LIKELY(!klass->IsProxyClass())) {
+ if (LIKELY(!IsProxyMethod())) {
return this;
}
+ mirror::Class* klass = GetDeclaringClass();
mirror::ArtMethod* interface_method = GetDexCacheResolvedMethods()->Get(GetDexMethodIndex());
DCHECK(interface_method != nullptr);
DCHECK_EQ(interface_method,
@@ -531,6 +547,21 @@
return interface_method;
}
+inline void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
+ new_dex_cache_strings);
+}
+
+inline void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
+ new_dex_cache_methods);
+}
+
+inline void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) {
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
+ new_dex_cache_classes);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 27499c2..5b833b9 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -89,21 +89,6 @@
java_lang_reflect_ArtMethod_ = GcRoot<Class>(nullptr);
}
-void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_),
- new_dex_cache_strings);
-}
-
-void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_),
- new_dex_cache_methods);
-}
-
-void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_),
- new_dex_cache_classes);
-}
-
size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
CHECK_LE(1, shorty.length());
uint32_t num_registers = 0;
@@ -118,10 +103,6 @@
return num_registers;
}
-bool ArtMethod::IsProxyMethod() {
- return GetDeclaringClass()->IsProxyClass();
-}
-
ArtMethod* ArtMethod::FindOverriddenMethod() {
if (IsStatic()) {
return NULL;
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index abfdd42..36bb9e0 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -64,7 +64,7 @@
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, declaring_class_));
}
- uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Not called within a transaction.
@@ -168,6 +168,9 @@
uint16_t GetMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Doesn't do erroneous / unresolved class checks.
+ uint16_t GetMethodIndexDuringLinking() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
size_t GetVtableIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetMethodIndex();
}
@@ -193,7 +196,7 @@
// Number of 32bit registers that would be required to hold all the arguments
static size_t NumArgRegisters(const StringPiece& shorty);
- uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetDexMethodIndex(uint32_t new_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Not called within a transaction.
@@ -216,11 +219,11 @@
return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_);
}
- ArtMethod* GetDexCacheResolvedMethod(uint16_t method_idx)
+ ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method)
+ ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods)
+ ALWAYS_INLINE void SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool HasDexCacheResolvedMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool HasSameDexCacheResolvedMethods(ArtMethod* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -409,6 +412,8 @@
bool IsImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsImtUnimplementedMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
uintptr_t NativePcOffset(const uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uintptr_t NativePcOffset(const uintptr_t pc, const void* quick_entry_point)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -536,9 +541,11 @@
static GcRoot<Class> java_lang_reflect_ArtMethod_;
private:
- ObjectArray<ArtMethod>* GetDexCacheResolvedMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ObjectArray<ArtMethod>* GetDexCacheResolvedMethods()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ObjectArray<Class>* GetDexCacheResolvedTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ObjectArray<Class>* GetDexCacheResolvedTypes()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
friend struct art::ArtMethodOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 8e44471..0d109e5 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -113,19 +113,19 @@
template<VerifyObjectFlags kVerifyFlags>
inline ArtMethod* Class::GetVirtualMethod(uint32_t i) {
DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
- return GetVirtualMethods()->Get(i);
+ return GetVirtualMethods()->GetWithoutChecks(i);
}
inline ArtMethod* Class::GetVirtualMethodDuringLinking(uint32_t i) {
DCHECK(IsLoaded() || IsErroneous());
- return GetVirtualMethods()->Get(i);
+ return GetVirtualMethods()->GetWithoutChecks(i);
}
inline void Class::SetVirtualMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ObjectArray<ArtMethod>* virtual_methods =
GetFieldObject<ObjectArray<ArtMethod>>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_));
- virtual_methods->Set<false>(i, f);
+ virtual_methods->SetWithoutChecks<false>(i, f);
}
inline ObjectArray<ArtMethod>* Class::GetVTable() {
@@ -142,14 +142,6 @@
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable);
}
-inline ObjectArray<ArtMethod>* Class::GetImTable() {
- return GetFieldObject<ObjectArray<ArtMethod>>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_));
-}
-
-inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable);
-}
-
inline ArtMethod* Class::GetEmbeddedImTableEntry(uint32_t i) {
uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry);
return GetFieldObject<mirror::ArtMethod>(MemberOffset(offset));
@@ -158,7 +150,6 @@
inline void Class::SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) {
uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry);
SetFieldObject<false>(MemberOffset(offset), method);
- CHECK(method == GetImTable()->Get(i));
}
inline bool Class::HasVTable() {
@@ -733,6 +724,24 @@
}
}
+inline uint32_t Class::NumDirectInterfaces() {
+ if (IsPrimitive()) {
+ return 0;
+ } else if (IsArrayClass()) {
+ return 2;
+ } else if (IsProxyClass()) {
+ mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
+ return interfaces != nullptr ? interfaces->GetLength() : 0;
+ } else {
+ const DexFile::TypeList* interfaces = GetInterfaceTypeList();
+ if (interfaces == nullptr) {
+ return 0;
+ } else {
+ return interfaces->Size();
+ }
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 5b8eb82..4ef6ea0 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -499,7 +499,10 @@
if (GetDexCache() == dex_cache) {
for (size_t i = 0; i < NumVirtualMethods(); ++i) {
ArtMethod* method = GetVirtualMethod(i);
- if (method->GetDexMethodIndex() == dex_method_idx) {
+ if (method->GetDexMethodIndex() == dex_method_idx &&
+ // A miranda method may have a different DexCache and is always created by linking,
+ // never *declared* in the class.
+ !method->IsMiranda()) {
return method;
}
}
@@ -748,24 +751,6 @@
return &GetDexFile().GetClassDef(class_def_idx);
}
-uint32_t Class::NumDirectInterfaces() {
- if (IsPrimitive()) {
- return 0;
- } else if (IsArrayClass()) {
- return 2;
- } else if (IsProxyClass()) {
- mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
- return interfaces != nullptr ? interfaces->GetLength() : 0;
- } else {
- const DexFile::TypeList* interfaces = GetInterfaceTypeList();
- if (interfaces == nullptr) {
- return 0;
- } else {
- return interfaces->Size();
- }
- }
-}
-
uint16_t Class::GetDirectInterfaceTypeIdx(uint32_t idx) {
DCHECK(!IsPrimitive());
DCHECK(!IsArrayClass());
@@ -826,22 +811,21 @@
return GetDexFile().GetInterfacesList(*class_def);
}
-void Class::PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectArray<ArtMethod>* table = GetImTable();
- if (table != nullptr) {
- for (uint32_t i = 0; i < kImtSize; i++) {
- SetEmbeddedImTableEntry(i, table->Get(i));
- }
+void Class::PopulateEmbeddedImtAndVTable(StackHandleScope<kImtSize>* imt_handle_scope) {
+ for (uint32_t i = 0; i < kImtSize; i++) {
+ // Replace null with conflict.
+ mirror::Object* obj = imt_handle_scope->GetReference(i);
+ DCHECK(obj != nullptr);
+ SetEmbeddedImTableEntry(i, obj->AsArtMethod());
}
- table = GetVTableDuringLinking();
+ ObjectArray<ArtMethod>* table = GetVTableDuringLinking();
CHECK(table != nullptr) << PrettyClass(this);
SetEmbeddedVTableLength(table->GetLength());
for (int32_t i = 0; i < table->GetLength(); i++) {
- SetEmbeddedVTableEntry(i, table->Get(i));
+ SetEmbeddedVTableEntry(i, table->GetWithoutChecks(i));
}
- SetImTable(nullptr);
// Keep java.lang.Object class's vtable around for since it's easier
// to be reused by array classes during their linking.
if (!IsObjectClass()) {
@@ -853,9 +837,10 @@
class CopyClassVisitor {
public:
explicit CopyClassVisitor(Thread* self, Handle<mirror::Class>* orig,
- size_t new_length, size_t copy_bytes)
+ size_t new_length, size_t copy_bytes,
+ StackHandleScope<mirror::Class::kImtSize>* imt_handle_scope)
: self_(self), orig_(orig), new_length_(new_length),
- copy_bytes_(copy_bytes) {
+ copy_bytes_(copy_bytes), imt_handle_scope_(imt_handle_scope) {
}
void operator()(Object* obj, size_t usable_size) const
@@ -864,7 +849,7 @@
mirror::Class* new_class_obj = obj->AsClass();
mirror::Object::CopyObject(self_, new_class_obj, orig_->Get(), copy_bytes_);
new_class_obj->SetStatus(Class::kStatusResolving, self_);
- new_class_obj->PopulateEmbeddedImtAndVTable();
+ new_class_obj->PopulateEmbeddedImtAndVTable(imt_handle_scope_);
new_class_obj->SetClassSize(new_length_);
}
@@ -873,10 +858,12 @@
Handle<mirror::Class>* const orig_;
const size_t new_length_;
const size_t copy_bytes_;
+ StackHandleScope<mirror::Class::kImtSize>* const imt_handle_scope_;
DISALLOW_COPY_AND_ASSIGN(CopyClassVisitor);
};
-Class* Class::CopyOf(Thread* self, int32_t new_length) {
+Class* Class::CopyOf(Thread* self, int32_t new_length,
+ StackHandleScope<kImtSize>* imt_handle_scope) {
DCHECK_GE(new_length, static_cast<int32_t>(sizeof(Class)));
// We may get copied by a compacting GC.
StackHandleScope<1> hs(self);
@@ -884,17 +871,15 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
// The num_bytes (3rd param) is sizeof(Class) as opposed to SizeOf()
// to skip copying the tail part that we will overwrite here.
- CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class));
-
+ CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class), imt_handle_scope);
mirror::Object* new_class =
kMovingClasses
? heap->AllocObject<true>(self, java_lang_Class_.Read(), new_length, visitor)
: heap->AllocNonMovableObject<true>(self, java_lang_Class_.Read(), new_length, visitor);
if (UNLIKELY(new_class == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
- return NULL;
+ return nullptr;
}
-
return new_class->AsClass();
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 03a8563..5b87cbd 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -67,6 +67,7 @@
struct ClassOffsets;
class Signature;
class StringPiece;
+template<size_t kNumReferences> class PACKED(4) StackHandleScope;
namespace mirror {
@@ -215,46 +216,46 @@
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns true if the class is an interface.
- bool IsInterface() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsInterface() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccInterface) != 0;
}
// Returns true if the class is declared public.
- bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccPublic) != 0;
}
// Returns true if the class is declared final.
- bool IsFinal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsFinal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccFinal) != 0;
}
- bool IsFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
}
- void SetFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE void SetFinalizable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
SetAccessFlags(flags | kAccClassIsFinalizable);
}
// Returns true if the class is abstract.
- bool IsAbstract() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsAbstract() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccAbstract) != 0;
}
// Returns true if the class is an annotation.
- bool IsAnnotation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsAnnotation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccAnnotation) != 0;
}
// Returns true if the class is synthetic.
- bool IsSynthetic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsSynthetic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccSynthetic) != 0;
}
@@ -582,7 +583,7 @@
// downcast would be necessary. Similarly for interfaces, a class that implements (or an interface
// that extends) another can be assigned to its parent, but not vice-versa. All Classes may assign
// to themselves. Classes for primitive types may not assign to each other.
- inline bool IsAssignableFrom(Class* src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ALWAYS_INLINE bool IsAssignableFrom(Class* src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(src != NULL);
if (this == src) {
// Can always assign to things of the same type.
@@ -599,7 +600,7 @@
}
}
- Class* GetSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE Class* GetSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetSuperClass(Class *new_super_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Super class is assigned once, except during class linker initialization.
@@ -638,12 +639,13 @@
void SetDexCache(DexCache* new_dex_cache) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ObjectArray<ArtMethod>* GetDirectMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ObjectArray<ArtMethod>* GetDirectMethods()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetDirectMethods(ObjectArray<ArtMethod>* new_direct_methods)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* GetDirectMethod(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ArtMethod* GetDirectMethod(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetDirectMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -652,13 +654,14 @@
uint32_t NumDirectMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- ObjectArray<ArtMethod>* GetVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ObjectArray<ArtMethod>* GetVirtualMethods()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods)
+ ALWAYS_INLINE void SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns the number of non-inherited virtual methods.
- uint32_t NumVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE uint32_t NumVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ArtMethod* GetVirtualMethod(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -668,9 +671,10 @@
void SetVirtualMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ObjectArray<ArtMethod>* GetVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ObjectArray<ArtMethod>* GetVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ObjectArray<ArtMethod>* GetVTableDuringLinking() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE ObjectArray<ArtMethod>* GetVTableDuringLinking()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetVTable(ObjectArray<ArtMethod>* new_vtable)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -679,13 +683,6 @@
return OFFSET_OF_OBJECT_MEMBER(Class, vtable_);
}
- void SetImTable(ObjectArray<ArtMethod>* new_imtable)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static MemberOffset ImTableOffset() {
- return OFFSET_OF_OBJECT_MEMBER(Class, imtable_);
- }
-
static MemberOffset EmbeddedImTableOffset() {
return MemberOffset(sizeof(Class));
}
@@ -695,7 +692,7 @@
}
static MemberOffset EmbeddedVTableOffset() {
- return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry) + sizeof(int32_t));
+ return MemberOffset(sizeof(Class) + kImtSize * sizeof(ImTableEntry) + sizeof(int32_t));
}
bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -720,7 +717,8 @@
void SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PopulateEmbeddedImtAndVTable(StackHandleScope<kImtSize>* imt_handle_scope)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Given a method implemented by this class but potentially from a super class, return the
// specific implementation method for this class.
@@ -788,11 +786,11 @@
ArtMethod* FindClassInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- int32_t GetIfTableCount() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE int32_t GetIfTableCount() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- IfTable* GetIfTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE IfTable* GetIfTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetIfTable(IfTable* new_iftable) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE void SetIfTable(IfTable* new_iftable) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get instance fields of the class (See also GetSFields).
ObjectArray<ArtField>* GetIFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -968,7 +966,7 @@
const DexFile::ClassDef* GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -987,7 +985,7 @@
void AssertInitializedOrInitializingInThread(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- Class* CopyOf(Thread* self, int32_t new_length)
+ Class* CopyOf(Thread* self, int32_t new_length, StackHandleScope<kImtSize>* imt_handle_scope)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// For proxy class only.
@@ -1036,8 +1034,6 @@
void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ObjectArray<ArtMethod>* GetImTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// defining class loader, or NULL for the "bootstrap" system loader
HeapReference<ClassLoader> class_loader_;
@@ -1150,11 +1146,11 @@
// The following data exist in real class objects.
// Embedded Imtable, for class object that's not an interface, fixed size.
- ImTableEntry embedded_imtable_[0];
+ // ImTableEntry embedded_imtable_[0];
// Embedded Vtable, for class object that's not an interface, variable size.
- VTableEntry embedded_vtable_[0];
+ // VTableEntry embedded_vtable_[0];
// Static fields, variable size.
- uint32_t fields_[0];
+ // uint32_t fields_[0];
// java.lang.Class
static GcRoot<Class> java_lang_Class_;
diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h
index 3f20bf4..d1309d2 100644
--- a/runtime/mirror/iftable-inl.h
+++ b/runtime/mirror/iftable-inl.h
@@ -27,7 +27,7 @@
DCHECK(interface->IsInterface());
const size_t idx = i * kMax + kInterface;
DCHECK_EQ(Get(idx), static_cast<Object*>(nullptr));
- Set<false>(idx, interface);
+ SetWithoutChecks<false>(idx, interface);
}
} // namespace mirror
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index 5feb602..4d899d2 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -25,13 +25,14 @@
class MANAGED IfTable FINAL : public ObjectArray<Object> {
public:
- Class* GetInterface(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Class* interface = Get((i * kMax) + kInterface)->AsClass();
+ ALWAYS_INLINE Class* GetInterface(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Class* interface = GetWithoutChecks((i * kMax) + kInterface)->AsClass();
DCHECK(interface != NULL);
return interface;
}
- void SetInterface(int32_t i, Class* interface) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE void SetInterface(int32_t i, Class* interface)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ObjectArray<ArtMethod>* GetMethodArray(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ObjectArray<ArtMethod>* method_array =
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 7012b19..6404faf 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -45,11 +45,11 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CheckAssignable(T* object) NO_THREAD_SAFETY_ANALYSIS;
- void Set(int32_t i, T* object) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void Set(int32_t i, T* object) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS;
+ ALWAYS_INLINE void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
// Set element without bound and element type checks, to be used in limited
// circumstances, such as during boot image writing.
@@ -57,15 +57,15 @@
// SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetWithoutChecks(int32_t i, T* object) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS;
+ ALWAYS_INLINE void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
// TODO fix thread safety analysis broken by the use of template. This should be
// SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) ALWAYS_INLINE
+ ALWAYS_INLINE void SetWithoutChecksAndWriteBarrier(int32_t i, T* object)
NO_THREAD_SAFETY_ANALYSIS;
- T* GetWithoutChecks(int32_t i) ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Copy src into this array (dealing with overlaps as memmove does) without assignability checks.
void AssignableMemmove(int32_t dst_pos, ObjectArray<T>* src, int32_t src_pos,
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 01c8978..a8b297d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -179,9 +179,9 @@
return NULL;
}
const std::string descriptor(DotToDescriptor(class_name.c_str()));
-
+ const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
for (const DexFile* dex_file : *dex_files) {
- const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str());
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
if (dex_class_def != nullptr) {
ScopedObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -189,8 +189,8 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
- mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
- *dex_class_def);
+ mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,
+ class_loader, *dex_file, *dex_class_def);
if (result != nullptr) {
VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
return soa.AddLocalReference<jclass>(result);
@@ -287,9 +287,11 @@
template <const bool kVerboseLogging, const bool kReasonLogging>
static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
- InstructionSet target_instruction_set) {
+ InstructionSet target_instruction_set,
+ bool* oat_is_pic) {
std::string error_msg;
std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
+ nullptr,
false, &error_msg));
if (oat_file.get() == nullptr) {
if (kReasonLogging) {
@@ -299,6 +301,11 @@
error_msg.clear();
return kDexoptNeeded;
}
+
+ // Pass-up the information about if this is PIC.
+ // TODO: Refactor this function to be less complicated.
+ *oat_is_pic = oat_file->IsPic();
+
bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
uint32_t location_checksum = 0;
const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
@@ -526,10 +533,16 @@
// Lets try the cache first (since we want to load from there since thats where the relocated
// versions will be).
if (have_cache_filename && !force_system_only) {
+ bool oat_is_pic;
// We can use the dalvik-cache if we find a good file.
dalvik_cache_decision =
IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
- target_instruction_set);
+ target_instruction_set, &oat_is_pic);
+
+ // Apps that are compiled with --compile-pic never need to be patchoat-d
+ if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
+ dalvik_cache_decision = kUpToDate;
+ }
// We will only return DexOptNeeded if both the cache and system return it.
if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
@@ -539,12 +552,18 @@
// We couldn't find one thats easy. We should now try the system.
}
+ bool oat_is_pic;
jbyte system_decision =
IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
- target_instruction_set);
+ target_instruction_set, &oat_is_pic);
CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
<< "May not return PatchoatNeeded when patching is disabled.";
+ // Apps that are compiled with --compile-pic never need to be patchoat-d
+ if (oat_is_pic && system_decision == kPatchoatNeeded) {
+ system_decision = kUpToDate;
+ }
+
if (require_system_version && system_decision == kPatchoatNeeded
&& dalvik_cache_decision == kUpToDate) {
// We have a version from system relocated to the cache. Return it.
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index fc1018a..4b1236a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -254,7 +254,7 @@
if (class_name[1] == '\0') {
klass = linker->FindPrimitiveClass(class_name[0]);
} else {
- klass = linker->LookupClass(class_name, NULL);
+ klass = linker->LookupClass(class_name, ComputeModifiedUtf8Hash(class_name), NULL);
}
if (klass == NULL) {
return;
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 761e800..36b02dc 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -28,26 +28,28 @@
ScopedFastNativeObjectAccess soa(env);
mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
ScopedUtfChars name(env, javaName);
- if (name.c_str() == NULL) {
- return NULL;
+ if (name.c_str() == nullptr) {
+ return nullptr;
}
ClassLinker* cl = Runtime::Current()->GetClassLinker();
std::string descriptor(DotToDescriptor(name.c_str()));
- mirror::Class* c = cl->LookupClass(descriptor.c_str(), loader);
- if (c != NULL && c->IsResolved()) {
+ const size_t descriptor_hash = ComputeModifiedUtf8Hash(descriptor.c_str());
+ mirror::Class* c = cl->LookupClass(descriptor.c_str(), descriptor_hash, loader);
+ if (c != nullptr && c->IsResolved()) {
return soa.AddLocalReference<jclass>(c);
}
if (loader != nullptr) {
// Try the common case.
StackHandleScope<1> hs(soa.Self());
- c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), hs.NewHandle(loader));
+ c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
+ hs.NewHandle(loader));
if (c != nullptr) {
return soa.AddLocalReference<jclass>(c);
}
}
// Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
// the regular loadClass code.
- return NULL;
+ return nullptr;
}
static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 3903ffc..8850df2 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -23,19 +23,21 @@
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
-#include "reflection.h"
+#include "reflection-inl.h"
#include "scoped_fast_native_object_access.h"
namespace art {
-static bool VerifyFieldAccess(mirror::ArtField* field, mirror::Object* obj, bool is_set)
+template<bool kIsSet>
+ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::ArtField* field,
+ mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (field->IsFinal() && is_set) {
+ if (kIsSet && field->IsFinal()) {
ThrowIllegalAccessException(nullptr, StringPrintf("Cannot set final field: %s",
PrettyField(field).c_str()).c_str());
return false;
}
- if (!VerifyAccess(obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
+ if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access field: %s",
PrettyField(field).c_str()).c_str());
return false;
@@ -43,9 +45,10 @@
return true;
}
-static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o,
- mirror::ArtField* f, Primitive::Type field_type, bool allow_references,
- JValue* value)
+template<bool kAllowReferences>
+ALWAYS_INLINE inline static bool GetFieldValue(
+ const ScopedFastNativeObjectAccess& soa, mirror::Object* o, mirror::ArtField* f,
+ Primitive::Type field_type, JValue* value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_EQ(value->GetJ(), INT64_C(0));
switch (field_type) {
@@ -74,7 +77,7 @@
value->SetS(f->GetShort(o));
return true;
case Primitive::kPrimNot:
- if (allow_references) {
+ if (kAllowReferences) {
value->SetL(f->GetObject(o));
return true;
}
@@ -89,28 +92,29 @@
return false;
}
-static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, jobject j_rcvr,
- mirror::ArtField** f, mirror::Object** class_or_rcvr)
+ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa,
+ jobject j_rcvr, mirror::ArtField** f,
+ mirror::Object** class_or_rcvr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
soa.Self()->AssertThreadSuspensionIsAllowable();
+ mirror::Class* declaringClass = (*f)->GetDeclaringClass();
if ((*f)->IsStatic()) {
- StackHandleScope<2> hs(soa.Self());
- HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f));
- Handle<mirror::Class> h_klass(hs.NewHandle((*f)->GetDeclaringClass()));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true))) {
- DCHECK(soa.Self()->IsExceptionPending());
- *class_or_rcvr = nullptr;
- return false;
+ if (UNLIKELY(!declaringClass->IsInitialized())) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f));
+ HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass));
+ if (UNLIKELY(!class_linker->EnsureInitialized(h_klass, true, true))) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return false;
+ }
}
- *class_or_rcvr = h_klass.Get();
+ *class_or_rcvr = declaringClass;
return true;
}
-
*class_or_rcvr = soa.Decode<mirror::Object*>(j_rcvr);
- mirror::Class* declaringClass = (*f)->GetDeclaringClass();
if (!VerifyObjectIsClass(*class_or_rcvr, declaringClass)) {
DCHECK(soa.Self()->IsExceptionPending());
- *class_or_rcvr = nullptr;
return false;
}
return true;
@@ -125,7 +129,7 @@
return nullptr;
}
// If field is not set to be accessible, verify it can be accessed by the caller.
- if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, false)) {
+ if ((accessible == JNI_FALSE) && !VerifyFieldAccess<false>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
@@ -133,15 +137,16 @@
// Get the field's value, boxing if necessary.
Primitive::Type field_type = f->GetTypeAsPrimitiveType();
JValue value;
- if (!GetFieldValue(soa, o, f, field_type, true, &value)) {
+ if (!GetFieldValue<true>(soa, o, f, field_type, &value)) {
DCHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value));
}
-static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
- char dst_descriptor, jboolean accessible) {
+template<Primitive::Type kPrimitiveType>
+ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaField,
+ jobject javaObj, jboolean accessible) {
ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
mirror::Object* o = nullptr;
@@ -151,7 +156,7 @@
}
// If field is not set to be accessible, verify it can be accessed by the caller.
- if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, false)) {
+ if (accessible == JNI_FALSE && !VerifyFieldAccess<false>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
@@ -160,15 +165,22 @@
// Read the value.
Primitive::Type field_type = f->GetTypeAsPrimitiveType();
JValue field_value;
- if (!GetFieldValue(soa, o, f, field_type, false, &field_value)) {
+ if (field_type == kPrimitiveType) {
+ // This if statement should get optimized out since we only pass in valid primitive types.
+ if (UNLIKELY(!GetFieldValue<false>(soa, o, f, kPrimitiveType, &field_value))) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return JValue();
+ }
+ return field_value;
+ }
+ if (!GetFieldValue<false>(soa, o, f, field_type, &field_value)) {
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
-
// Widen it if necessary (and possible).
JValue wide_value;
- if (!ConvertPrimitiveValue(NULL, false, field_type, Primitive::GetType(dst_descriptor),
- field_value, &wide_value)) {
+ if (!ConvertPrimitiveValue(nullptr, false, field_type, kPrimitiveType, field_value,
+ &wide_value)) {
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
@@ -177,36 +189,36 @@
static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj,
jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'Z', accessible).GetZ();
+ return GetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, accessible).GetZ();
}
static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'B', accessible).GetB();
+ return GetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, accessible).GetB();
}
static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'C', accessible).GetC();
+ return GetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, accessible).GetC();
}
static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj,
jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'D', accessible).GetD();
+ return GetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, accessible).GetD();
}
static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'F', accessible).GetF();
+ return GetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, accessible).GetF();
}
static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'I', accessible).GetI();
+ return GetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, accessible).GetI();
}
static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'J', accessible).GetJ();
+ return GetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, accessible).GetJ();
}
static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
- return GetPrimitiveField(env, javaField, javaObj, 'S', accessible).GetS();
+ return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, accessible).GetS();
}
static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o,
@@ -289,14 +301,15 @@
return;
}
// If field is not set to be accessible, verify it can be accessed by the caller.
- if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, true)) {
+ if ((accessible == JNI_FALSE) && !VerifyFieldAccess<true>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
SetFieldValue(soa, o, f, field_prim_type, true, unboxed_value);
}
-static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
+template<Primitive::Type kPrimitiveType>
+static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
const JValue& new_value, jboolean accessible) {
ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
@@ -313,14 +326,13 @@
// Widen the value if necessary (and possible).
JValue wide_value;
- if (!ConvertPrimitiveValue(nullptr, false, Primitive::GetType(src_descriptor),
- field_type, new_value, &wide_value)) {
+ if (!ConvertPrimitiveValue(nullptr, false, kPrimitiveType, field_type, new_value, &wide_value)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
// If field is not set to be accessible, verify it can be accessed by the caller.
- if ((accessible == JNI_FALSE) && !VerifyFieldAccess(f, o, true)) {
+ if ((accessible == JNI_FALSE) && !VerifyFieldAccess<true>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
@@ -333,56 +345,56 @@
jboolean accessible) {
JValue value;
value.SetZ(z);
- SetPrimitiveField(env, javaField, javaObj, 'Z', value, accessible);
+ SetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, value, accessible);
}
static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b,
jboolean accessible) {
JValue value;
value.SetB(b);
- SetPrimitiveField(env, javaField, javaObj, 'B', value, accessible);
+ SetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, value, accessible);
}
static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c,
jboolean accessible) {
JValue value;
value.SetC(c);
- SetPrimitiveField(env, javaField, javaObj, 'C', value, accessible);
+ SetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, value, accessible);
}
static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d,
jboolean accessible) {
JValue value;
value.SetD(d);
- SetPrimitiveField(env, javaField, javaObj, 'D', value, accessible);
+ SetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, value, accessible);
}
static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f,
jboolean accessible) {
JValue value;
value.SetF(f);
- SetPrimitiveField(env, javaField, javaObj, 'F', value, accessible);
+ SetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, value, accessible);
}
static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i,
jboolean accessible) {
JValue value;
value.SetI(i);
- SetPrimitiveField(env, javaField, javaObj, 'I', value, accessible);
+ SetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, value, accessible);
}
static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j,
jboolean accessible) {
JValue value;
value.SetJ(j);
- SetPrimitiveField(env, javaField, javaObj, 'J', value, accessible);
+ SetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, value, accessible);
}
static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s,
jboolean accessible) {
JValue value;
value.SetS(s);
- SetPrimitiveField(env, javaField, javaObj, 'S', value, accessible);
+ SetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, value, accessible);
}
static JNINativeMethod gMethods[] = {
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index cc44615..9f77e55 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -107,10 +107,11 @@
GetMethodShorty, GetNativeMethodCount, GetNativeMethods
};
-void LoadNativeBridge(std::string& native_bridge_library_filename) {
- android::LoadNativeBridge(native_bridge_library_filename.c_str(), &native_bridge_art_callbacks_);
+bool LoadNativeBridge(std::string& native_bridge_library_filename) {
VLOG(startup) << "Runtime::Setup native bridge library: "
<< (native_bridge_library_filename.empty() ? "(empty)" : native_bridge_library_filename);
+ return android::LoadNativeBridge(native_bridge_library_filename.c_str(),
+ &native_bridge_art_callbacks_);
}
void PreInitializeNativeBridge(std::string dir) {
@@ -118,7 +119,6 @@
#ifndef __APPLE__ // Mac OS does not support CLONE_NEWNS.
if (unshare(CLONE_NEWNS) == -1) {
LOG(WARNING) << "Could not create mount namespace.";
- return;
}
android::PreInitializeNativeBridge(dir.c_str(), GetInstructionSetString(kRuntimeISA));
#endif
diff --git a/runtime/native_bridge_art_interface.h b/runtime/native_bridge_art_interface.h
index 42f0ed2..c287f3b 100644
--- a/runtime/native_bridge_art_interface.h
+++ b/runtime/native_bridge_art_interface.h
@@ -26,7 +26,7 @@
// Mirror libnativebridge interface. Done to have the ART callbacks out of line, and not require
// the system/core header file in other files.
-void LoadNativeBridge(std::string& native_bridge_library_filename);
+bool LoadNativeBridge(std::string& native_bridge_library_filename);
// This is mostly for testing purposes, as in a full system this is called by Zygote code.
void PreInitializeNativeBridge(std::string dir);
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ede108c..9d11de8 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -470,6 +470,12 @@
return sizeof(OatHeader) + key_value_store_size_;
}
+bool OatHeader::IsPic() const {
+ const char* pic_string = GetStoreValueByKey(OatHeader::kPicKey);
+ static const char kTrue[] = "true";
+ return (pic_string != nullptr && strncmp(pic_string, kTrue, sizeof(kTrue)) == 0);
+}
+
void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
if (key_value_store != nullptr) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 6d5fefe..a5cb4bc 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -35,6 +35,7 @@
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
+ static constexpr const char* kPicKey = "pic";
static OatHeader* Create(InstructionSet instruction_set,
const InstructionSetFeatures& instruction_set_features,
@@ -103,6 +104,7 @@
bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const;
size_t GetHeaderSize() const;
+ bool IsPic() const;
private:
OatHeader(InstructionSet instruction_set,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 025f87d..9ba860c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -67,10 +67,11 @@
OatFile* OatFile::Open(const std::string& filename,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool executable,
std::string* error_msg) {
CHECK(!filename.empty()) << location;
- CheckLocation(filename);
+ CheckLocation(location);
std::unique_ptr<OatFile> ret;
if (kUsePortableCompiler && executable) {
// If we are using PORTABLE, use dlopen to deal with relocations.
@@ -91,7 +92,8 @@
*error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
return nullptr;
}
- ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg));
+ ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+ error_msg));
// It would be nice to unlink here. But we might have opened the file created by the
// ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -102,12 +104,12 @@
OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, true, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
}
OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, false, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
}
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
@@ -125,11 +127,13 @@
OatFile* OatFile::OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool writable,
bool executable,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
- bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
+ bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
+ error_msg);
if (!success) {
CHECK(!error_msg->empty());
return nullptr;
@@ -188,9 +192,12 @@
return Setup(error_msg);
}
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, uint8_t* oat_file_begin,
+ bool writable, bool executable,
std::string* error_msg) {
- elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+ // TODO: rename requested_base to oat_data_begin
+ elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
+ oat_file_begin));
if (elf_file_.get() == nullptr) {
DCHECK(!error_msg->empty());
return false;
@@ -599,4 +606,9 @@
method->SetNativeGcMap(GetNativeGcMap());
}
+bool OatFile::IsPic() const {
+ return GetOatHeader().IsPic();
+ // TODO: Check against oat_patches. b/18144996
+}
+
} // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index e5cd6ec..488988e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -49,6 +49,7 @@
static OatFile* Open(const std::string& filename,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool executable,
std::string* error_msg);
@@ -72,6 +73,8 @@
return is_executable_;
}
+ bool IsPic() const;
+
ElfFile* GetElfFile() const {
CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
<< "Cannot get an elf file from " << GetLocation();
@@ -303,13 +306,16 @@
static OatFile* OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
std::string* error_msg);
explicit OatFile(const std::string& filename, bool executable);
bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
- bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ bool ElfFileOpen(File* file, byte* requested_base,
+ uint8_t* oat_file_begin, // Override where the file is loaded to if not null
+ bool writable, bool executable,
std::string* error_msg);
bool Setup(std::string* error_msg);
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 98eeda7..d4713a0 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -229,7 +229,7 @@
reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
break;
case kLongLoVReg:
- if (GetVRegKind(reg + 1, kinds), kLongHiVReg) {
+ if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
// Treat it as a "long" register pair.
new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg));
} else {
@@ -237,14 +237,14 @@
}
break;
case kLongHiVReg:
- if (GetVRegKind(reg - 1, kinds), kLongLoVReg) {
+ if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
// Nothing to do: we treated it as a "long" register pair.
} else {
new_frame->SetVReg(reg, GetVReg(m, reg, kind));
}
break;
case kDoubleLoVReg:
- if (GetVRegKind(reg + 1, kinds), kDoubleHiVReg) {
+ if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
// Treat it as a "double" register pair.
new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg));
} else {
@@ -252,7 +252,7 @@
}
break;
case kDoubleHiVReg:
- if (GetVRegKind(reg - 1, kinds), kDoubleLoVReg) {
+ if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
// Nothing to do: we treated it as a "double" register pair.
} else {
new_frame->SetVReg(reg, GetVReg(m, reg, kind));
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
new file mode 100644
index 0000000..be4d560
--- /dev/null
+++ b/runtime/reflection-inl.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 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_REFLECTION_INL_H_
+#define ART_RUNTIME_REFLECTION_INL_H_
+
+#include "reflection.h"
+
+#include "base/stringprintf.h"
+#include "common_throws.h"
+#include "jvalue.h"
+#include "primitive.h"
+#include "utils.h"
+
+namespace art {
+
+inline bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+ 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());
+ return true;
+ }
+ switch (dstType) {
+ case Primitive::kPrimBoolean: // Fall-through.
+ case Primitive::kPrimChar: // Fall-through.
+ case Primitive::kPrimByte:
+ // Only expect assignment with source and destination of identical type.
+ break;
+ case Primitive::kPrimShort:
+ if (srcType == Primitive::kPrimByte) {
+ dst->SetS(src.GetI());
+ return true;
+ }
+ break;
+ case Primitive::kPrimInt:
+ if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+ srcType == Primitive::kPrimShort) {
+ dst->SetI(src.GetI());
+ return true;
+ }
+ break;
+ case Primitive::kPrimLong:
+ if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+ srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
+ dst->SetJ(src.GetI());
+ return true;
+ }
+ break;
+ case Primitive::kPrimFloat:
+ if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+ srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
+ dst->SetF(src.GetI());
+ return true;
+ } else if (srcType == Primitive::kPrimLong) {
+ dst->SetF(src.GetJ());
+ return true;
+ }
+ break;
+ case Primitive::kPrimDouble:
+ if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
+ srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
+ dst->SetD(src.GetI());
+ return true;
+ } else if (srcType == Primitive::kPrimLong) {
+ dst->SetD(src.GetJ());
+ return true;
+ } else if (srcType == Primitive::kPrimFloat) {
+ dst->SetD(src.GetF());
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!unbox_for_result) {
+ ThrowIllegalArgumentException(throw_location,
+ StringPrintf("Invalid primitive conversion from %s to %s",
+ PrettyDescriptor(srcType).c_str(),
+ PrettyDescriptor(dstType).c_str()).c_str());
+ } else {
+ ThrowClassCastException(throw_location,
+ StringPrintf("Couldn't convert result of type %s to %s",
+ PrettyDescriptor(srcType).c_str(),
+ PrettyDescriptor(dstType).c_str()).c_str());
+ }
+ return false;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_REFLECTION_INL_H_
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4cd61a5..18fcee4 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "reflection.h"
+#include "reflection-inl.h"
#include "class_linker.h"
#include "common_throws.h"
@@ -565,7 +565,7 @@
}
// If method is not set to be accessible, verify it can be accessed by the caller.
- if (!accessible && !VerifyAccess(receiver, declaring_class, m->GetAccessFlags())) {
+ if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags())) {
ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access method: %s",
PrettyMethod(m).c_str()).c_str());
return nullptr;
@@ -617,84 +617,6 @@
return true;
}
-static std::string PrettyDescriptor(Primitive::Type type) {
- return PrettyDescriptor(Primitive::Descriptor(type));
-}
-
-bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
- 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());
- return true;
- }
- switch (dstType) {
- case Primitive::kPrimBoolean: // Fall-through.
- case Primitive::kPrimChar: // Fall-through.
- case Primitive::kPrimByte:
- // Only expect assignment with source and destination of identical type.
- break;
- case Primitive::kPrimShort:
- if (srcType == Primitive::kPrimByte) {
- dst->SetS(src.GetI());
- return true;
- }
- break;
- case Primitive::kPrimInt:
- if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
- srcType == Primitive::kPrimShort) {
- dst->SetI(src.GetI());
- return true;
- }
- break;
- case Primitive::kPrimLong:
- if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
- srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
- dst->SetJ(src.GetI());
- return true;
- }
- break;
- case Primitive::kPrimFloat:
- if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
- srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
- dst->SetF(src.GetI());
- return true;
- } else if (srcType == Primitive::kPrimLong) {
- dst->SetF(src.GetJ());
- return true;
- }
- break;
- case Primitive::kPrimDouble:
- if (srcType == Primitive::kPrimByte || srcType == Primitive::kPrimChar ||
- srcType == Primitive::kPrimShort || srcType == Primitive::kPrimInt) {
- dst->SetD(src.GetI());
- return true;
- } else if (srcType == Primitive::kPrimLong) {
- dst->SetD(src.GetJ());
- return true;
- } else if (srcType == Primitive::kPrimFloat) {
- dst->SetD(src.GetF());
- return true;
- }
- break;
- default:
- break;
- }
- if (!unbox_for_result) {
- ThrowIllegalArgumentException(throw_location,
- StringPrintf("Invalid primitive conversion from %s to %s",
- PrettyDescriptor(srcType).c_str(),
- PrettyDescriptor(dstType).c_str()).c_str());
- } else {
- ThrowClassCastException(throw_location,
- StringPrintf("Couldn't convert result of type %s to %s",
- PrettyDescriptor(srcType).c_str(),
- PrettyDescriptor(dstType).c_str()).c_str());
- }
- return false;
-}
-
mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) {
if (src_class == Primitive::kPrimNot) {
return value.GetL();
@@ -866,16 +788,18 @@
return UnboxPrimitive(&throw_location, o, dst_class, nullptr, unboxed_value);
}
-bool VerifyAccess(mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
- NthCallerVisitor visitor(Thread::Current(), 2);
+bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
+ if ((access_flags & kAccPublic) != 0) {
+ return true;
+ }
+ NthCallerVisitor visitor(self, 2);
visitor.WalkStack();
if (UNLIKELY(visitor.caller == nullptr)) {
// The caller is an attached native thread.
- return (access_flags & kAccPublic) != 0;
+ return false;
}
mirror::Class* caller_class = visitor.caller->GetDeclaringClass();
-
- if (((access_flags & kAccPublic) != 0) || (caller_class == declaring_class)) {
+ if (caller_class == declaring_class) {
return true;
}
if ((access_flags & kAccPrivate) != 0) {
@@ -889,10 +813,7 @@
return true;
}
}
- if (!declaring_class->IsInSamePackage(caller_class)) {
- return false;
- }
- return true;
+ return declaring_class->IsInSamePackage(caller_class);
}
} // namespace art
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 2c54c06..0f41aca 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -42,9 +42,9 @@
mirror::Class* dst_class, JValue* unboxed_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
- Primitive::Type src_class, Primitive::Type dst_class,
- const JValue& src, JValue* dst)
+ALWAYS_INLINE bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+ Primitive::Type src_class, Primitive::Type dst_class,
+ const JValue& src, JValue* dst)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
@@ -74,7 +74,8 @@
bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-bool VerifyAccess(mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags)
+bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
+ uint32_t access_flags)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
} // namespace art
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index ac9026b..001598f 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -49,6 +49,11 @@
return imt_conflict_method_.Read();
}
+inline mirror::ArtMethod* Runtime::GetImtUnimplementedMethod() {
+ CHECK(!imt_unimplemented_method_.IsNull());
+ return imt_unimplemented_method_.Read();
+}
+
inline mirror::ObjectArray<mirror::ArtMethod>* Runtime::GetDefaultImt()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK(HasDefaultImt());
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index bcb9e0c..053769a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -147,10 +147,14 @@
target_sdk_version_(0),
implicit_null_checks_(false),
implicit_so_checks_(false),
- implicit_suspend_checks_(false) {
+ implicit_suspend_checks_(false),
+ is_native_bridge_loaded_(false) {
}
Runtime::~Runtime() {
+ if (is_native_bridge_loaded_) {
+ UnloadNativeBridge();
+ }
if (dump_gc_performance_on_shutdown_) {
// This can't be called from the Heap destructor below because it
// could call RosAlloc::InspectAll() which needs the thread_list
@@ -172,9 +176,6 @@
BackgroundMethodSamplingProfiler::Shutdown();
}
- // Shutdown the fault manager if it was initialized.
- fault_manager.Shutdown();
-
Trace::Shutdown();
// Make sure to let the GC complete if it is running.
@@ -187,6 +188,10 @@
// Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
delete thread_list_;
+
+ // Shutdown the fault manager if it was initialized.
+ fault_manager.Shutdown();
+
delete monitor_list_;
delete monitor_pool_;
delete class_linker_;
@@ -410,8 +415,14 @@
started_ = true;
+ if (IsZygote()) {
+ ScopedObjectAccess soa(self);
+ Runtime::Current()->GetInternTable()->AddImageStringsToTable(heap_->GetImageSpace());
+ Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable();
+ }
+
if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) {
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
StackHandleScope<1> hs(soa.Self());
auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
class_linker_->EnsureInitialized(klass, true, true);
@@ -433,12 +444,11 @@
return false;
}
} else {
- bool have_native_bridge = !native_bridge_library_filename_.empty();
- if (have_native_bridge) {
+ if (is_native_bridge_loaded_) {
PreInitializeNativeBridge(".");
}
- DidForkFromZygote(self->GetJniEnv(), have_native_bridge ? NativeBridgeAction::kInitialize :
- NativeBridgeAction::kUnload, GetInstructionSetString(kRuntimeISA));
+ DidForkFromZygote(self->GetJniEnv(), NativeBridgeAction::kInitialize,
+ GetInstructionSetString(kRuntimeISA));
}
StartDaemonThreads();
@@ -517,14 +527,17 @@
void Runtime::DidForkFromZygote(JNIEnv* env, NativeBridgeAction action, const char* isa) {
is_zygote_ = false;
- switch (action) {
- case NativeBridgeAction::kUnload:
- UnloadNativeBridge();
- break;
+ if (is_native_bridge_loaded_) {
+ switch (action) {
+ case NativeBridgeAction::kUnload:
+ UnloadNativeBridge();
+ is_native_bridge_loaded_ = false;
+ break;
- case NativeBridgeAction::kInitialize:
- InitializeNativeBridge(env, isa);
- break;
+ case NativeBridgeAction::kInitialize:
+ InitializeNativeBridge(env, isa);
+ break;
+ }
}
// Create the thread pool.
@@ -881,8 +894,7 @@
// Runtime::Start():
// DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
// No-op wrt native bridge.
- native_bridge_library_filename_ = options->native_bridge_library_filename_;
- LoadNativeBridge(native_bridge_library_filename_);
+ is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_);
VLOG(startup) << "Runtime::Init exiting";
return true;
@@ -1155,6 +1167,9 @@
if (HasImtConflictMethod()) {
imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
}
+ if (!imt_unimplemented_method_.IsNull()) {
+ imt_unimplemented_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
+ }
if (HasDefaultImt()) {
default_imt_.VisitRoot(callback, arg, 0, kRootVMInternal);
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8cfa8aa..60cb529 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -310,6 +310,7 @@
// Returns a special method that calls into a trampoline for runtime imt conflicts.
mirror::ArtMethod* GetImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::ArtMethod* GetImtUnimplementedMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool HasImtConflictMethod() const {
return !imt_conflict_method_.IsNull();
@@ -318,6 +319,9 @@
void SetImtConflictMethod(mirror::ArtMethod* method) {
imt_conflict_method_ = GcRoot<mirror::ArtMethod>(method);
}
+ void SetImtUnimplementedMethod(mirror::ArtMethod* method) {
+ imt_unimplemented_method_ = GcRoot<mirror::ArtMethod>(method);
+ }
mirror::ArtMethod* CreateImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -515,6 +519,9 @@
GcRoot<mirror::Throwable> pre_allocated_NoClassDefFoundError_;
GcRoot<mirror::ArtMethod> resolution_method_;
GcRoot<mirror::ArtMethod> imt_conflict_method_;
+ // Unresolved method has the same behavior as the conflict method, it is used by the class linker
+ // for differentiating between unfilled imt slots vs conflict slots in superclasses.
+ GcRoot<mirror::ArtMethod> imt_unimplemented_method_;
GcRoot<mirror::ObjectArray<mirror::ArtMethod>> default_imt_;
InstructionSet instruction_set_;
@@ -638,14 +645,16 @@
bool implicit_so_checks_; // StackOverflow checks are implicit.
bool implicit_suspend_checks_; // Thread suspension checks are implicit.
- // The filename to the native bridge library. If this is not empty the native bridge will be
- // initialized and loaded from the given file (initialized and available). An empty value means
- // that there's no native bridge (initialized but not available).
+ // Whether or not a native bridge has been loaded.
//
// The native bridge allows running native code compiled for a foreign ISA. The way it works is,
// if standard dlopen fails to load native library associated with native activity, it calls to
// the native bridge to load it and then gets the trampoline for the entry to native activity.
- std::string native_bridge_library_filename_;
+ //
+ // The option 'native_bridge_library_filename' specifies the name of the native bridge.
+ // When non-empty the native bridge will be loaded from the given file. An empty value means
+ // that there's no native bridge.
+ bool is_native_bridge_loaded_;
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 45d799d..d231525 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -168,6 +168,9 @@
self->GetJniEnv()->DeleteGlobalRef(self->tlsPtr_.jpeer);
self->tlsPtr_.jpeer = nullptr;
self->SetThreadName(self->GetThreadName(soa)->ToModifiedUtf8().c_str());
+
+ mirror::ArtField* priorityField = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority);
+ self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
Dbg::PostThreadStart(self);
// Invoke the 'run' method of our java.lang.Thread.
@@ -409,6 +412,11 @@
}
}
+ {
+ ScopedObjectAccess soa(self);
+ Dbg::PostThreadStart(self);
+ }
+
return self;
}
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 73a9e54..d5db983 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -55,6 +55,13 @@
int newNice = kNiceValues[newPriority-1];
pid_t tid = GetTid();
+ // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a
+ // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background
+ // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will
+ // have to call setPriority twice to do what they want :
+ //
+ // Thread.setPriority(Thread.MIN_PRIORITY); // no-op wrt to cgroups
+ // Thread.setPriority(Thread.MAX_PRIORITY); // will actually change cgroups.
if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
set_sched_policy(tid, SP_BACKGROUND);
} else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 011bf96..24ff92e 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -625,6 +625,7 @@
{
MutexLock mu(self, *Locks::thread_suspend_count_lock_);
// Update global suspend all state for attaching threads.
+ DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
++suspend_all_count_;
++debug_suspend_all_count_;
// Increment everybody's suspend count (except our own).
@@ -716,6 +717,55 @@
VLOG(threads) << *self << " self-reviving (debugger)";
}
+void ThreadList::ResumeAllForDebugger() {
+ Thread* self = Thread::Current();
+ Thread* debug_thread = Dbg::GetDebugThread();
+ bool needs_resume = false;
+
+ VLOG(threads) << *self << " ResumeAllForDebugger starting...";
+
+ // Threads can't resume if we exclusively hold the mutator lock.
+ Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ // Update global suspend all state for attaching threads.
+ DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
+ needs_resume = (debug_suspend_all_count_ > 0);
+ if (needs_resume) {
+ --suspend_all_count_;
+ --debug_suspend_all_count_;
+ // Decrement everybody's suspend count (except our own).
+ for (const auto& thread : list_) {
+ if (thread == self || thread == debug_thread) {
+ continue;
+ }
+ if (thread->GetDebugSuspendCount() == 0) {
+ // This thread may have been individually resumed with ThreadReference.Resume.
+ continue;
+ }
+ VLOG(threads) << "requesting thread resume: " << *thread;
+ thread->ModifySuspendCount(self, -1, true);
+ }
+ } else {
+ // We've been asked to resume all threads without being asked to
+ // suspend them all before. Let's print a warning.
+ LOG(WARNING) << "Debugger attempted to resume all threads without "
+ << "having suspended them all before.";
+ }
+ }
+ }
+
+ if (needs_resume) {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ Thread::resume_cond_->Broadcast(self);
+ }
+
+ VLOG(threads) << *self << " ResumeAllForDebugger complete";
+}
+
void ThreadList::UndoDebuggerSuspensions() {
Thread* self = Thread::Current();
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index bb4f775..9f515a8 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -104,6 +104,11 @@
void SuspendSelfForDebugger()
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_);
+ // Resume all threads
+ void ResumeAllForDebugger()
+ LOCKS_EXCLUDED(Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
+
void UndoDebuggerSuspensions()
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 02cbe3b..797c717 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -85,10 +85,10 @@
return hash;
}
-int32_t ComputeUtf8Hash(const char* chars) {
- int32_t hash = 0;
+size_t ComputeModifiedUtf8Hash(const char* chars) {
+ size_t hash = 0;
while (*chars != '\0') {
- hash = hash * 31 + GetUtf16FromUtf8(&chars);
+ hash = hash * 31 + *chars++;
}
return hash;
}
diff --git a/runtime/utf.h b/runtime/utf.h
index a09db9d..b55227b 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -79,8 +79,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count);
-// Compute a hash code of a UTF-8 string.
-int32_t ComputeUtf8Hash(const char* chars);
+// Compute a hash code of a modified UTF-8 string. Not the standard java hash since it returns a
+// size_t and hashes individual chars instead of codepoint words.
+size_t ComputeModifiedUtf8Hash(const char* chars);
/*
* Retrieve the next UTF-16 character from a UTF-8 string.
diff --git a/runtime/utils.cc b/runtime/utils.cc
index d01c2cb..3765759 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1376,4 +1376,8 @@
return true;
}
+std::string PrettyDescriptor(Primitive::Type type) {
+ return PrettyDescriptor(Primitive::Descriptor(type));
+}
+
} // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index 1dfa02a..5bdbba8 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -25,9 +25,10 @@
#include <vector>
#include "base/logging.h"
+#include "base/mutex.h"
#include "globals.h"
#include "instruction_set.h"
-#include "base/mutex.h"
+#include "primitive.h"
#ifdef HAVE_ANDROID_OS
#include "cutils/properties.h"
@@ -280,6 +281,7 @@
std::string PrettyDescriptor(const char* descriptor);
std::string PrettyDescriptor(mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+std::string PrettyDescriptor(Primitive::Type type);
// Returns a human-readable signature for 'f'. Something like "a.b.C.f" or
// "int a.b.C.f" (depending on the value of 'with_type').
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index d6c90e1..1b2c3ee 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -44,6 +44,18 @@
EXPECT_EQ("java.lang.String", PrettyDescriptor("Ljava/lang/String;"));
}
+TEST_F(UtilsTest, PrettyDescriptor_Primitive) {
+ EXPECT_EQ("boolean", PrettyDescriptor(Primitive::kPrimBoolean));
+ EXPECT_EQ("byte", PrettyDescriptor(Primitive::kPrimByte));
+ EXPECT_EQ("char", PrettyDescriptor(Primitive::kPrimChar));
+ EXPECT_EQ("short", PrettyDescriptor(Primitive::kPrimShort));
+ EXPECT_EQ("int", PrettyDescriptor(Primitive::kPrimInt));
+ EXPECT_EQ("float", PrettyDescriptor(Primitive::kPrimFloat));
+ EXPECT_EQ("long", PrettyDescriptor(Primitive::kPrimLong));
+ EXPECT_EQ("double", PrettyDescriptor(Primitive::kPrimDouble));
+ EXPECT_EQ("void", PrettyDescriptor(Primitive::kPrimVoid));
+}
+
TEST_F(UtilsTest, PrettyDescriptor_PrimitiveArrays) {
EXPECT_EQ("boolean[]", PrettyDescriptor("[Z"));
EXPECT_EQ("boolean[][]", PrettyDescriptor("[[Z"));
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5df4d6e..c88a295 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -87,6 +87,23 @@
}
}
+// Note: returns true on failure.
+ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition,
+ const char* error_msg, uint32_t work_insn_idx) {
+ if (kIsDebugBuild) {
+ // In a debug build, abort if the error condition is wrong.
+ DCHECK(condition) << error_msg << work_insn_idx;
+ } else {
+ // In a non-debug build, just fail the class.
+ if (!condition) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx;
+ return true;
+ }
+ }
+
+ return false;
+}
+
MethodVerifier::FailureKind MethodVerifier::VerifyClass(mirror::Class* klass,
bool allow_soft_failures,
std::string* error) {
@@ -1951,7 +1968,11 @@
while (0 != instance_of_idx && !insn_flags_[instance_of_idx].IsOpcode()) {
instance_of_idx--;
}
- CHECK(insn_flags_[instance_of_idx].IsOpcode());
+ if (FailOrAbort(this, insn_flags_[instance_of_idx].IsOpcode(),
+ "Unable to get previous instruction of if-eqz/if-nez for work index ",
+ work_insn_idx_)) {
+ break;
+ }
} else {
break;
}
@@ -2009,7 +2030,11 @@
while (0 != move_idx && !insn_flags_[move_idx].IsOpcode()) {
move_idx--;
}
- CHECK(insn_flags_[move_idx].IsOpcode());
+ if (FailOrAbort(this, insn_flags_[move_idx].IsOpcode(),
+ "Unable to get previous instruction of if-eqz/if-nez for work index ",
+ work_insn_idx_)) {
+ break;
+ }
const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx);
switch (move_inst->Opcode()) {
case Instruction::MOVE_OBJECT:
@@ -2986,7 +3011,12 @@
// odd case, but nothing to do
} else {
common_super = &common_super->Merge(exception, ®_types_);
- CHECK(reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super));
+ if (FailOrAbort(this,
+ reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super),
+ "java.lang.Throwable is not assignable-from common_super at ",
+ work_insn_idx_)) {
+ break;
+ }
}
}
}
@@ -3312,17 +3342,32 @@
if (klass->IsInterface()) {
// Derive Object.class from Class.class.getSuperclass().
mirror::Class* object_klass = klass->GetClass()->GetSuperClass();
- CHECK(object_klass->IsObjectClass());
+ if (FailOrAbort(this, object_klass->IsObjectClass(),
+ "Failed to find Object class in quickened invoke receiver",
+ work_insn_idx_)) {
+ return nullptr;
+ }
dispatch_class = object_klass;
} else {
dispatch_class = klass;
}
- CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class);
+ if (FailOrAbort(this, dispatch_class->HasVTable(),
+ "Receiver class has no vtable for quickened invoke at ",
+ work_insn_idx_)) {
+ return nullptr;
+ }
uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
- CHECK_LT(static_cast<int32_t>(vtable_index), dispatch_class->GetVTableLength())
- << PrettyDescriptor(klass);
+ if (FailOrAbort(this, static_cast<int32_t>(vtable_index) < dispatch_class->GetVTableLength(),
+ "Receiver class has not enough vtable slots for quickened invoke at ",
+ work_insn_idx_)) {
+ return nullptr;
+ }
mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
- CHECK(!Thread::Current()->IsExceptionPending());
+ if (FailOrAbort(this, !Thread::Current()->IsExceptionPending(),
+ "Unexpected exception pending for quickened invoke at ",
+ work_insn_idx_)) {
+ return nullptr;
+ }
return res_method;
}
@@ -3335,7 +3380,14 @@
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name();
return nullptr;
}
- CHECK(!res_method->IsDirect() && !res_method->IsStatic());
+ if (FailOrAbort(this, !res_method->IsDirect(), "Quick-invoked method is direct at ",
+ work_insn_idx_)) {
+ return nullptr;
+ }
+ if (FailOrAbort(this, !res_method->IsStatic(), "Quick-invoked method is static at ",
+ work_insn_idx_)) {
+ return nullptr;
+ }
// We use vAA as our expected arg count, rather than res_method->insSize, because we need to
// match the call to the signature. Also, we might be calling through an abstract method
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 63fb2d9..4bb99c1 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -147,7 +147,7 @@
if (can_load_classes_) {
klass = class_linker->FindClass(self, descriptor, class_loader);
} else {
- klass = class_linker->LookupClass(descriptor, loader);
+ klass = class_linker->LookupClass(descriptor, ComputeModifiedUtf8Hash(descriptor), loader);
if (klass != nullptr && !klass->IsLoaded()) {
// We found the class but without it being loaded its not safe for use.
klass = nullptr;
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index d86735d..e52adfc 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -22,10 +22,10 @@
LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
-LOCAL_SRC_FILES := sigchain.cc
+LOCAL_SRC_FILES := sigchain_dummy.cc
LOCAL_CLANG = $(ART_TARGET_CLANG)
LOCAL_MODULE:= libsigchain
-LOCAL_SHARED_LIBRARIES := liblog libdl
+LOCAL_SHARED_LIBRARIES := liblog
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
include $(BUILD_SHARED_LIBRARY)
@@ -37,7 +37,7 @@
LOCAL_IS_HOST_MODULE := true
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
LOCAL_CLANG = $(ART_HOST_CLANG)
-LOCAL_SRC_FILES := sigchain.cc
+LOCAL_SRC_FILES := sigchain_dummy.cc
LOCAL_MODULE:= libsigchain
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_LDLIBS = -ldl
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 4991891..4f16c7f 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -99,21 +99,20 @@
}
}
-
// Claim a signal chain for a particular signal.
-void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
CheckSignalValid(signal);
user_sigactions[signal].Claim(*oldaction);
}
-void UnclaimSignalChain(int signal) {
+extern "C" void UnclaimSignalChain(int signal) {
CheckSignalValid(signal);
user_sigactions[signal].Unclaim(signal);
}
// Invoke the user's signal handler.
-void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
// Check the arguments.
CheckSignalValid(sig);
@@ -140,7 +139,7 @@
}
}
-void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
CheckSignalValid(signal);
// Read the current action without looking at the chain, it should be the expected action.
SigActionFnPtr linked_sigaction = reinterpret_cast<SigActionFnPtr>(linked_sigaction_sym);
@@ -155,10 +154,9 @@
}
}
-extern "C" {
// These functions are C linkage since they replace the functions in libc.
-int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
+extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
// If this signal has been claimed as a signal chain, record the user's
// action but don't pass it on to the kernel.
// Note that we check that the signal number is in range here. An out of range signal
@@ -192,7 +190,7 @@
return linked_sigaction(signal, new_action, old_action);
}
-int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
const sigset_t* new_set_ptr = bionic_new_set;
sigset_t tmpset;
if (bionic_new_set != NULL) {
@@ -224,9 +222,8 @@
SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
}
-} // extern "C"
-void InitializeSignalChain() {
+extern "C" void InitializeSignalChain() {
// Warning.
// Don't call this from within a signal context as it makes calls to
// dlsym. Calling into the dynamic linker will result in locks being
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index be69ca4..59a1f1e 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -19,18 +19,14 @@
#include <signal.h>
-namespace art {
+extern "C" void InitializeSignalChain();
-void InitializeSignalChain();
+extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction);
-void ClaimSignalChain(int signal, struct sigaction* oldaction);
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
-void EnsureFrontOfChain(int signal, struct sigaction* expected_action);
+extern "C" void UnclaimSignalChain(int signal);
-void UnclaimSignalChain(int signal);
-
-void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
-
-} // namespace art
+extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
#endif // ART_SIGCHAINLIB_SIGCHAIN_H_
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
new file mode 100644
index 0000000..b0a6ebb
--- /dev/null
+++ b/sigchainlib/sigchain_dummy.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifdef HAVE_ANDROID_OS
+#include <android/log.h>
+#else
+#include <stdarg.h>
+#include <iostream>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "sigchain.h"
+
+static void log(const char* format, ...) {
+ char buf[256];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+#ifdef HAVE_ANDROID_OS
+ __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
+#else
+ std::cout << buf << "\n";
+#endif
+ va_end(ap);
+}
+
+extern "C" void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+ log("ClaimSignalChain is not exported by the main executable.");
+ abort();
+}
+
+extern "C" void EnsureFrontOfChain(int signal, struct sigaction* expected_action) {
+ log("EnsureFrontOfChain is not exported by the main executable.");
+ abort();
+}
+
+extern "C" void UnclaimSignalChain(int signal) {
+ log("UnclaimSignalChain is not exported by the main executable.");
+ abort();
+}
+
+extern "C" void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+ log("InvokeUserSignalHandler is not exported by the main executable.");
+ abort();
+}
+
+extern "C" void InitializeSignalChain() {
+ log("InitializeSignalChain is not exported by the main executable.");
+ abort();
+}
diff --git a/sigchainlib/version-script.txt b/sigchainlib/version-script.txt
new file mode 100644
index 0000000..5c72a3e
--- /dev/null
+++ b/sigchainlib/version-script.txt
@@ -0,0 +1,13 @@
+{
+global:
+ ClaimSignalChain;
+ EnsureFrontOfChain;
+ UnclaimSignalChain;
+ InvokeUserSignalHandler;
+ InitializeSignalChain;
+ sigaction;
+ signal;
+ sigprocmask;
+local:
+ *;
+};
diff --git a/test/040-miranda/src/Main.java b/test/040-miranda/src/Main.java
index ff5eba0..65f4fb4 100644
--- a/test/040-miranda/src/Main.java
+++ b/test/040-miranda/src/Main.java
@@ -42,8 +42,8 @@
System.out.println("Test getting miranda method via reflection:");
try {
- Class mirandaClass = Class.forName("MirandaAbstract");
- Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface", (Class[]) null);
+ Class<?> mirandaClass = Class.forName("MirandaAbstract");
+ Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface");
System.out.println(" did not expect to find miranda method");
} catch (NoSuchMethodException nsme) {
System.out.println(" caught expected NoSuchMethodException");
diff --git a/test/040-miranda/src/MirandaAbstract.java b/test/040-miranda/src/MirandaAbstract.java
index 309ecca..c8cfa34 100644
--- a/test/040-miranda/src/MirandaAbstract.java
+++ b/test/040-miranda/src/MirandaAbstract.java
@@ -21,6 +21,8 @@
{
protected MirandaAbstract() { }
+ // These will be miranda methods, as the interfaces define them, but they are not
+ // implemented in this abstract class:
//public abstract boolean inInterface();
//public abstract int inInterface2();
diff --git a/test/040-miranda/src/MirandaClass.java b/test/040-miranda/src/MirandaClass.java
index 0d942f0..4160992 100644
--- a/test/040-miranda/src/MirandaClass.java
+++ b/test/040-miranda/src/MirandaClass.java
@@ -22,17 +22,14 @@
public MirandaClass() {}
public boolean inInterface() {
- //System.out.println(" MirandaClass inInterface");
return true;
}
public int inInterface2() {
- //System.out.println(" MirandaClass inInterface2");
return 27;
}
public boolean inAbstract() {
- //System.out.println(" MirandaClass inAbstract");
return false;
}
}
diff --git a/test/040-miranda/src/MirandaClass2.java b/test/040-miranda/src/MirandaClass2.java
index e9bdf2b..143eb37 100644
--- a/test/040-miranda/src/MirandaClass2.java
+++ b/test/040-miranda/src/MirandaClass2.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2006 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 MirandaClass2 extends MirandaAbstract {
public boolean inInterface() {
return true;
diff --git a/test/051-thread/expected.txt b/test/051-thread/expected.txt
index 943d1df..54e34af 100644
--- a/test/051-thread/expected.txt
+++ b/test/051-thread/expected.txt
@@ -9,4 +9,6 @@
testSetName starting
testSetName running
testSetName finished
+testThreadPriorities starting
+testThreadPriorities finished
thread test done
diff --git a/test/051-thread/src/Main.java b/test/051-thread/src/Main.java
index 390685d..b81273e 100644
--- a/test/051-thread/src/Main.java
+++ b/test/051-thread/src/Main.java
@@ -20,12 +20,17 @@
* Test some basic thread stuff.
*/
public class Main {
+ static {
+ System.loadLibrary("arttest");
+ }
+
public static void main(String[] args) throws Exception {
System.out.println("thread test starting");
testThreadCapacity();
testThreadDaemons();
testSleepZero();
testSetName();
+ testThreadPriorities();
System.out.println("thread test done");
}
@@ -133,4 +138,53 @@
}
System.out.print("testSetName finished\n");
}
+
+ private static void testThreadPriorities() throws Exception {
+ System.out.print("testThreadPriorities starting\n");
+
+ PriorityStoringThread t1 = new PriorityStoringThread(false);
+ t1.setPriority(Thread.MAX_PRIORITY);
+ t1.start();
+ t1.join();
+ if (supportsThreadPriorities() && (t1.getNativePriority() != Thread.MAX_PRIORITY)) {
+ System.out.print("thread priority for t1 was " + t1.getNativePriority() +
+ " [expected Thread.MAX_PRIORITY]\n");
+ }
+
+ PriorityStoringThread t2 = new PriorityStoringThread(true);
+ t2.start();
+ t2.join();
+ if (supportsThreadPriorities() && (t2.getNativePriority() != Thread.MAX_PRIORITY)) {
+ System.out.print("thread priority for t2 was " + t2.getNativePriority() +
+ " [expected Thread.MAX_PRIORITY]\n");
+ }
+
+ System.out.print("testThreadPriorities finished\n");
+ }
+
+ private static native int getNativePriority();
+ private static native boolean supportsThreadPriorities();
+
+ static class PriorityStoringThread extends Thread {
+ private final boolean setPriority;
+ private volatile int nativePriority;
+
+ public PriorityStoringThread(boolean setPriority) {
+ this.setPriority = setPriority;
+ this.nativePriority = -1;
+ }
+
+ @Override
+ public void run() {
+ if (setPriority) {
+ setPriority(Thread.MAX_PRIORITY);
+ }
+
+ nativePriority = Main.getNativePriority();
+ }
+
+ public int getNativePriority() {
+ return nativePriority;
+ }
+ }
}
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
new file mode 100644
index 0000000..2f5fffc
--- /dev/null
+++ b/test/051-thread/thread_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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 "thread-inl.h"
+
+namespace art {
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env, jclass) {
+ return ThreadForEnv(env)->GetNativePriority();
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(JNIEnv* env, jclass) {
+#if defined(HAVE_ANDROID_OS)
+ return JNI_TRUE;
+#else
+ return JNI_FALSE;
+#endif
+}
+
+} // namespace art
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index a5eedc6..16a71e4 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -1,3 +1,4 @@
+Code cache exists: './code_cache'.
Native bridge initialized.
Checking for getEnvValues.
Ready for native bridge tests.
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 442f99c..23145e3 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -23,6 +23,7 @@
#include "jni.h"
#include "stdio.h"
#include "unistd.h"
+#include "sys/stat.h"
#include "nativebridge/native_bridge.h"
@@ -208,7 +209,13 @@
// NativeBridgeCallbacks implementations
extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* art_cbs,
- const char* private_dir, const char* isa) {
+ const char* app_code_cache_dir, const char* isa) {
+ struct stat st;
+ if ((app_code_cache_dir != nullptr)
+ && (stat(app_code_cache_dir, &st) == 0)
+ && S_ISDIR(st.st_mode)) {
+ printf("Code cache exists: '%s'.\n", app_code_cache_dir);
+ }
if (art_cbs != nullptr) {
gNativeBridgeArtCallbacks = art_cbs;
printf("Native bridge initialized.\n");
diff --git a/test/117-nopatchoat/expected.txt b/test/117-nopatchoat/expected.txt
index a1293ae..5cc02d1 100644
--- a/test/117-nopatchoat/expected.txt
+++ b/test/117-nopatchoat/expected.txt
@@ -1,9 +1,9 @@
Run without dex2oat/patchoat
-dex2oat & patchoat are disabled, has oat is true, has executable oat is false.
+dex2oat & patchoat are disabled, has oat is true, has executable oat is expected.
This is a function call
Run with dexoat/patchoat
-dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+dex2oat & patchoat are enabled, has oat is true, has executable oat is expected.
This is a function call
Run default
-dex2oat & patchoat are enabled, has oat is true, has executable oat is true.
+dex2oat & patchoat are enabled, has oat is true, has executable oat is expected.
This is a function call
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index 5994653..da276f2 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -24,18 +24,41 @@
class NoPatchoatTest {
public:
- static bool hasExecutableOat(jclass cls) {
+ static const OatFile::OatDexFile* getOatDexFile(jclass cls) {
ScopedObjectAccess soa(Thread::Current());
mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
const DexFile& dex_file = klass->GetDexFile();
+
const OatFile::OatDexFile* oat_dex_file =
Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+
+ return oat_dex_file;
+ }
+
+ static bool hasExecutableOat(jclass cls) {
+ const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls);
+
return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable();
}
+
+ static bool isPic(jclass cls) {
+ const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls);
+
+ if (oat_dex_file == nullptr) {
+ return false;
+ }
+
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ return oat_file->IsPic();
+ }
};
extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasExecutableOat(JNIEnv*, jclass cls) {
return NoPatchoatTest::hasExecutableOat(cls);
}
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isPic(JNIEnv*, jclass cls) {
+ return NoPatchoatTest::isPic(cls);
+}
+
} // namespace art
diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java
index f3f91ce..7bc9dbb 100644
--- a/test/117-nopatchoat/src/Main.java
+++ b/test/117-nopatchoat/src/Main.java
@@ -16,9 +16,14 @@
public class Main {
public static void main(String[] args) {
+ boolean executable_correct = (isPic() ?
+ hasExecutableOat() == true :
+ hasExecutableOat() == isDex2OatEnabled());
+
System.out.println(
"dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") +
- ", has oat is " + hasOat() + ", has executable oat is " + hasExecutableOat() + ".");
+ ", has oat is " + hasOat() + ", has executable oat is " + (
+ executable_correct ? "expected" : "not expected") + ".");
if (!hasOat() && isDex2OatEnabled()) {
throw new Error("Application with dex2oat enabled runs without an oat file");
@@ -42,6 +47,8 @@
private native static boolean isDex2OatEnabled();
+ private native static boolean isPic();
+
private native static boolean hasOat();
private native static boolean hasExecutableOat();
diff --git a/test/122-secondarydex/build b/test/122-secondarydex/build
new file mode 100755
index 0000000..712774f
--- /dev/null
+++ b/test/122-secondarydex/build
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+mv classes/Super.class classes-ex
+
+if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+fi
diff --git a/test/122-secondarydex/expected.txt b/test/122-secondarydex/expected.txt
new file mode 100644
index 0000000..29a1411
--- /dev/null
+++ b/test/122-secondarydex/expected.txt
@@ -0,0 +1,3 @@
+testSlowPathDirectInvoke
+Test
+Got null pointer exception
diff --git a/test/122-secondarydex/info.txt b/test/122-secondarydex/info.txt
new file mode 100644
index 0000000..0479d1a
--- /dev/null
+++ b/test/122-secondarydex/info.txt
@@ -0,0 +1,3 @@
+Test features with a secondary dex file.
+
+- Regression test to ensure slow path of direct invoke does null check.
diff --git a/test/122-secondarydex/run b/test/122-secondarydex/run
new file mode 100755
index 0000000..d8c3c79
--- /dev/null
+++ b/test/122-secondarydex/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/122-secondarydex/src/Main.java b/test/122-secondarydex/src/Main.java
new file mode 100644
index 0000000..c921c5b
--- /dev/null
+++ b/test/122-secondarydex/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Secondary dex file test.
+ */
+public class Main {
+ public static void main(String[] args) {
+ testSlowPathDirectInvoke();
+ }
+
+ public static void testSlowPathDirectInvoke() {
+ System.out.println("testSlowPathDirectInvoke");
+ try {
+ Test t1 = new Test();
+ Test t2 = new Test();
+ Test t3 = null;
+ t1.test(t2);
+ t1.test(t3);
+ } catch (NullPointerException npe) {
+ System.out.println("Got null pointer exception");
+ } catch (Exception e) {
+ System.out.println("Got unexpected exception " + e);
+ }
+ }
+}
diff --git a/test/122-secondarydex/src/Super.java b/test/122-secondarydex/src/Super.java
new file mode 100644
index 0000000..7608d4a
--- /dev/null
+++ b/test/122-secondarydex/src/Super.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2014 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 Super {
+ private void print() {
+ System.out.println("Super");
+ }
+}
diff --git a/test/122-secondarydex/src/Test.java b/test/122-secondarydex/src/Test.java
new file mode 100644
index 0000000..82cb901
--- /dev/null
+++ b/test/122-secondarydex/src/Test.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 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 Test extends Super {
+ public void test(Test t) {
+ t.print();
+ }
+
+ private void print() {
+ System.out.println("Test");
+ }
+}
diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build
new file mode 100644
index 0000000..4c30f3f
--- /dev/null
+++ b/test/126-miranda-multidex/build
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+
+# All except Main
+${JAVAC} -d classes `find src -name '*.java'`
+rm classes/MirandaInterface.class
+${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+
+# Only Main
+${JAVAC} -d classes `find src -name '*.java'`
+rm classes/Main.class classes/MirandaAbstract.class classes/MirandaClass*.class classes/MirandaInterface2*.class
+${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes
+
+zip $TEST_NAME.jar classes.dex classes2.dex
diff --git a/test/126-miranda-multidex/expected.txt b/test/126-miranda-multidex/expected.txt
new file mode 100644
index 0000000..dbe3717
--- /dev/null
+++ b/test/126-miranda-multidex/expected.txt
@@ -0,0 +1,32 @@
+MirandaClass:
+ inInterface: true
+ inInterface2: 27
+ inAbstract: false
+MirandaAbstract / MirandaClass:
+ inInterface: true
+ inInterface2: 27
+ inAbstract: false
+true 27
+MirandaAbstract / MirandaClass2:
+ inInterface: true
+ inInterface2: 28
+ inAbstract: true
+true 28
+Test getting miranda method via reflection:
+ caught expected NoSuchMethodException
+MirandaClass:
+ inInterface: true
+ inInterface2: 27
+ inAbstract: false
+MirandaAbstract / MirandaClass:
+ inInterface: true
+ inInterface2: 27
+ inAbstract: false
+true 27
+MirandaAbstract / MirandaClass2:
+ inInterface: true
+ inInterface2: 28
+ inAbstract: true
+true 28
+Test getting miranda method via reflection:
+ caught expected NoSuchMethodException
diff --git a/test/126-miranda-multidex/info.txt b/test/126-miranda-multidex/info.txt
new file mode 100644
index 0000000..ac50e2e
--- /dev/null
+++ b/test/126-miranda-multidex/info.txt
@@ -0,0 +1,2 @@
+This test ensures that cross-dex-file Miranda methods are correctly resolved.
+See b/18193682 for details.
diff --git a/test/126-miranda-multidex/run b/test/126-miranda-multidex/run
new file mode 100755
index 0000000..23c9935
--- /dev/null
+++ b/test/126-miranda-multidex/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+${RUN} $@
+
+# The problem was first exposed in a no-verify setting, as that changes the resolution path
+# taken. Make sure we also test in that environment.
+${RUN} --no-verify ${@}
diff --git a/test/126-miranda-multidex/src/Main.java b/test/126-miranda-multidex/src/Main.java
new file mode 100644
index 0000000..8624378
--- /dev/null
+++ b/test/126-miranda-multidex/src/Main.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Miranda testing.
+ */
+public class Main {
+ public static void main(String[] args) {
+ MirandaClass mir = new MirandaClass();
+ System.out.println("MirandaClass:");
+ System.out.println(" inInterface: " + mir.inInterface());
+ System.out.println(" inInterface2: " + mir.inInterface2());
+ System.out.println(" inAbstract: " + mir.inAbstract());
+
+ /* try again through abstract class; results should be identical */
+ MirandaAbstract mira = mir;
+ System.out.println("MirandaAbstract / MirandaClass:");
+ System.out.println(" inInterface: " + mira.inInterface());
+ System.out.println(" inInterface2: " + mira.inInterface2());
+ System.out.println(" inAbstract: " + mira.inAbstract());
+ mira.callMiranda();
+
+ MirandaAbstract mira2 = new MirandaClass2();
+ System.out.println("MirandaAbstract / MirandaClass2:");
+ System.out.println(" inInterface: " + mira2.inInterface());
+ System.out.println(" inInterface2: " + mira2.inInterface2());
+ System.out.println(" inAbstract: " + mira2.inAbstract());
+ mira2.callMiranda();
+
+ System.out.println("Test getting miranda method via reflection:");
+ try {
+ Class<?> mirandaClass = Class.forName("MirandaAbstract");
+ Method mirandaMethod = mirandaClass.getDeclaredMethod("inInterface");
+ System.out.println(" did not expect to find miranda method");
+ } catch (NoSuchMethodException nsme) {
+ System.out.println(" caught expected NoSuchMethodException");
+ } catch (Exception e) {
+ System.out.println(" caught unexpected exception " + e);
+ }
+ }
+}
diff --git a/test/126-miranda-multidex/src/MirandaAbstract.java b/test/126-miranda-multidex/src/MirandaAbstract.java
new file mode 100644
index 0000000..c09a61f
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaAbstract.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public abstract class MirandaAbstract implements MirandaInterface, MirandaInterface2
+{
+ protected MirandaAbstract() { }
+
+ // These will be miranda methods, as the interfaces define them, but they are not
+ // implemented in this abstract class:
+ //public abstract boolean inInterface();
+ //public abstract int inInterface2();
+
+ public boolean inAbstract() {
+ return true;
+ }
+
+ public void callMiranda() {
+ System.out.println(inInterface() + " " + inInterface2());
+ }
+}
diff --git a/test/126-miranda-multidex/src/MirandaClass.java b/test/126-miranda-multidex/src/MirandaClass.java
new file mode 100644
index 0000000..7bb37e7
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaClass.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public class MirandaClass extends MirandaAbstract {
+
+ public MirandaClass() {}
+
+ public boolean inInterface() {
+ return true;
+ }
+
+ public int inInterface2() {
+ return 27;
+ }
+
+ public boolean inAbstract() {
+ return false;
+ }
+
+ // Better not hit any of these...
+ public void inInterfaceDummy1() {
+ System.out.println("inInterfaceDummy1");
+ }
+ public void inInterfaceDummy2() {
+ System.out.println("inInterfaceDummy2");
+ }
+ public void inInterfaceDummy3() {
+ System.out.println("inInterfaceDummy3");
+ }
+ public void inInterfaceDummy4() {
+ System.out.println("inInterfaceDummy4");
+ }
+ public void inInterfaceDummy5() {
+ System.out.println("inInterfaceDummy5");
+ }
+}
diff --git a/test/126-miranda-multidex/src/MirandaClass2.java b/test/126-miranda-multidex/src/MirandaClass2.java
new file mode 100644
index 0000000..797ead2
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaClass2.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 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 MirandaClass2 extends MirandaAbstract {
+ public boolean inInterface() {
+ return true;
+ }
+
+ public int inInterface2() {
+ return 28;
+ }
+
+ // Better not hit any of these...
+ public void inInterfaceDummy1() {
+ System.out.println("inInterfaceDummy1");
+ }
+ public void inInterfaceDummy2() {
+ System.out.println("inInterfaceDummy2");
+ }
+ public void inInterfaceDummy3() {
+ System.out.println("inInterfaceDummy3");
+ }
+ public void inInterfaceDummy4() {
+ System.out.println("inInterfaceDummy4");
+ }
+ public void inInterfaceDummy5() {
+ System.out.println("inInterfaceDummy5");
+ }
+}
diff --git a/test/126-miranda-multidex/src/MirandaInterface.java b/test/126-miranda-multidex/src/MirandaInterface.java
new file mode 100644
index 0000000..df12fcc
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface {
+
+ public boolean inInterface();
+
+ // A couple of dummy methods to fill the method table.
+ public void inInterfaceDummy1();
+ public void inInterfaceDummy2();
+ public void inInterfaceDummy3();
+ public void inInterfaceDummy4();
+ public void inInterfaceDummy5();
+
+}
diff --git a/test/126-miranda-multidex/src/MirandaInterface2.java b/test/126-miranda-multidex/src/MirandaInterface2.java
new file mode 100644
index 0000000..7c93fd0
--- /dev/null
+++ b/test/126-miranda-multidex/src/MirandaInterface2.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface2 {
+
+ public boolean inInterface();
+
+ public int inInterface2();
+
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 2d139a6..2e12335 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -24,6 +24,7 @@
004-ReferenceMap/stack_walk_refmap_jni.cc \
004-StackWalk/stack_walk_jni.cc \
004-UnsafeTest/unsafe_test.cc \
+ 051-thread/thread_test.cc \
116-nodex2oat/nodex2oat.cc \
117-nopatchoat/nopatchoat.cc \
118-noimage-dex2oat/noimage-dex2oat.cc
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 7253a2b..37c579d 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -23,8 +23,10 @@
FLAGS=""
COMPILER_FLAGS=""
BUILD_BOOT_OPT=""
+SECONDARY_DEX=""
exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
main="Main"
+DEX_VERIFY=""
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -91,6 +93,9 @@
elif [ "x$1" = "x--relocate" ]; then
RELOCATE="y"
shift
+ elif [ "x$1" = "x--secondary" ]; then
+ SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+ shift
elif [ "x$1" = "x-Xcompiler-option" ]; then
shift
option="$1"
@@ -154,7 +159,12 @@
if [ "$INTERPRETER" = "y" ]; then
INT_OPTS="-Xint"
- COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+ if [ "$VERIFY" = "y" ] ; then
+ COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+ else
+ COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=verify-none"
+ DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+ fi
fi
if [ "$RELOCATE" = "y" ]; then
@@ -180,7 +190,7 @@
fi
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar $main"
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $DEX_VERIFY $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $main"
if [ "$DEV_MODE" = "y" ]; then
if [ "$PREBUILD" = "y" ]; then
echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
@@ -192,4 +202,4 @@
fi
cd $ANDROID_BUILD_TOP
-$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@"
+$mkdir_cmd && $prebuild_cmd && $cmdline "$@"
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
index ad23edf..7353544 100755
--- a/test/etc/push-and-run-prebuilt-test-jar
+++ b/test/etc/push-and-run-prebuilt-test-jar
@@ -26,6 +26,8 @@
TARGET_SUFFIX="32"
GDB_TARGET_SUFFIX=""
COMPILE_FLAGS=""
+SECONDARY_DEX=""
+DEX_VERIFY=""
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -104,6 +106,9 @@
GDB_TARGET_SUFFIX="64"
ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
shift
+ elif [ "x$1" = "x--secondary" ]; then
+ SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -126,7 +131,6 @@
fi
if [ "$VERIFY" = "y" ]; then
- DEX_VERIFY=""
msg "Performing verification"
else
DEX_VERIFY="-Xverify:none"
@@ -173,7 +177,12 @@
if [ "$INTERPRETER" = "y" ]; then
INT_OPTS="-Xint"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+ if [ "$VERIFY" = "y" ] ; then
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+ else
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
+ DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+ fi
fi
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
@@ -194,7 +203,7 @@
cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
$INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
- $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX Main $@"
cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
echo "$cmdline" > $cmdfile
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 06075c2..a6a4e78 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -22,6 +22,8 @@
FLAGS=""
TARGET_SUFFIX="32"
GDB_TARGET_SUFFIX=""
+SECONDARY_DEX=""
+DEX_VERIFY=""
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -97,6 +99,9 @@
TARGET_SUFFIX="64"
GDB_TARGET_SUFFIX="64"
shift
+ elif [ "x$1" = "x--secondary" ]; then
+ SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -119,7 +124,6 @@
fi
if [ "$VERIFY" = "y" ]; then
- DEX_VERIFY=""
msg "Performing verification"
else
DEX_VERIFY="-Xverify:none"
@@ -160,6 +164,12 @@
if [ "$INTERPRETER" = "y" ]; then
INT_OPTS="-Xint"
+ if [ "$VERIFY" = "y" ] ; then
+ COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+ else
+ COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=verify-none"
+ DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
+ fi
fi
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
@@ -172,7 +182,7 @@
fi
cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
- $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $DEX_VERIFY $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX Main"
if [ "$DEV_MODE" = "y" ]; then
echo $cmdline "$@"
fi