summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk13
-rw-r--r--compiler/driver/compiler_driver.cc20
-rw-r--r--compiler/image_writer.cc10
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.cc10
-rw-r--r--compiler/optimizing/code_generator.cc2
-rw-r--r--compiler/optimizing/code_generator_arm.cc132
-rw-r--r--compiler/optimizing/code_generator_arm64.cc506
-rw-r--r--compiler/optimizing/code_generator_arm64.h13
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc111
-rw-r--r--compiler/optimizing/code_generator_mips.cc58
-rw-r--r--compiler/optimizing/code_generator_mips64.cc62
-rw-r--r--compiler/optimizing/code_generator_x86.cc30
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc39
-rw-r--r--compiler/optimizing/inliner.cc28
-rw-r--r--compiler/optimizing/instruction_builder.cc20
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc212
-rw-r--r--compiler/optimizing/intrinsics_mips.cc55
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc49
-rw-r--r--compiler/optimizing/optimizing_compiler.cc2
-rw-r--r--compiler/optimizing/sharpening.cc6
-rw-r--r--compiler/optimizing/stack_map_stream.cc225
-rw-r--r--compiler/optimizing/stack_map_stream.h52
-rw-r--r--compiler/optimizing/stack_map_test.cc94
-rw-r--r--compiler/utils/x86/assembler_x86.cc142
-rw-r--r--compiler/utils/x86/assembler_x86.h16
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc74
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc119
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h18
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc50
-rw-r--r--compiler/verifier_deps_test.cc2
-rw-r--r--dex2oat/dex2oat.cc58
-rw-r--r--dex2oat/dex2oat_test.cc41
-rw-r--r--dexdump/dexdump.cc244
-rw-r--r--disassembler/disassembler_x86.cc16
-rw-r--r--oatdump/oatdump.cc4
-rw-r--r--patchoat/patchoat.cc10
-rw-r--r--profman/profile_assistant_test.cc99
-rw-r--r--profman/profman.cc317
-rw-r--r--runtime/Android.bp3
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S49
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S80
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S263
-rw-r--r--runtime/arch/mips64/quick_entrypoints_mips64.S259
-rw-r--r--runtime/arch/stub_test.cc4
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S54
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S45
-rw-r--r--runtime/art_method.cc53
-rw-r--r--runtime/art_method.h1
-rw-r--r--runtime/base/mutex.cc49
-rw-r--r--runtime/base/mutex.h21
-rw-r--r--runtime/cha.cc18
-rw-r--r--runtime/class_linker.cc315
-rw-r--r--runtime/class_linker.h10
-rw-r--r--runtime/class_linker_test.cc35
-rw-r--r--runtime/class_table_test.cc2
-rw-r--r--runtime/common_throws.cc16
-rw-r--r--runtime/common_throws.h10
-rw-r--r--runtime/debugger.cc18
-rw-r--r--runtime/dex2oat_environment_test.h4
-rw-r--r--runtime/dex_file.cc118
-rw-r--r--runtime/dex_file.h160
-rw-r--r--runtime/dex_file_annotations.cc28
-rw-r--r--runtime/dex_file_test.cc26
-rw-r--r--runtime/dex_file_verifier.cc258
-rw-r--r--runtime/dex_file_verifier.h10
-rw-r--r--runtime/dex_file_verifier_test.cc205
-rw-r--r--runtime/dex_instruction.cc32
-rw-r--r--runtime/dex_instruction.h68
-rw-r--r--runtime/dex_instruction_list.h4
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc2
-rw-r--r--runtime/entrypoints/quick/quick_dexcache_entrypoints.cc17
-rw-r--r--runtime/entrypoints/quick/quick_field_entrypoints.cc2
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc21
-rw-r--r--runtime/gc/allocation_record.cc2
-rw-r--r--runtime/gc/collector/concurrent_copying.cc58
-rw-r--r--runtime/gc/reference_processor.cc4
-rw-r--r--runtime/gc/reference_queue_test.cc14
-rw-r--r--runtime/gc/space/image_space.cc123
-rw-r--r--runtime/gc/space/image_space.h11
-rw-r--r--runtime/gc/space/image_space_test.cc111
-rw-r--r--runtime/gc/space/space_create_test.cc16
-rw-r--r--runtime/gc/space/space_test.h4
-rw-r--r--runtime/gc/system_weak.h2
-rw-r--r--runtime/handle.h8
-rw-r--r--runtime/imtable_test.cc4
-rw-r--r--runtime/indirect_reference_table_test.cc20
-rw-r--r--runtime/instrumentation.cc9
-rw-r--r--runtime/intern_table_test.cc14
-rw-r--r--runtime/interpreter/interpreter_common.cc336
-rw-r--r--runtime/interpreter/interpreter_common.h12
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc22
-rw-r--r--runtime/interpreter/unstarted_runtime.cc22
-rw-r--r--runtime/interpreter/unstarted_runtime_test.cc4
-rw-r--r--runtime/java_vm_ext.cc6
-rw-r--r--runtime/jdwp/jdwp.h5
-rw-r--r--runtime/jdwp/jdwp_event.cc37
-rw-r--r--runtime/jdwp/object_registry.cc4
-rw-r--r--runtime/jit/jit_code_cache.cc15
-rw-r--r--runtime/jit/profile_compilation_info.cc32
-rw-r--r--runtime/jit/profile_compilation_info.h5
-rw-r--r--runtime/jni_internal.cc2
-rw-r--r--runtime/jobject_comparator.cc4
-rw-r--r--runtime/mem_map.cc2
-rw-r--r--runtime/method_handles.cc138
-rw-r--r--runtime/method_handles.h6
-rw-r--r--runtime/mirror/array.cc6
-rw-r--r--runtime/mirror/call_site.cc52
-rw-r--r--runtime/mirror/call_site.h64
-rw-r--r--runtime/mirror/class.cc14
-rw-r--r--runtime/mirror/class_ext.cc4
-rw-r--r--runtime/mirror/dex_cache-inl.h46
-rw-r--r--runtime/mirror/dex_cache.cc14
-rw-r--r--runtime/mirror/dex_cache.h51
-rw-r--r--runtime/mirror/dex_cache_test.cc6
-rw-r--r--runtime/mirror/emulated_stack_frame.cc4
-rw-r--r--runtime/mirror/field-inl.h4
-rw-r--r--runtime/mirror/method_handle_impl.cc24
-rw-r--r--runtime/mirror/method_handle_impl.h23
-rw-r--r--runtime/mirror/method_handles_lookup.cc58
-rw-r--r--runtime/mirror/method_handles_lookup.h70
-rw-r--r--runtime/mirror/method_type.cc16
-rw-r--r--runtime/mirror/method_type.h4
-rw-r--r--runtime/mirror/method_type_test.cc2
-rw-r--r--runtime/mirror/object_test.cc10
-rw-r--r--runtime/mirror/string-inl.h9
-rw-r--r--runtime/mirror/string.h4
-rw-r--r--runtime/monitor.cc2
-rw-r--r--runtime/monitor_test.cc4
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc4
-rw-r--r--runtime/native/java_lang_Class.cc22
-rw-r--r--runtime/native/java_lang_invoke_MethodHandleImpl.cc2
-rw-r--r--runtime/native/java_lang_reflect_Executable.cc6
-rw-r--r--runtime/native/libcore_util_CharsetUtils.cc4
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/oat_file.cc115
-rw-r--r--runtime/oat_file.h9
-rw-r--r--runtime/oat_file_assistant.cc163
-rw-r--r--runtime/oat_file_assistant.h24
-rw-r--r--runtime/oat_file_assistant_test.cc51
-rw-r--r--runtime/oat_file_manager.cc8
-rw-r--r--runtime/oat_file_test.cc50
-rw-r--r--runtime/object_lock.cc4
-rw-r--r--runtime/openjdkjvmti/ti_class.cc221
-rw-r--r--runtime/openjdkjvmti/ti_class_loader.cc8
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc160
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h9
-rw-r--r--runtime/openjdkjvmti/ti_threadgroup.cc6
-rw-r--r--runtime/proxy_test.cc20
-rw-r--r--runtime/reference_table_test.cc4
-rw-r--r--runtime/reflection.cc10
-rw-r--r--runtime/runtime.cc2
-rw-r--r--runtime/runtime_callbacks_test.cc2
-rw-r--r--runtime/stack.cc12
-rw-r--r--runtime/stack_map.h21
-rw-r--r--runtime/thread-inl.h29
-rw-r--r--runtime/thread.cc16
-rw-r--r--runtime/thread.h3
-rw-r--r--runtime/thread_list.cc74
-rw-r--r--runtime/thread_list.h4
-rw-r--r--runtime/transaction_test.cc62
-rw-r--r--runtime/utils.h24
-rw-r--r--runtime/utils/dex_cache_arrays_layout-inl.h21
-rw-r--r--runtime/utils/dex_cache_arrays_layout.h14
-rw-r--r--runtime/utils_test.cc19
-rw-r--r--runtime/verifier/method_verifier.cc150
-rw-r--r--runtime/verifier/method_verifier.h5
-rw-r--r--runtime/verifier/verifier_deps.cc8
-rw-r--r--runtime/zip_archive.cc113
-rw-r--r--runtime/zip_archive.h18
-rw-r--r--test/021-string2/src/Main.java263
-rwxr-xr-xtest/044-proxy/build17
-rw-r--r--test/044-proxy/expected.txt2
-rwxr-xr-xtest/071-dexfile-map-clean/build21
-rw-r--r--test/071-dexfile-map-clean/expected.txt3
-rw-r--r--test/071-dexfile-map-clean/info.txt11
-rwxr-xr-xtest/071-dexfile-map-clean/run31
-rw-r--r--test/071-dexfile-map-clean/src-ex/Another.java21
-rw-r--r--test/071-dexfile-map-clean/src/Main.java147
-rw-r--r--test/121-modifiers/build40
-rw-r--r--test/121-modifiers/info.txt17
-rw-r--r--test/121-modifiers/smali/A$B.smali42
-rw-r--r--test/121-modifiers/smali/A$C.smali30
-rw-r--r--test/121-modifiers/smali/A.smali41
-rw-r--r--test/121-modifiers/smali/Inf.smali23
-rw-r--r--test/121-modifiers/smali/NonInf.smali177
-rw-r--r--test/121-modifiers/src-java/A.java23
-rw-r--r--test/121-modifiers/src-java/Asm.java (renamed from test/121-modifiers/src/Asm.java)0
-rw-r--r--test/121-modifiers/src-java/Inf.java (renamed from test/121-modifiers/src/Inf.java)0
-rw-r--r--test/121-modifiers/src-java/NonInf.java (renamed from test/121-modifiers/src/NonInf.java)0
-rw-r--r--test/121-modifiers/src2/Main.java (renamed from test/121-modifiers/src/Main.java)8
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java6
-rw-r--r--test/616-cha-native/expected.txt1
-rw-r--r--test/616-cha-native/info.txt2
-rw-r--r--test/616-cha-native/src/Main.java33
-rw-r--r--test/616-cha/src/Main.java2
-rw-r--r--test/626-const-class-linking/clear_dex_cache_types.cc2
-rwxr-xr-xtest/636-arm64-veneer-pool/build22
-rw-r--r--test/636-arm64-veneer-pool/expected.txt1
-rw-r--r--test/636-arm64-veneer-pool/info.txt1
-rw-r--r--test/636-arm64-veneer-pool/src/Main.java4223
-rw-r--r--test/911-get-stack-trace/expected.txt146
-rw-r--r--test/911-get-stack-trace/src/AllTraces.java6
-rw-r--r--test/911-get-stack-trace/src/Frames.java4
-rw-r--r--test/911-get-stack-trace/src/OtherThread.java4
-rw-r--r--test/911-get-stack-trace/src/ThreadListTraces.java2
-rw-r--r--test/912-classes/classes.cc140
-rw-r--r--test/912-classes/src/Main.java28
-rwxr-xr-xtest/945-obsolete-native/build17
-rw-r--r--test/945-obsolete-native/expected.txt9
-rw-r--r--test/945-obsolete-native/info.txt1
-rw-r--r--test/945-obsolete-native/obsolete_native.cc51
-rwxr-xr-xtest/945-obsolete-native/run17
-rw-r--r--test/945-obsolete-native/src/Main.java77
-rw-r--r--test/945-obsolete-native/src/Transform.java25
-rw-r--r--test/952-invoke-custom/build25
-rw-r--r--test/952-invoke-custom/expected.txt14
-rw-r--r--test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java227
-rw-r--r--test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java137
-rw-r--r--test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java140
-rwxr-xr-xtest/952-invoke-custom/generator/build-test.sh77
-rw-r--r--test/952-invoke-custom/info.txt7
-rw-r--r--test/952-invoke-custom/src/Main.java159
-rw-r--r--test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java145
-rw-r--r--test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java106
-rw-r--r--test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java108
-rw-r--r--test/Android.bp1
-rw-r--r--test/dexdump/invoke-custom.dexbin0 -> 3276 bytes
-rw-r--r--test/dexdump/invoke-custom.lst6
-rw-r--r--test/dexdump/invoke-custom.txt254
-rw-r--r--test/dexdump/invoke-custom.xml89
-rwxr-xr-xtest/etc/default-build42
-rwxr-xr-xtest/etc/run-test-jar19
-rw-r--r--test/knownfailures.json5
-rwxr-xr-xtest/run-test18
-rw-r--r--test/testrunner/env.py49
-rwxr-xr-xtest/testrunner/testrunner.py6
-rw-r--r--test/ti-agent/common_load.cc1
-rwxr-xr-xtools/run-libcore-tests.sh2
238 files changed, 13609 insertions, 2026 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index e5258087db..d376f291cd 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -92,8 +92,8 @@ ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Erroneou
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
-ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics
+ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
+ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB
ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
@@ -102,6 +102,7 @@ ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
+ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -146,6 +147,11 @@ ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
dexoptanalyzerd
+ART_GTEST_image_space_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_image_space_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+
ART_GTEST_dex2oat_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
ART_GTEST_dex2oat_test_TARGET_DEPS := \
@@ -627,6 +633,9 @@ ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
ART_GTEST_dexoptanalyzer_test_DEX_DEPS :=
ART_GTEST_dexoptanalyzer_test_HOST_DEPS :=
ART_GTEST_dexoptanalyzer_test_TARGET_DEPS :=
+ART_GTEST_image_space_test_DEX_DEPS :=
+ART_GTEST_image_space_test_HOST_DEPS :=
+ART_GTEST_image_space_test_TARGET_DEPS :=
ART_GTEST_dex2oat_test_DEX_DEPS :=
ART_GTEST_dex2oat_test_HOST_DEPS :=
ART_GTEST_dex2oat_test_TARGET_DEPS :=
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 7af850a263..f7bea32d4c 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -960,7 +960,7 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader,
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
- if (cls.Get() == nullptr) {
+ if (cls == nullptr) {
soa.Self()->ClearException();
} else if (&cls->GetDexFile() == dex_file) {
DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified())
@@ -1155,7 +1155,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
StackHandleScope<1> hs(self);
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str())));
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
VLOG(compiler) << "Failed to find class " << descriptor;
image_classes_->erase(it++);
self->ClearException();
@@ -1182,13 +1182,13 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file,
nullptr)));
Handle<mirror::Class> klass(hs2.NewHandle(
- (dex_cache.Get() != nullptr)
+ (dex_cache != nullptr)
? class_linker->ResolveType(*dex_file,
exception_type_idx,
dex_cache,
ScopedNullHandle<mirror::ClassLoader>())
: nullptr));
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx);
const char* descriptor = dex_file->GetTypeDescriptor(type_id);
LOG(FATAL) << "Failed to resolve class " << descriptor;
@@ -1877,7 +1877,7 @@ class ResolveTypeVisitor : public CompilationVisitor {
Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
dex_file,
class_loader.Get())));
- ObjPtr<mirror::Class> klass = (dex_cache.Get() != nullptr)
+ ObjPtr<mirror::Class> klass = (dex_cache != nullptr)
? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader)
: nullptr;
@@ -1978,7 +1978,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file,
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>(
class_linker->FindClass(self, descriptor, class_loader)));
- if (cls.Get() != nullptr) {
+ if (cls != nullptr) {
// Check that the class is resolved with the current dex file. We might get
// a boot image class, or a class in a different dex file for multidex, and
// we should not update the status in that case.
@@ -2126,7 +2126,7 @@ class VerifyClassVisitor : public CompilationVisitor {
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
verifier::MethodVerifier::FailureKind failure_kind;
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
@@ -2228,7 +2228,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor {
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
// Class might have failed resolution. Then don't set it to verified.
- if (klass.Get() != nullptr) {
+ if (klass != nullptr) {
// Only do this if the class is resolved. If even resolution fails, quickening will go very,
// very wrong.
if (klass->IsResolved() && !klass->IsErroneousResolved()) {
@@ -2290,7 +2290,7 @@ class InitializeClassVisitor : public CompilationVisitor {
Handle<mirror::Class> klass(
hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader)));
- if (klass.Get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) {
+ if (klass != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) {
// Only try to initialize classes that were successfully verified.
if (klass->IsVerified()) {
// Attempt to initialize the class but bail if we either need to initialize the super-class
@@ -2540,7 +2540,7 @@ class CompileClassVisitor : public CompilationVisitor {
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
Handle<mirror::DexCache> dex_cache;
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
soa.Self()->AssertPendingException();
soa.Self()->ClearException();
dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c72edb18a3..d2dd30d8e6 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1078,7 +1078,7 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
}
Handle<ObjectArray<Object>> dex_caches(
hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count)));
- CHECK(dex_caches.Get() != nullptr) << "Failed to allocate a dex cache array.";
+ CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
{
ReaderMutexLock mu(self, *Locks::dex_lock_);
size_t non_image_dex_caches = 0;
@@ -2254,6 +2254,14 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache),
ImageAddressVisitor(this));
}
+ GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+ if (orig_call_sites != nullptr) {
+ copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(),
+ NativeLocationInImage(orig_call_sites),
+ PointerSize::k64);
+ orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache),
+ ImageAddressVisitor(this));
+ }
// Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
// compiler pointers in here will make the output non-deterministic.
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 79e1785e91..9ddf200237 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -31,9 +31,7 @@ namespace linker {
namespace {
inline bool IsAdrpPatch(const LinkerPatch& patch) {
- LinkerPatch::Type type = patch.GetType();
- return
- (type == LinkerPatch::Type::kStringRelative || type == LinkerPatch::Type::kDexCacheArray) &&
+ return (patch.IsPcRelative() && patch.GetType() != LinkerPatch::Type::kCallRelative) &&
patch.LiteralOffset() == patch.PcInsnOffset();
}
@@ -214,11 +212,11 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
patch.GetType() == LinkerPatch::Type::kTypeRelative) << patch.GetType();
} else {
- // With the read barrier (non-Baker) enabled, it could be kDexCacheArray in the
- // HLoadString::LoadKind::kDexCachePcRelative case of VisitLoadString().
+ // With the read barrier (non-Baker) enabled, it could be kStringBssEntry or kTypeBssEntry.
DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
patch.GetType() == LinkerPatch::Type::kTypeRelative ||
- patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType();
+ patch.GetType() == LinkerPatch::Type::kStringBssEntry ||
+ patch.GetType() == LinkerPatch::Type::kTypeBssEntry) << patch.GetType();
}
shift = 0u; // No shift for ADD.
} else {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index d68aa51b1b..bac16cd5df 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1419,7 +1419,7 @@ void CodeGenerator::EmitJitRoots(uint8_t* code,
QuickEntrypointEnum CodeGenerator::GetArrayAllocationEntrypoint(Handle<mirror::Class> array_klass) {
ScopedObjectAccess soa(Thread::Current());
- if (array_klass.Get() == nullptr) {
+ if (array_klass == nullptr) {
// This can only happen for non-primitive arrays, as primitive arrays can always
// be resolved.
return kQuickAllocArrayResolved32;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 20cdae3619..759a951d6b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -367,22 +367,37 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM {
class LoadClassSlowPathARM : public SlowPathCodeARM {
public:
- LoadClassSlowPathARM(HLoadClass* cls,
- HInstruction* at,
- uint32_t dex_pc,
- bool do_clinit)
+ LoadClassSlowPathARM(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
: SlowPathCodeARM(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
+ constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
+ Register entry_address = kNoRegister;
+ if (is_load_class_bss_entry && call_saves_everything_except_r0) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
+ // the kSaveEverything call.
+ bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
+ entry_address = temp_is_r0 ? out.AsRegister<Register>() : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_r0) {
+ __ mov(entry_address, ShifterOperand(temp));
+ }
+ }
dex::TypeIndex type_index = cls_->GetTypeIndex();
__ LoadImmediate(calling_convention.GetRegisterAt(0), type_index.index_);
QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
@@ -394,30 +409,31 @@ class LoadClassSlowPathARM : public SlowPathCodeARM {
CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
}
+ // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
+ if (is_load_class_bss_entry) {
+ if (call_saves_everything_except_r0) {
+ // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ str(R0, Address(entry_address));
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ Register temp = IP;
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
+ __ BindTrackedLabel(&labels->movw_label);
+ __ movw(temp, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->movt_label);
+ __ movt(temp, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->add_pc_label);
+ __ add(temp, temp, ShifterOperand(PC));
+ __ str(R0, Address(temp));
+ }
+ }
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
}
RestoreLiveRegisters(codegen, locations);
- // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
- DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
- if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
- DCHECK(out.IsValid());
- // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
- // kSaveEverything and use a temporary for the .bss entry address in the fast path,
- // so that we can avoid another calculation here.
- CodeGeneratorARM::PcRelativePatchInfo* labels =
- arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- __ BindTrackedLabel(&labels->movw_label);
- __ movw(IP, /* placeholder */ 0u);
- __ BindTrackedLabel(&labels->movt_label);
- __ movt(IP, /* placeholder */ 0u);
- __ BindTrackedLabel(&labels->add_pc_label);
- __ add(IP, IP, ShifterOperand(PC));
- __ str(locations->Out().AsRegister<Register>(), Address(IP));
- }
__ b(GetExitLabel());
}
@@ -441,12 +457,13 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCodeARM(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ DCHECK(instruction_->IsLoadString());
+ DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
LocationSummary* locations = instruction_->GetLocations();
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
HLoadString* load = instruction_->AsLoadString();
const dex::StringIndex string_index = load->GetStringIndex();
Register out = locations->Out().AsRegister<Register>();
- Register temp = locations->GetTemp(0).AsRegister<Register>();
constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -455,12 +472,16 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
InvokeRuntimeCallingConvention calling_convention;
// In the unlucky case that the `temp` is R0, we preserve the address in `out` across
- // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
- bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
- Register entry_address = temp_is_r0 ? out : temp;
- DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
- if (call_saves_everything_except_r0 && temp_is_r0) {
- __ mov(entry_address, ShifterOperand(temp));
+ // the kSaveEverything call.
+ Register entry_address = kNoRegister;
+ if (call_saves_everything_except_r0) {
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0));
+ entry_address = temp_is_r0 ? out : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_r0) {
+ __ mov(entry_address, ShifterOperand(temp));
+ }
}
__ LoadImmediate(calling_convention.GetRegisterAt(0), string_index.index_);
@@ -473,15 +494,16 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
__ str(R0, Address(entry_address));
} else {
// For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ Register temp = IP;
CodeGeneratorARM::PcRelativePatchInfo* labels =
arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
__ BindTrackedLabel(&labels->movw_label);
- __ movw(entry_address, /* placeholder */ 0u);
+ __ movw(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->movt_label);
- __ movt(entry_address, /* placeholder */ 0u);
+ __ movt(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
- __ add(entry_address, entry_address, ShifterOperand(PC));
- __ str(R0, Address(entry_address));
+ __ add(temp, temp, ShifterOperand(PC));
+ __ str(R0, Address(temp));
}
arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
@@ -624,6 +646,10 @@ class ArraySetSlowPathARM : public SlowPathCodeARM {
// probably still be a from-space reference (unless it gets updated by
// another thread, or if another thread installed another object
// reference (different from `ref`) in `obj.field`).
+//
+// If `entrypoint` is a valid location it is assumed to already be
+// holding the entrypoint. The case where the entrypoint is passed in
+// is for the GcRoot read barrier.
class ReadBarrierMarkSlowPathARM : public SlowPathCodeARM {
public:
ReadBarrierMarkSlowPathARM(HInstruction* instruction,
@@ -5755,6 +5781,7 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
cls,
Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Location::RegisterLocation(R0));
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), R0);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -5772,6 +5799,22 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution or initialization and marking to save everything we need.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
+ locations->AddTemp(Location::RequiresRegister());
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
+ // that the the kPrimNot result register is the same as the first argument register.
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
@@ -5834,15 +5877,18 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE
break;
}
case HLoadClass::LoadKind::kBssEntry: {
+ Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? locations->GetTemp(0).AsRegister<Register>()
+ : out;
CodeGeneratorARM::PcRelativePatchInfo* labels =
codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
__ BindTrackedLabel(&labels->movw_label);
- __ movw(out, /* placeholder */ 0u);
+ __ movw(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->movt_label);
- __ movt(out, /* placeholder */ 0u);
+ __ movt(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
- __ add(out, out, ShifterOperand(PC));
- GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption);
+ __ add(temp, temp, ShifterOperand(PC));
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
generate_null_check = true;
break;
}
@@ -5851,7 +5897,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE
cls->GetTypeIndex(),
cls->GetClass()));
// /* GcRoot<mirror::Class> */ out = *out
- GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -5938,9 +5984,9 @@ void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything, including temps.
- // Note that IP may theoretically be clobbered by saving/restoring the live register
- // (only one thanks to the custom calling convention), so we request a different temp.
+ // Rely on the pResolveString and marking to save everything we need, including temps.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
@@ -5991,7 +6037,9 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_S
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
- Register temp = locations->GetTemp(0).AsRegister<Register>();
+ Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? locations->GetTemp(0).AsRegister<Register>()
+ : out;
CodeGeneratorARM::PcRelativePatchInfo* labels =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
__ BindTrackedLabel(&labels->movw_label);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 598be4715b..e6032d2381 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -34,6 +34,9 @@
#include "utils/stack_checks.h"
using namespace vixl::aarch64; // NOLINT(build/namespaces)
+using vixl::ExactAssemblyScope;
+using vixl::CodeBufferCheckScope;
+using vixl::EmissionCheckScope;
#ifdef __
#error "ARM64 Codegen VIXL macro-assembler macro already defined."
@@ -275,15 +278,37 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
LoadClassSlowPathARM64(HLoadClass* cls,
HInstruction* at,
uint32_t dex_pc,
- bool do_clinit)
- : SlowPathCodeARM64(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ bool do_clinit,
+ vixl::aarch64::Register bss_entry_temp = vixl::aarch64::Register(),
+ vixl::aarch64::Label* bss_entry_adrp_label = nullptr)
+ : SlowPathCodeARM64(at),
+ cls_(cls),
+ dex_pc_(dex_pc),
+ do_clinit_(do_clinit),
+ bss_entry_temp_(bss_entry_temp),
+ bss_entry_adrp_label_(bss_entry_adrp_label) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
+ constexpr bool call_saves_everything_except_r0_ip0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the page address of
+ // the entry which is in a scratch register. Make sure it's not used for saving/restoring
+ // registers. Exclude the scratch register also for non-Baker read barrier for simplicity.
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
+ UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler());
+ if (is_load_class_bss_entry) {
+ // This temp is a scratch register.
+ DCHECK(bss_entry_temp_.IsValid());
+ temps.Exclude(bss_entry_temp_);
+ }
+
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -300,7 +325,6 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
}
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Primitive::Type type = instruction_->GetType();
@@ -308,25 +332,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
}
RestoreLiveRegisters(codegen, locations);
// For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
- DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
- if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
+ if (is_load_class_bss_entry) {
DCHECK(out.IsValid());
- UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler());
- Register temp = temps.AcquireX();
const DexFile& dex_file = cls_->GetDexFile();
- // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
- // kSaveEverything and use a temporary for the ADRP in the fast path, so that we
- // can avoid the ADRP here.
- vixl::aarch64::Label* adrp_label =
- arm64_codegen->NewBssEntryTypePatch(dex_file, type_index);
- arm64_codegen->EmitAdrpPlaceholder(adrp_label, temp);
+ if (call_saves_everything_except_r0_ip0) {
+ // The class entry page address was preserved in bss_entry_temp_ thanks to kSaveEverything.
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the class entry page.
+ bss_entry_adrp_label_ = arm64_codegen->NewBssEntryTypePatch(dex_file, type_index);
+ arm64_codegen->EmitAdrpPlaceholder(bss_entry_adrp_label_, bss_entry_temp_);
+ }
vixl::aarch64::Label* strp_label =
- arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
+ arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label_);
{
SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler());
__ Bind(strp_label);
__ str(RegisterFrom(locations->Out(), Primitive::kPrimNot),
- MemOperand(temp, /* offset placeholder */ 0));
+ MemOperand(bss_entry_temp_, /* offset placeholder */ 0));
}
}
__ B(GetExitLabel());
@@ -344,6 +366,10 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
// Whether to initialize the class.
const bool do_clinit_;
+ // For HLoadClass/kBssEntry, the temp register and the label of the ADRP where it was loaded.
+ vixl::aarch64::Register bss_entry_temp_;
+ vixl::aarch64::Label* bss_entry_adrp_label_;
+
DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64);
};
@@ -590,10 +616,9 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
// We are about to use the assembler to place literals directly. Make sure we have enough
// underlying code buffer and we have generated the jump table with right size.
- vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(),
- num_entries * sizeof(int32_t),
- vixl::CodeBufferCheckScope::kReserveBufferSpace,
- vixl::CodeBufferCheckScope::kExactSize);
+ EmissionCheckScope scope(codegen->GetVIXLAssembler(),
+ num_entries * sizeof(int32_t),
+ CodeBufferCheckScope::kExactSize);
__ Bind(&table_start_);
const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
@@ -619,8 +644,10 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
// probably still be a from-space reference (unless it gets updated by
// another thread, or if another thread installed another object
// reference (different from `ref`) in `obj.field`).
-// If entrypoint is a valid location it is assumed to already be holding the entrypoint. The case
-// where the entrypoint is passed in is for the GcRoot read barrier.
+//
+// If `entrypoint` is a valid location it is assumed to already be
+// holding the entrypoint. The case where the entrypoint is passed in
+// is for the GcRoot read barrier.
class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 {
public:
ReadBarrierMarkSlowPathARM64(HInstruction* instruction,
@@ -1254,7 +1281,6 @@ void ParallelMoveResolverARM64::EmitMove(size_t index) {
void CodeGeneratorARM64::GenerateFrameEntry() {
MacroAssembler* masm = GetVIXLAssembler();
- BlockPoolsScope block_pools(masm);
__ Bind(&frame_entry_label_);
bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kArm64) || !IsLeafMethod();
@@ -1263,8 +1289,14 @@ void CodeGeneratorARM64::GenerateFrameEntry() {
Register temp = temps.AcquireX();
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
__ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
- __ Ldr(wzr, MemOperand(temp, 0));
- RecordPcInfo(nullptr, 0);
+ {
+ // Ensure that between load and RecordPcInfo there are no pools emitted.
+ ExactAssemblyScope eas(GetVIXLAssembler(),
+ kInstructionSize,
+ CodeBufferCheckScope::kExactSize);
+ __ ldr(wzr, MemOperand(temp, 0));
+ RecordPcInfo(nullptr, 0);
+ }
}
if (!HasEmptyFrame()) {
@@ -1299,7 +1331,6 @@ void CodeGeneratorARM64::GenerateFrameEntry() {
}
void CodeGeneratorARM64::GenerateFrameExit() {
- BlockPoolsScope block_pools(GetVIXLAssembler());
GetAssembler()->cfi().RememberState();
if (!HasEmptyFrame()) {
int frame_size = GetFrameSize();
@@ -1626,7 +1657,6 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
const MemOperand& src,
bool needs_null_check) {
MacroAssembler* masm = GetVIXLAssembler();
- BlockPoolsScope block_pools(masm);
UseScratchRegisterScope temps(masm);
Register temp_base = temps.AcquireX();
Primitive::Type type = instruction->GetType();
@@ -1636,58 +1666,79 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
// TODO(vixl): Let the MacroAssembler handle MemOperand.
__ Add(temp_base, src.GetBaseRegister(), OperandFromMemOperand(src));
- MemOperand base = MemOperand(temp_base);
- switch (type) {
- case Primitive::kPrimBoolean:
- __ Ldarb(Register(dst), base);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- break;
- case Primitive::kPrimByte:
- __ Ldarb(Register(dst), base);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
- break;
- case Primitive::kPrimChar:
- __ Ldarh(Register(dst), base);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- break;
- case Primitive::kPrimShort:
- __ Ldarh(Register(dst), base);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
- break;
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimLong:
- DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
- __ Ldar(Register(dst), base);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- break;
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble: {
- DCHECK(dst.IsFPRegister());
- DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
-
- Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
- __ Ldar(temp, base);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ MemOperand base = MemOperand(temp_base);
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ ldarb(Register(dst), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
+ break;
+ case Primitive::kPrimByte:
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ ldarb(Register(dst), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
+ __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
+ break;
+ case Primitive::kPrimChar:
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ ldarh(Register(dst), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
+ break;
+ case Primitive::kPrimShort:
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ ldarh(Register(dst), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
+ __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimLong:
+ DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ ldar(Register(dst), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ DCHECK(dst.IsFPRegister());
+ DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
+
+ Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ ldar(temp, base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
+ __ Fmov(FPRegister(dst), temp);
+ break;
}
- __ Fmov(FPRegister(dst), temp);
- break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << type;
}
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unreachable type " << type;
}
}
@@ -1716,9 +1767,12 @@ void CodeGeneratorARM64::Store(Primitive::Type type,
}
}
-void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
+void CodeGeneratorARM64::StoreRelease(HInstruction* instruction,
+ Primitive::Type type,
CPURegister src,
- const MemOperand& dst) {
+ const MemOperand& dst,
+ bool needs_null_check) {
+ MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp_base = temps.AcquireX();
@@ -1729,20 +1783,39 @@ void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
Operand op = OperandFromMemOperand(dst);
__ Add(temp_base, dst.GetBaseRegister(), op);
MemOperand base = MemOperand(temp_base);
+ // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- __ Stlrb(Register(src), base);
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ stlrb(Register(src), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
break;
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- __ Stlrh(Register(src), base);
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ stlrh(Register(src), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
case Primitive::kPrimLong:
DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
- __ Stlr(Register(src), base);
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ stlr(Register(src), base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
@@ -1756,8 +1829,13 @@ void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
temp_src = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
__ Fmov(temp_src, FPRegister(src));
}
-
- __ Stlr(temp_src, base);
+ {
+ ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ stlr(temp_src, base);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ }
break;
}
case Primitive::kPrimVoid:
@@ -1770,9 +1848,15 @@ void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint,
uint32_t dex_pc,
SlowPathCode* slow_path) {
ValidateInvokeRuntime(entrypoint, instruction, slow_path);
- GenerateInvokeRuntime(GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value());
- if (EntrypointRequiresStackMap(entrypoint)) {
- RecordPcInfo(instruction, dex_pc, slow_path);
+
+ __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value()));
+ {
+ // Ensure the pc position is recorded immediately after the `blr` instruction.
+ ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
+ __ blr(lr);
+ if (EntrypointRequiresStackMap(entrypoint)) {
+ RecordPcInfo(instruction, dex_pc, slow_path);
+ }
}
}
@@ -1780,11 +1864,6 @@ void CodeGeneratorARM64::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point
HInstruction* instruction,
SlowPathCode* slow_path) {
ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
- GenerateInvokeRuntime(entry_point_offset);
-}
-
-void CodeGeneratorARM64::GenerateInvokeRuntime(int32_t entry_point_offset) {
- BlockPoolsScope block_pools(GetVIXLAssembler());
__ Ldr(lr, MemOperand(tr, entry_point_offset));
__ Blr(lr);
}
@@ -1951,7 +2030,6 @@ void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction,
Location out = locations->Out();
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
Primitive::Type field_type = field_info.GetFieldType();
- BlockPoolsScope block_pools(GetVIXLAssembler());
MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset());
if (field_type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -1978,6 +2056,8 @@ void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction,
codegen_->LoadAcquire(
instruction, OutputCPURegister(instruction), field, /* needs_null_check */ true);
} else {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
codegen_->Load(field_type, OutputCPURegister(instruction), field);
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -2007,7 +2087,6 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
bool value_can_be_null) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
- BlockPoolsScope block_pools(GetVIXLAssembler());
Register obj = InputRegisterAt(instruction, 0);
CPURegister value = InputCPURegisterOrZeroRegAt(instruction, 1);
@@ -2029,9 +2108,11 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
}
if (field_info.IsVolatile()) {
- codegen_->StoreRelease(field_type, source, HeapOperand(obj, offset));
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ codegen_->StoreRelease(
+ instruction, field_type, source, HeapOperand(obj, offset), /* needs_null_check */ true);
} else {
+ // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
codegen_->Store(field_type, source, HeapOperand(obj, offset));
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -2317,10 +2398,7 @@ void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate*
masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize;
if (prev->IsLoadOrStore()) {
// Make sure we emit only exactly one nop.
- vixl::CodeBufferCheckScope scope(masm,
- kInstructionSize,
- vixl::CodeBufferCheckScope::kReserveBufferSpace,
- vixl::CodeBufferCheckScope::kExactSize);
+ ExactAssemblyScope scope(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
__ nop();
}
}
@@ -2376,8 +2454,6 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
instruction->IsStringCharAt();
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
- // Block pools between `Load` and `MaybeRecordImplicitNullCheck`.
- BlockPoolsScope block_pools(masm);
// The read barrier instrumentation of object ArrayGet instructions
// does not support the HIntermediateAddress instruction.
@@ -2399,15 +2475,21 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
if (maybe_compressed_char_at) {
uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
length = temps.AcquireW();
- if (instruction->GetArray()->IsIntermediateAddress()) {
- DCHECK_LT(count_offset, offset);
- int64_t adjusted_offset = static_cast<int64_t>(count_offset) - static_cast<int64_t>(offset);
- // Note that `adjusted_offset` is negative, so this will be a LDUR.
- __ Ldr(length, MemOperand(obj.X(), adjusted_offset));
- } else {
- __ Ldr(length, HeapOperand(obj, count_offset));
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+
+ if (instruction->GetArray()->IsIntermediateAddress()) {
+ DCHECK_LT(count_offset, offset);
+ int64_t adjusted_offset =
+ static_cast<int64_t>(count_offset) - static_cast<int64_t>(offset);
+ // Note that `adjusted_offset` is negative, so this will be a LDUR.
+ __ Ldr(length, MemOperand(obj.X(), adjusted_offset));
+ } else {
+ __ Ldr(length, HeapOperand(obj, count_offset));
+ }
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
}
if (index.IsConstant()) {
if (maybe_compressed_char_at) {
@@ -2457,6 +2539,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
}
}
if (!maybe_compressed_char_at) {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
codegen_->Load(type, OutputCPURegister(instruction), source);
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -2484,9 +2568,12 @@ void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
vixl::aarch64::Register out = OutputRegister(instruction);
- BlockPoolsScope block_pools(GetVIXLAssembler());
- __ Ldr(out, HeapOperand(InputRegisterAt(instruction, 0), offset));
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ __ Ldr(out, HeapOperand(InputRegisterAt(instruction, 0), offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
// Mask out compression flag from String's array length.
if (mirror::kUseStringCompression && instruction->IsStringLength()) {
__ Lsr(out.W(), out.W(), 1u);
@@ -2527,7 +2614,6 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
MemOperand destination = HeapOperand(array);
MacroAssembler* masm = GetVIXLAssembler();
- BlockPoolsScope block_pools(masm);
if (!needs_write_barrier) {
DCHECK(!may_need_runtime_call_for_type_check);
@@ -2554,8 +2640,12 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
LSL,
Primitive::ComponentSizeShift(value_type));
}
- codegen_->Store(value_type, value, destination);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ codegen_->Store(value_type, value, destination);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
} else {
DCHECK(!instruction->GetArray()->IsIntermediateAddress());
vixl::aarch64::Label done;
@@ -2588,8 +2678,13 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
if (!index.IsConstant()) {
__ Add(temp, array, offset);
}
- __ Str(wzr, destination);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools
+ // emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ __ Str(wzr, destination);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
__ B(&done);
__ Bind(&non_zero);
}
@@ -2604,8 +2699,12 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
Register temp2 = temps.AcquireSameSizeAs(array);
// /* HeapReference<Class> */ temp = array->klass_
- __ Ldr(temp, HeapOperand(array, class_offset));
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ __ Ldr(temp, HeapOperand(array, class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
GetAssembler()->MaybeUnpoisonHeapReference(temp);
// /* HeapReference<Class> */ temp = temp->component_type_
@@ -2646,10 +2745,14 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
if (!index.IsConstant()) {
__ Add(temp, array, offset);
}
- __ Str(source, destination);
+ {
+ // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ __ Str(source, destination);
- if (!may_need_runtime_call_for_type_check) {
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ if (!may_need_runtime_call_for_type_check) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
}
@@ -3944,19 +4047,25 @@ void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invok
// art_quick_imt_conflict_trampoline, so prevent VIXL from using it.
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope scratch_scope(masm);
- BlockPoolsScope block_pools(masm);
scratch_scope.Exclude(ip1);
__ Mov(ip1, invoke->GetDexMethodIndex());
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
if (receiver.IsStackSlot()) {
__ Ldr(temp.W(), StackOperandFrom(receiver));
- // /* HeapReference<Class> */ temp = temp->klass_
- __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset));
+ {
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ // /* HeapReference<Class> */ temp = temp->klass_
+ __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ }
} else {
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
// /* HeapReference<Class> */ temp = receiver->klass_
__ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
}
- codegen_->MaybeRecordImplicitNullCheck(invoke);
+
// Instead of simply (possibly) unpoisoning `temp` here, we should
// emit a read barrier for the previous class reference load.
// However this is not required in practice, as this is an
@@ -3973,10 +4082,16 @@ void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invok
__ Ldr(temp, MemOperand(temp, method_offset));
// lr = temp->GetEntryPoint();
__ Ldr(lr, MemOperand(temp, entry_point.Int32Value()));
- // lr();
- __ Blr(lr);
- DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+
+ {
+ // Ensure the pc position is recorded immediately after the `blr` instruction.
+ ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
+
+ // lr();
+ __ blr(lr);
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+ }
}
void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
@@ -4088,8 +4203,16 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok
__ Ldr(lr, MemOperand(
XRegisterFrom(callee_method),
ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize).Int32Value()));
- // lr()
- __ Blr(lr);
+ {
+ // To ensure that the pc position is recorded immediately after the `blr` instruction
+ // BLR must be the last instruction emitted in this function.
+ // Recording the pc will occur right after returning from this function.
+ ExactAssemblyScope eas(GetVIXLAssembler(),
+ kInstructionSize,
+ CodeBufferCheckScope::kExactSize);
+ // lr()
+ __ blr(lr);
+ }
break;
}
@@ -4109,12 +4232,15 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te
Offset class_offset = mirror::Object::ClassOffset();
Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize);
- BlockPoolsScope block_pools(GetVIXLAssembler());
-
DCHECK(receiver.IsRegister());
- // /* HeapReference<Class> */ temp = receiver->klass_
- __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset));
- MaybeRecordImplicitNullCheck(invoke);
+
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ // /* HeapReference<Class> */ temp = receiver->klass_
+ __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset));
+ MaybeRecordImplicitNullCheck(invoke);
+ }
// Instead of simply (possibly) unpoisoning `temp` here, we should
// emit a read barrier for the previous class reference load.
// intermediate/temporary reference and because the current
@@ -4126,8 +4252,14 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te
__ Ldr(temp, MemOperand(temp, method_offset));
// lr = temp->GetEntryPoint();
__ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
- // lr();
- __ Blr(lr);
+ {
+ // To ensure that the pc position is recorded immediately after the `blr` instruction
+ // BLR should be the last instruction emitted in this function.
+ // Recording the pc will occur right after returning from this function.
+ ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
+ // lr();
+ __ blr(lr);
+ }
}
void LocationsBuilderARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
@@ -4340,7 +4472,9 @@ void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDir
return;
}
- BlockPoolsScope block_pools(GetVIXLAssembler());
+ // Ensure that between the BLR (emitted by GenerateStaticOrDirectCall) and RecordPcInfo there
+ // are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
LocationSummary* locations = invoke->GetLocations();
codegen_->GenerateStaticOrDirectCall(
invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
@@ -4352,6 +4486,9 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
return;
}
+ // Ensure that between the BLR (emitted by GenerateVirtualCall) and RecordPcInfo there
+ // are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -4393,6 +4530,7 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
cls,
LocationFrom(calling_convention.GetRegisterAt(0)),
LocationFrom(vixl::aarch64::x0));
+ DCHECK(calling_convention.GetRegisterAt(0).Is(vixl::aarch64::x0));
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -4410,6 +4548,22 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution or initialization and marking to save everything we need.
+ // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we shall use IP1.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
+ DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(),
+ RegisterFrom(calling_convention.GetReturnLocation(Primitive::kPrimNot),
+ Primitive::kPrimNot).GetCode());
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
@@ -4424,6 +4578,8 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
Location out_loc = cls->GetLocations()->Out();
Register out = OutputRegister(cls);
+ Register bss_entry_temp;
+ vixl::aarch64::Label* bss_entry_adrp_label = nullptr;
const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
? kWithoutReadBarrier
@@ -4473,18 +4629,23 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
// Add ADRP with its PC-relative Class .bss entry patch.
const DexFile& dex_file = cls->GetDexFile();
dex::TypeIndex type_index = cls->GetTypeIndex();
- vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
- codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
+ // We can go to slow path even with non-zero reference and in that case marking
+ // can clobber IP0, so we need to use IP1 which shall be preserved.
+ bss_entry_temp = ip1;
+ UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
+ temps.Exclude(bss_entry_temp);
+ bss_entry_adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
+ codegen_->EmitAdrpPlaceholder(bss_entry_adrp_label, bss_entry_temp);
// Add LDR with its PC-relative Class patch.
vixl::aarch64::Label* ldr_label =
- codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
+ codegen_->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label);
// /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */
GenerateGcRootFieldLoad(cls,
- cls->GetLocations()->Out(),
- out.X(),
- /* placeholder */ 0u,
+ out_loc,
+ bss_entry_temp,
+ /* offset placeholder */ 0u,
ldr_label,
- kCompilerReadBarrierOption);
+ read_barrier_option);
generate_null_check = true;
break;
}
@@ -4497,7 +4658,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
out.X(),
/* offset */ 0,
/* fixup_label */ nullptr,
- kCompilerReadBarrierOption);
+ read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -4506,10 +4667,11 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA
UNREACHABLE();
}
- if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ bool do_clinit = cls->MustGenerateClinitCheck();
+ if (generate_null_check || do_clinit) {
DCHECK(cls->CanCallRuntime());
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ cls, cls, cls->GetDexPc(), do_clinit, bss_entry_temp, bss_entry_adrp_label);
codegen_->AddSlowPath(slow_path);
if (generate_null_check) {
__ Cbz(out, slow_path->GetEntryLabel());
@@ -4577,7 +4739,9 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything, including temps.
+ // Rely on the pResolveString and marking to save everything we need.
+ // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we shall use IP1.
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
@@ -4628,8 +4792,11 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD
const DexFile& dex_file = load->GetDexFile();
const dex::StringIndex string_index = load->GetStringIndex();
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+ // We could use IP0 as the marking shall not clobber IP0 if the reference is null and
+ // that's when we need the slow path. But let's not rely on such details and use IP1.
+ Register temp = ip1;
UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
- Register temp = temps.AcquireX();
+ temps.Exclude(temp);
vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index);
codegen_->EmitAdrpPlaceholder(adrp_label, temp);
// Add LDR with its PC-relative String patch.
@@ -4817,8 +4984,15 @@ void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction)
MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize);
__ Ldr(XRegisterFrom(temp), MemOperand(tr, QUICK_ENTRY_POINT(pNewEmptyString)));
__ Ldr(lr, MemOperand(XRegisterFrom(temp), code_offset.Int32Value()));
- __ Blr(lr);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+
+ {
+ // Ensure the pc position is recorded immediately after the `blr` instruction.
+ ExactAssemblyScope eas(GetVIXLAssembler(),
+ kInstructionSize,
+ CodeBufferCheckScope::kExactSize);
+ __ blr(lr);
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ }
} else {
codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
@@ -4862,11 +5036,13 @@ void CodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
if (CanMoveNullCheckToUser(instruction)) {
return;
}
-
- BlockPoolsScope block_pools(GetVIXLAssembler());
- Location obj = instruction->GetLocations()->InAt(0);
- __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
- RecordPcInfo(instruction, instruction->GetDexPc());
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ Location obj = instruction->GetLocations()->InAt(0);
+ __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
+ RecordPcInfo(instruction, instruction->GetDexPc());
+ }
}
void CodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
@@ -5603,10 +5779,14 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction*
DCHECK(obj.IsW());
uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
- // /* int32_t */ monitor = obj->monitor_
- __ Ldr(temp, HeapOperand(obj, monitor_offset));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ // /* int32_t */ monitor = obj->monitor_
+ __ Ldr(temp, HeapOperand(obj, monitor_offset));
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
}
// /* LockWord */ lock_word = LockWord(monitor)
static_assert(sizeof(LockWord) == sizeof(int32_t),
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index f6cb90a63a..5faf29a90f 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -43,6 +43,11 @@ class CodeGeneratorARM64;
// Use a local definition to prevent copying mistakes.
static constexpr size_t kArm64WordSize = static_cast<size_t>(kArm64PointerSize);
+// These constants are used as an approximate margin when emission of veneer and literal pools
+// must be blocked.
+static constexpr int kMaxMacroInstructionSizeInBytes = 15 * vixl::aarch64::kInstructionSize;
+static constexpr int kInvokeCodeMarginSizeInBytes = 6 * kMaxMacroInstructionSizeInBytes;
+
static const vixl::aarch64::Register kParameterCoreRegisters[] = {
vixl::aarch64::x1,
vixl::aarch64::x2,
@@ -486,9 +491,11 @@ class CodeGeneratorARM64 : public CodeGenerator {
vixl::aarch64::CPURegister dst,
const vixl::aarch64::MemOperand& src,
bool needs_null_check);
- void StoreRelease(Primitive::Type type,
+ void StoreRelease(HInstruction* instruction,
+ Primitive::Type type,
vixl::aarch64::CPURegister src,
- const vixl::aarch64::MemOperand& dst);
+ const vixl::aarch64::MemOperand& dst,
+ bool needs_null_check);
// Generate code to invoke a runtime entry point.
void InvokeRuntime(QuickEntrypointEnum entrypoint,
@@ -502,8 +509,6 @@ class CodeGeneratorARM64 : public CodeGenerator {
HInstruction* instruction,
SlowPathCode* slow_path);
- void GenerateInvokeRuntime(int32_t entry_point_offset);
-
ParallelMoveResolverARM64* GetMoveResolver() OVERRIDE { return &move_resolver_; }
bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index e18960872e..5c4ca5bc17 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -400,12 +400,30 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
+ constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
+ vixl32::Register entry_address;
+ if (is_load_class_bss_entry && call_saves_everything_except_r0) {
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
+ // the kSaveEverything call.
+ bool temp_is_r0 = temp.Is(calling_convention.GetRegisterAt(0));
+ entry_address = temp_is_r0 ? RegisterFrom(out) : temp;
+ DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
+ if (temp_is_r0) {
+ __ Mov(entry_address, temp);
+ }
+ }
dex::TypeIndex type_index = cls_->GetTypeIndex();
__ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
@@ -417,27 +435,28 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
}
+ // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
+ if (is_load_class_bss_entry) {
+ if (call_saves_everything_except_r0) {
+ // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ Str(r0, MemOperand(entry_address));
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ UseScratchRegisterScope temps(
+ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
+ arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+ __ Str(r0, MemOperand(temp));
+ }
+ }
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
arm_codegen->Move32(locations->Out(), LocationFrom(r0));
}
RestoreLiveRegisters(codegen, locations);
- // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
- DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
- if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
- DCHECK(out.IsValid());
- // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
- // kSaveEverything and use a temporary for the .bss entry address in the fast path,
- // so that we can avoid another calculation here.
- UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
- vixl32::Register temp = temps.Acquire();
- CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
- arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
- __ Str(OutputRegister(cls_), MemOperand(temp));
- }
__ B(GetExitLabel());
}
@@ -462,12 +481,13 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
: SlowPathCodeARMVIXL(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ DCHECK(instruction_->IsLoadString());
+ DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
LocationSummary* locations = instruction_->GetLocations();
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
HLoadString* load = instruction_->AsLoadString();
const dex::StringIndex string_index = load->GetStringIndex();
vixl32::Register out = OutputRegister(load);
- vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
@@ -476,12 +496,16 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
InvokeRuntimeCallingConventionARMVIXL calling_convention;
// In the unlucky case that the `temp` is R0, we preserve the address in `out` across
- // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
- bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
- vixl32::Register entry_address = temp_is_r0 ? out : temp;
- DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
- if (call_saves_everything_except_r0 && temp_is_r0) {
- __ Mov(entry_address, temp);
+ // the kSaveEverything call.
+ vixl32::Register entry_address;
+ if (call_saves_everything_except_r0) {
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
+ entry_address = temp_is_r0 ? out : temp;
+ DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
+ if (temp_is_r0) {
+ __ Mov(entry_address, temp);
+ }
}
__ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
@@ -494,10 +518,13 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
__ Str(r0, MemOperand(entry_address));
} else {
// For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ UseScratchRegisterScope temps(
+ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
- arm_codegen->EmitMovwMovtPlaceholder(labels, out);
- __ Str(r0, MemOperand(entry_address));
+ arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+ __ Str(r0, MemOperand(temp));
}
arm_codegen->Move32(locations->Out(), LocationFrom(r0));
@@ -5832,6 +5859,7 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
cls,
LocationFrom(calling_convention.GetRegisterAt(0)),
LocationFrom(r0));
+ DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -5849,6 +5877,22 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution or initialization and marking to save everything we need.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
+ locations->AddTemp(Location::RequiresRegister());
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
+ // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
+ // that the the kPrimNot result register is the same as the first argument register.
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
@@ -5906,10 +5950,13 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_
break;
}
case HLoadClass::LoadKind::kBssEntry: {
+ vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? RegisterFrom(locations->GetTemp(0))
+ : out;
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
- codegen_->EmitMovwMovtPlaceholder(labels, out);
- GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption);
+ codegen_->EmitMovwMovtPlaceholder(labels, temp);
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
generate_null_check = true;
break;
}
@@ -5918,7 +5965,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_
cls->GetTypeIndex(),
cls->GetClass()));
// /* GcRoot<mirror::Class> */ out = *out
- GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -6012,9 +6059,9 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything, including temps.
- // Note that IP may theoretically be clobbered by saving/restoring the live register
- // (only one thanks to the custom calling convention), so we request a different temp.
+ // Rely on the pResolveString and marking to save everything we need, including temps.
+ // Note that IP may be clobbered by saving/restoring the live register (only one thanks
+ // to the custom calling convention) or by marking, so we request a different temp.
locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConventionARMVIXL calling_convention;
@@ -6059,7 +6106,9 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
- vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier)
+ ? RegisterFrom(locations->GetTemp(0))
+ : out;
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
codegen_->EmitMovwMovtPlaceholder(labels, temp);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 0677dad078..c9dde7cc55 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1914,6 +1914,8 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
auto null_checker = GetImplicitNullChecker(instruction);
Primitive::Type type = instruction->GetType();
+ const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+ instruction->IsStringCharAt();
switch (type) {
case Primitive::kPrimBoolean: {
Register out = locations->Out().AsRegister<Register>();
@@ -1957,14 +1959,54 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimChar: {
Register out = locations->Out().AsRegister<Register>();
+ if (maybe_compressed_char_at) {
+ uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+ __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker);
+ __ Sll(TMP, TMP, 31); // Extract compression flag into the most significant bit of TMP.
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
+ }
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
- __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset, null_checker);
+ int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ if (maybe_compressed_char_at) {
+ MipsLabel uncompressed_load, done;
+ __ Bnez(TMP, &uncompressed_load);
+ __ LoadFromOffset(kLoadUnsignedByte,
+ out,
+ obj,
+ data_offset + (const_index << TIMES_1));
+ __ B(&done);
+ __ Bind(&uncompressed_load);
+ __ LoadFromOffset(kLoadUnsignedHalfword,
+ out,
+ obj,
+ data_offset + (const_index << TIMES_2));
+ __ Bind(&done);
+ } else {
+ __ LoadFromOffset(kLoadUnsignedHalfword,
+ out,
+ obj,
+ data_offset + (const_index << TIMES_2),
+ null_checker);
+ }
} else {
- __ Sll(TMP, index.AsRegister<Register>(), TIMES_2);
- __ Addu(TMP, obj, TMP);
- __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
+ Register index_reg = index.AsRegister<Register>();
+ if (maybe_compressed_char_at) {
+ MipsLabel uncompressed_load, done;
+ __ Bnez(TMP, &uncompressed_load);
+ __ Addu(TMP, obj, index_reg);
+ __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
+ __ B(&done);
+ __ Bind(&uncompressed_load);
+ __ Sll(TMP, index_reg, TIMES_2);
+ __ Addu(TMP, obj, TMP);
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+ __ Bind(&done);
+ } else {
+ __ Sll(TMP, index_reg, TIMES_2);
+ __ Addu(TMP, obj, TMP);
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker);
+ }
}
break;
}
@@ -2046,6 +2088,10 @@ void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) {
Register out = locations->Out().AsRegister<Register>();
__ LoadFromOffset(kLoadWord, out, obj, offset);
codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // Mask out compression flag from String's array length.
+ if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+ __ Srl(out, out, 1u);
+ }
}
Location LocationsBuilderMIPS::RegisterOrZeroConstant(HInstruction* instruction) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 4c8dabfede..5be0da4011 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1490,6 +1490,8 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
+ const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+ instruction->IsStringCharAt();
switch (type) {
case Primitive::kPrimBoolean: {
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
@@ -1533,14 +1535,54 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimChar: {
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ if (maybe_compressed_char_at) {
+ uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+ __ LoadFromOffset(kLoadWord, TMP, obj, count_offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ Dext(TMP, TMP, 0, 1);
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
+ }
if (index.IsConstant()) {
- size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
- __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+ int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ if (maybe_compressed_char_at) {
+ Mips64Label uncompressed_load, done;
+ __ Bnezc(TMP, &uncompressed_load);
+ __ LoadFromOffset(kLoadUnsignedByte,
+ out,
+ obj,
+ data_offset + (const_index << TIMES_1));
+ __ Bc(&done);
+ __ Bind(&uncompressed_load);
+ __ LoadFromOffset(kLoadUnsignedHalfword,
+ out,
+ obj,
+ data_offset + (const_index << TIMES_2));
+ __ Bind(&done);
+ } else {
+ __ LoadFromOffset(kLoadUnsignedHalfword,
+ out,
+ obj,
+ data_offset + (const_index << TIMES_2));
+ }
} else {
- __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2);
- __ Daddu(TMP, obj, TMP);
- __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+ GpuRegister index_reg = index.AsRegister<GpuRegister>();
+ if (maybe_compressed_char_at) {
+ Mips64Label uncompressed_load, done;
+ __ Bnezc(TMP, &uncompressed_load);
+ __ Daddu(TMP, obj, index_reg);
+ __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset);
+ __ Bc(&done);
+ __ Bind(&uncompressed_load);
+ __ Dsll(TMP, index_reg, TIMES_2);
+ __ Daddu(TMP, obj, TMP);
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+ __ Bind(&done);
+ } else {
+ __ Dsll(TMP, index_reg, TIMES_2);
+ __ Daddu(TMP, obj, TMP);
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset);
+ }
}
break;
}
@@ -1608,7 +1650,9 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ if (!maybe_compressed_char_at) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
}
void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) {
@@ -1624,6 +1668,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction)
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
__ LoadFromOffset(kLoadWord, out, obj, offset);
codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // Mask out compression flag from String's array length.
+ if (mirror::kUseStringCompression && instruction->IsStringLength()) {
+ __ Srl(out, out, 1u);
+ }
}
void LocationsBuilderMIPS64::VisitArraySet(HArraySet* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 137b55423b..09612c8dbf 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6057,6 +6057,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
cls,
Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
Location::RegisterLocation(EAX));
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), EAX);
return;
}
DCHECK(!cls->NeedsAccessCheck());
@@ -6076,6 +6077,17 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution and/or initialization to save everything.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConvention calling_convention;
+ caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file,
@@ -6158,7 +6170,7 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE
Label* fixup_label = codegen_->NewJitRootClassPatch(
cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass());
// /* GcRoot<mirror::Class> */ out = *address
- GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod:
@@ -6250,7 +6262,7 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything.
+ // Rely on the pResolveString to save everything.
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
@@ -7136,9 +7148,10 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = *address;
- // if (Thread::Current()->GetIsGcMarking()) {
- // root = ReadBarrier::Mark(root)
+ // root = obj.field;
+ // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+ // if (temp != null) {
+ // root = temp(root)
// }
// /* GcRoot<mirror::Object> */ root = *address
@@ -7159,8 +7172,11 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(
instruction, root, /* unpoison_ref_before_marking */ false);
codegen_->AddSlowPath(slow_path);
- __ fs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86PointerSize>().Int32Value()),
- Immediate(0));
+ // Test the entrypoint (`Thread::Current()->pReadBarrierMarkReg ## root.reg()`).
+ const int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(root.reg());
+ __ fs()->cmpl(Address::Absolute(entry_point_offset), Immediate(0));
+ // The entrypoint is null when the GC is not marking.
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
} else {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c5367ce86e..0879992e32 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -245,9 +245,8 @@ class LoadClassSlowPathX86_64 : public SlowPathCode {
SaveLiveRegisters(codegen, locations);
- InvokeRuntimeCallingConvention calling_convention;
- __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
- Immediate(cls_->GetTypeIndex().index_));
+ // Custom calling convention: RAX serves as both input and output.
+ __ movl(CpuRegister(RAX), Immediate(cls_->GetTypeIndex().index_));
x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType,
instruction_,
dex_pc_,
@@ -5456,10 +5455,10 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
- InvokeRuntimeCallingConvention calling_convention;
+ // Custom calling convention: RAX serves as both input and output.
CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(RAX),
Location::RegisterLocation(RAX));
return;
}
@@ -5478,6 +5477,17 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
locations->SetInAt(0, Location::RequiresRegister());
}
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadClass::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the type resolution and/or initialization to save everything.
+ // Custom calling convention: RAX serves as both input and output.
+ RegisterSet caller_saves = RegisterSet::Empty();
+ caller_saves.Add(Location::RegisterLocation(RAX));
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file,
@@ -5553,7 +5563,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S
Label* fixup_label =
codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass());
// /* GcRoot<mirror::Class> */ out = *address
- GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option);
break;
}
default:
@@ -5629,7 +5639,7 @@ void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
locations->SetOut(Location::RequiresRegister());
if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
- // Rely on the pResolveString and/or marking to save everything.
+ // Rely on the pResolveString to save everything.
// Custom calling convention: RAX serves as both input and output.
RegisterSet caller_saves = RegisterSet::Empty();
caller_saves.Add(Location::RegisterLocation(RAX));
@@ -6501,9 +6511,10 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
//
- // root = *address;
- // if (Thread::Current()->GetIsGcMarking()) {
- // root = ReadBarrier::Mark(root)
+ // root = obj.field;
+ // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg()
+ // if (temp != null) {
+ // root = temp(root)
// }
// /* GcRoot<mirror::Object> */ root = *address
@@ -6524,9 +6535,11 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(
instruction, root, /* unpoison_ref_before_marking */ false);
codegen_->AddSlowPath(slow_path);
- __ gs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86_64PointerSize>().Int32Value(),
- /* no_rip */ true),
- Immediate(0));
+ // Test the `Thread::Current()->pReadBarrierMarkReg ## root.reg()` entrypoint.
+ const int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(root.reg());
+ __ gs()->cmpl(Address::Absolute(entry_point_offset, /* no_rip */ true), Immediate(0));
+ // The entrypoint is null when the GC is not marking.
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
} else {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index f0afccb782..2dd5fefc80 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -85,22 +85,20 @@ void HInliner::Run() {
HInvoke* call = instruction->AsInvoke();
// As long as the call is not intrinsified, it is worth trying to inline.
if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) {
- // We use the original invoke type to ensure the resolution of the called method
- // works properly.
- if (!TryInline(call)) {
- if (kIsDebugBuild && IsCompilingWithCoreImage()) {
- std::string callee_name =
- outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex());
- bool should_inline = callee_name.find("$inline$") != std::string::npos;
- CHECK(!should_inline) << "Could not inline " << callee_name;
+ if (kIsDebugBuild && IsCompilingWithCoreImage()) {
+ // Debugging case: directives in method names control or assert on inlining.
+ std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod(
+ call->GetDexMethodIndex(), /* with_signature */ false);
+ // Tests prevent inlining by having $noinline$ in their method names.
+ if (callee_name.find("$noinline$") == std::string::npos) {
+ if (!TryInline(call)) {
+ bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
+ CHECK(!should_have_inlined) << "Could not inline " << callee_name;
+ }
}
} else {
- if (kIsDebugBuild && IsCompilingWithCoreImage()) {
- std::string callee_name =
- outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex());
- bool must_not_inline = callee_name.find("$noinline$") != std::string::npos;
- CHECK(!must_not_inline) << "Should not have inlined " << callee_name;
- }
+ // Normal case: try to inline.
+ TryInline(call);
}
}
instruction = next;
@@ -380,7 +378,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
soa.Self(),
class_linker->GetClassRoot(ClassLinker::kClassArrayClass),
InlineCache::kIndividualCacheSize));
- if (inline_cache.Get() == nullptr) {
+ if (inline_cache == nullptr) {
// We got an OOME. Just clear the exception, and don't inline.
DCHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index a1c391f455..3374e42955 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -680,7 +680,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod(
method_idx, dex_compilation_unit_->GetDexCache(), class_loader)));
- if (UNLIKELY(methods_class.Get() == nullptr)) {
+ if (UNLIKELY(methods_class == nullptr)) {
// Clean up any exception left by type resolution.
soa.Self()->ClearException();
return nullptr;
@@ -702,7 +702,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
// Check access. The class linker has a fast path for looking into the dex cache
// and does not check the access if it hits it.
- if (compiling_class.Get() == nullptr) {
+ if (compiling_class == nullptr) {
if (!resolved_method->IsPublic()) {
return nullptr;
}
@@ -718,7 +718,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
// make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
// which require runtime handling.
if (invoke_type == kSuper) {
- if (compiling_class.Get() == nullptr) {
+ if (compiling_class == nullptr) {
// We could not determine the method's class we need to wait until runtime.
DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
@@ -954,7 +954,7 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d
}
// Consider classes we haven't resolved as potentially finalizable.
- bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable();
+ bool finalizable = (klass == nullptr) || klass->IsFinalizable();
AppendInstruction(new (arena_) HNewInstance(
cls,
@@ -972,7 +972,7 @@ static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class)
}
bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const {
- if (cls.Get() == nullptr) {
+ if (cls == nullptr) {
return false;
}
@@ -1292,7 +1292,7 @@ bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) c
// When this happens we cannot establish a direct relation between the current
// class and the outer class, so we return false.
// (Note that this is only used for optimizing invokes and field accesses)
- return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get());
+ return (cls != nullptr) && (outer_class.Get() == cls.Get());
}
void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
@@ -1340,7 +1340,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static,
}
// Check access.
- if (compiling_class.Get() == nullptr) {
+ if (compiling_class == nullptr) {
if (!resolved_field->IsPublic()) {
return nullptr;
}
@@ -1612,7 +1612,7 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object,
static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (cls.Get() == nullptr) {
+ if (cls == nullptr) {
return TypeCheckKind::kUnresolvedCheck;
} else if (cls->IsInterface()) {
return TypeCheckKind::kInterfaceCheck;
@@ -1643,7 +1643,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint3
soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_));
bool needs_access_check = true;
- if (klass.Get() != nullptr) {
+ if (klass != nullptr) {
if (klass->IsPublic()) {
needs_access_check = false;
} else {
@@ -1679,7 +1679,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index,
type_index,
*actual_dex_file,
klass,
- klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()),
+ klass != nullptr && (klass.Get() == GetOutermostCompilingClass()),
dex_pc,
needs_access_check);
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index bbf826ce7e..86e54294ae 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -23,7 +23,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "intrinsics.h"
#include "mirror/array-inl.h"
-#include "mirror/string.h"
+#include "mirror/string-inl.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
@@ -115,13 +115,18 @@ class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
MoveArguments(invoke_, codegen);
- if (invoke_->IsInvokeStaticOrDirect()) {
- codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
- LocationFrom(kArtMethodRegister));
- } else {
- codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
+ {
+ // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there
+ // are no pools emitted.
+ vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
+ if (invoke_->IsInvokeStaticOrDirect()) {
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+ LocationFrom(kArtMethodRegister));
+ } else {
+ codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
+ }
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
}
- codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
// Copy the result back to the expected output.
Location out = invoke_->GetLocations()->Out();
@@ -980,11 +985,12 @@ void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke)
CreateIntIntIntIntToVoid(arena_, invoke);
}
-static void GenUnsafePut(LocationSummary* locations,
+static void GenUnsafePut(HInvoke* invoke,
Primitive::Type type,
bool is_volatile,
bool is_ordered,
CodeGeneratorARM64* codegen) {
+ LocationSummary* locations = invoke->GetLocations();
MacroAssembler* masm = codegen->GetVIXLAssembler();
Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
@@ -1007,7 +1013,7 @@ static void GenUnsafePut(LocationSummary* locations,
}
if (is_volatile || is_ordered) {
- codegen->StoreRelease(type, source, mem_op);
+ codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false);
} else {
codegen->Store(type, source, mem_op);
}
@@ -1020,63 +1026,63 @@ static void GenUnsafePut(LocationSummary* locations,
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimInt,
/* is_volatile */ false,
/* is_ordered */ false,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimInt,
/* is_volatile */ false,
/* is_ordered */ true,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimInt,
/* is_volatile */ true,
/* is_ordered */ false,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimNot,
/* is_volatile */ false,
/* is_ordered */ false,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimNot,
/* is_volatile */ false,
/* is_ordered */ true,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimNot,
/* is_volatile */ true,
/* is_ordered */ false,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimLong,
/* is_volatile */ false,
/* is_ordered */ false,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimLong,
/* is_volatile */ false,
/* is_ordered */ true,
codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
- GenUnsafePut(invoke->GetLocations(),
+ GenUnsafePut(invoke,
Primitive::kPrimLong,
/* is_volatile */ true,
/* is_ordered */ false,
@@ -1444,16 +1450,47 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
}
}
+// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
+// The normal loop plus the pre-header is 9 instructions without string compression and 12
+// instructions with string compression. We can compare up to 8 bytes in 4 instructions
+// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up
+// to 10 instructions for the unrolled loop.
+constexpr size_t kShortConstStringEqualsCutoffInBytes = 32;
+
+static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
+ if (candidate->IsLoadString()) {
+ HLoadString* load_string = candidate->AsLoadString();
+ const DexFile& dex_file = load_string->GetDexFile();
+ return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
+ }
+ return nullptr;
+}
+
void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- // Temporary registers to store lengths of strings and for calculations.
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
+ // For the generic implementation and for long const strings we need a temporary.
+ // We do not need it for short const strings, up to 8 bytes, see code generation below.
+ uint32_t const_string_length = 0u;
+ const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+ if (const_string == nullptr) {
+ const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+ }
+ bool is_compressed =
+ mirror::kUseStringCompression &&
+ const_string != nullptr &&
+ mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+ if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+
+ // TODO: If the String.equals() is used only for an immediately following HIf, we can
+ // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
+ // Then we shall need an extra temporary register instead of the output register.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
@@ -1467,8 +1504,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
UseScratchRegisterScope scratch_scope(masm);
Register temp = scratch_scope.AcquireW();
- Register temp1 = WRegisterFrom(locations->GetTemp(0));
- Register temp2 = WRegisterFrom(locations->GetTemp(1));
+ Register temp1 = scratch_scope.AcquireW();
vixl::aarch64::Label loop;
vixl::aarch64::Label end;
@@ -1504,46 +1540,98 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) {
__ B(&return_false, ne);
}
- // Load `count` fields of this and argument strings.
- __ Ldr(temp, MemOperand(str.X(), count_offset));
- __ Ldr(temp1, MemOperand(arg.X(), count_offset));
- // Check if `count` fields are equal, return false if they're not.
- // Also compares the compression style, if differs return false.
- __ Cmp(temp, temp1);
- __ B(&return_false, ne);
- // Return true if both strings are empty. Even with string compression `count == 0` means empty.
- static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
- "Expecting 0=compressed, 1=uncompressed");
- __ Cbz(temp, &return_true);
+ // Check if one of the inputs is a const string. Do not special-case both strings
+ // being const, such cases should be handled by constant folding if needed.
+ uint32_t const_string_length = 0u;
+ const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+ if (const_string == nullptr) {
+ const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+ if (const_string != nullptr) {
+ std::swap(str, arg); // Make sure the const string is in `str`.
+ }
+ }
+ bool is_compressed =
+ mirror::kUseStringCompression &&
+ const_string != nullptr &&
+ mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+
+ if (const_string != nullptr) {
+ // Load `count` field of the argument string and check if it matches the const string.
+ // Also compares the compression style, if differs return false.
+ __ Ldr(temp, MemOperand(arg.X(), count_offset));
+ __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+ __ B(&return_false, ne);
+ } else {
+ // Load `count` fields of this and argument strings.
+ __ Ldr(temp, MemOperand(str.X(), count_offset));
+ __ Ldr(temp1, MemOperand(arg.X(), count_offset));
+ // Check if `count` fields are equal, return false if they're not.
+ // Also compares the compression style, if differs return false.
+ __ Cmp(temp, temp1);
+ __ B(&return_false, ne);
+ }
// Assertions that must hold in order to compare strings 8 bytes at a time.
DCHECK_ALIGNED(value_offset, 8);
static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
- if (mirror::kUseStringCompression) {
- // For string compression, calculate the number of bytes to compare (not chars).
- // This could in theory exceed INT32_MAX, so treat temp as unsigned.
- __ Lsr(temp, temp, 1u); // Extract length.
- __ And(temp1, temp1, Operand(1)); // Extract compression flag.
- __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
- }
-
- // Store offset of string value in preparation for comparison loop
- __ Mov(temp1, value_offset);
+ if (const_string != nullptr &&
+ const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
+ : kShortConstStringEqualsCutoffInBytes / 2u)) {
+ // Load and compare the contents. Though we know the contents of the short const string
+ // at compile time, materializing constants may be more code than loading from memory.
+ int32_t offset = value_offset;
+ size_t remaining_bytes =
+ RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
+ temp = temp.X();
+ temp1 = temp1.X();
+ while (remaining_bytes > 8u) {
+ Register temp2 = XRegisterFrom(locations->GetTemp(0));
+ __ Ldp(temp, temp1, MemOperand(str.X(), offset));
+ __ Ldp(temp2, out, MemOperand(arg.X(), offset));
+ __ Cmp(temp, temp2);
+ __ Ccmp(temp1, out, NoFlag, eq);
+ __ B(&return_false, ne);
+ offset += 2u * sizeof(uint64_t);
+ remaining_bytes -= 2u * sizeof(uint64_t);
+ }
+ if (remaining_bytes != 0u) {
+ __ Ldr(temp, MemOperand(str.X(), offset));
+ __ Ldr(temp1, MemOperand(arg.X(), offset));
+ __ Cmp(temp, temp1);
+ __ B(&return_false, ne);
+ }
+ } else {
+ // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
+ __ Cbz(temp, &return_true);
+
+ if (mirror::kUseStringCompression) {
+ // For string compression, calculate the number of bytes to compare (not chars).
+ // This could in theory exceed INT32_MAX, so treat temp as unsigned.
+ __ And(temp1, temp, Operand(1)); // Extract compression flag.
+ __ Lsr(temp, temp, 1u); // Extract length.
+ __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare.
+ }
- temp1 = temp1.X();
- temp2 = temp2.X();
- // Loop to compare strings 8 bytes at a time starting at the front of the string.
- // Ok to do this because strings are zero-padded to kObjectAlignment.
- __ Bind(&loop);
- __ Ldr(out, MemOperand(str.X(), temp1));
- __ Ldr(temp2, MemOperand(arg.X(), temp1));
- __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
- __ Cmp(out, temp2);
- __ B(&return_false, ne);
- // With string compression, we have compared 8 bytes, otherwise 4 chars.
- __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
- __ B(&loop, hi);
+ // Store offset of string value in preparation for comparison loop
+ __ Mov(temp1, value_offset);
+
+ temp1 = temp1.X();
+ Register temp2 = XRegisterFrom(locations->GetTemp(0));
+ // Loop to compare strings 8 bytes at a time starting at the front of the string.
+ // Ok to do this because strings are zero-padded to kObjectAlignment.
+ __ Bind(&loop);
+ __ Ldr(out, MemOperand(str.X(), temp1));
+ __ Ldr(temp2, MemOperand(arg.X(), temp1));
+ __ Add(temp1, temp1, Operand(sizeof(uint64_t)));
+ __ Cmp(out, temp2);
+ __ B(&return_false, ne);
+ // With string compression, we have compared 8 bytes, otherwise 4 chars.
+ __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags);
+ __ B(&loop, hi);
+ }
// Return true and exit the function.
// If loop does not result in returning false, we return true.
@@ -2825,9 +2913,13 @@ void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) {
}
__ Cbnz(temp0, slow_path->GetEntryLabel());
- // Fast path.
- __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
+ {
+ // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+ vixl::EmissionCheckScope guard(codegen_->GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
+ // Fast path.
+ __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ }
codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
__ Bind(slow_path->GetExitLabel());
}
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 6cf9b83d44..64a68403e9 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2004,31 +2004,48 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) {
__ Lw(temp2, arg, class_offset);
__ Bne(temp1, temp2, &return_false);
- // Load lengths of this and argument strings.
+ // Load `count` fields of this and argument strings.
__ Lw(temp1, str, count_offset);
__ Lw(temp2, arg, count_offset);
- // Check if lengths are equal, return false if they're not.
+ // Check if `count` fields are equal, return false if they're not.
+ // Also compares the compression style, if differs return false.
__ Bne(temp1, temp2, &return_false);
- // Return true if both strings are empty.
+ // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
__ Beqz(temp1, &return_true);
// Don't overwrite input registers
__ Move(TMP, str);
__ Move(temp3, arg);
- // Assertions that must hold in order to compare strings 2 characters at a time.
+ // Assertions that must hold in order to compare strings 4 bytes at a time.
DCHECK_ALIGNED(value_offset, 4);
static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded");
- // Loop to compare strings 2 characters at a time starting at the beginning of the string.
- // Ok to do this because strings are zero-padded.
+ // For string compression, calculate the number of bytes to compare (not chars).
+ if (mirror::kUseStringCompression) {
+ // Extract compression flag.
+ if (IsR2OrNewer()) {
+ __ Ext(temp2, temp1, 0, 1);
+ } else {
+ __ Sll(temp2, temp1, 31);
+ __ Srl(temp2, temp2, 31);
+ }
+ __ Srl(temp1, temp1, 1); // Extract length.
+ __ Sllv(temp1, temp1, temp2); // Double the byte count if uncompressed.
+ }
+
+ // Loop to compare strings 4 bytes at a time starting at the beginning of the string.
+ // Ok to do this because strings are zero-padded to kObjectAlignment.
__ Bind(&loop);
__ Lw(out, TMP, value_offset);
__ Lw(temp2, temp3, value_offset);
__ Bne(out, temp2, &return_false);
__ Addiu(TMP, TMP, 4);
__ Addiu(temp3, temp3, 4);
- __ Addiu(temp1, temp1, -2);
+ // With string compression, we have compared 4 bytes, otherwise 2 chars.
+ __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -4 : -2);
__ Bgtz(temp1, &loop);
// Return true and exit the function.
@@ -2578,6 +2595,30 @@ void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) {
__ Addu(dstPtr, dstPtr, AT);
}
+ if (mirror::kUseStringCompression) {
+ MipsLabel uncompressed_copy, compressed_loop;
+ const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+ // Load count field and extract compression flag.
+ __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset);
+ __ Sll(TMP, TMP, 31);
+
+ // If string is uncompressed, use memcpy() path.
+ __ Bnez(TMP, &uncompressed_copy);
+
+ // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
+ __ Addu(srcPtr, srcObj, srcBegin);
+ __ Bind(&compressed_loop);
+ __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset);
+ __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0);
+ __ Addiu(numChrs, numChrs, -1);
+ __ Addiu(srcPtr, srcPtr, 1);
+ __ Addiu(dstPtr, dstPtr, 2);
+ __ Bnez(numChrs, &compressed_loop);
+
+ __ B(&done);
+ __ Bind(&uncompressed_copy);
+ }
+
// Calculate source address.
__ Addiu(srcPtr, srcObj, value_offset);
if (IsR6()) {
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 00a1fa11bb..3888828722 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1607,31 +1607,42 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringEquals(HInvoke* invoke) {
__ Lw(temp2, arg, class_offset);
__ Bnec(temp1, temp2, &return_false);
- // Load lengths of this and argument strings.
+ // Load `count` fields of this and argument strings.
__ Lw(temp1, str, count_offset);
__ Lw(temp2, arg, count_offset);
- // Check if lengths are equal, return false if they're not.
+ // Check if `count` fields are equal, return false if they're not.
+ // Also compares the compression style, if differs return false.
__ Bnec(temp1, temp2, &return_false);
- // Return true if both strings are empty.
+ // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
__ Beqzc(temp1, &return_true);
// Don't overwrite input registers
__ Move(TMP, str);
__ Move(temp3, arg);
- // Assertions that must hold in order to compare strings 4 characters at a time.
+ // Assertions that must hold in order to compare strings 8 bytes at a time.
DCHECK_ALIGNED(value_offset, 8);
static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
- // Loop to compare strings 4 characters at a time starting at the beginning of the string.
- // Ok to do this because strings are zero-padded to be 8-byte aligned.
+ if (mirror::kUseStringCompression) {
+ // For string compression, calculate the number of bytes to compare (not chars).
+ __ Dext(temp2, temp1, 0, 1); // Extract compression flag.
+ __ Srl(temp1, temp1, 1); // Extract length.
+ __ Sllv(temp1, temp1, temp2); // Double the byte count if uncompressed.
+ }
+
+ // Loop to compare strings 8 bytes at a time starting at the beginning of the string.
+ // Ok to do this because strings are zero-padded to kObjectAlignment.
__ Bind(&loop);
__ Ld(out, TMP, value_offset);
__ Ld(temp2, temp3, value_offset);
__ Bnec(out, temp2, &return_false);
__ Daddiu(TMP, TMP, 8);
__ Daddiu(temp3, temp3, 8);
- __ Addiu(temp1, temp1, -4);
+ // With string compression, we have compared 8 bytes, otherwise 4 chars.
+ __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -8 : -4);
__ Bgtzc(temp1, &loop);
// Return true and exit the function.
@@ -1912,6 +1923,30 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
__ Daddiu(dstPtr, dstObj, data_offset);
__ Dlsa(dstPtr, dstBegin, dstPtr, char_shift);
+ if (mirror::kUseStringCompression) {
+ Mips64Label uncompressed_copy, compressed_loop;
+ const uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
+ // Load count field and extract compression flag.
+ __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset);
+ __ Dext(TMP, TMP, 0, 1);
+
+ // If string is uncompressed, use memcpy() path.
+ __ Bnezc(TMP, &uncompressed_copy);
+
+ // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time.
+ __ Daddu(srcPtr, srcObj, srcBegin);
+ __ Bind(&compressed_loop);
+ __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset);
+ __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0);
+ __ Daddiu(numChrs, numChrs, -1);
+ __ Daddiu(srcPtr, srcPtr, 1);
+ __ Daddiu(dstPtr, dstPtr, 2);
+ __ Bnezc(numChrs, &compressed_loop);
+
+ __ Bc(&done);
+ __ Bind(&uncompressed_copy);
+ }
+
// Calculate source address.
__ Daddiu(srcPtr, srcObj, value_offset);
__ Dlsa(srcPtr, srcBegin, srcPtr, char_shift);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 727ca7d893..8638e346fb 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1214,7 +1214,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
Handle<mirror::ObjectArray<mirror::Object>> roots(
hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(
self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots)));
- if (roots.Get() == nullptr) {
+ if (roots == nullptr) {
// Out of memory, just clear the exception to avoid any Java exception uncaught problems.
DCHECK(self->IsExceptionPending());
self->ClearException();
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index f07f02a719..be400925d5 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -163,7 +163,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class,
if (!compiler_driver->GetSupportBootImageFixup()) {
// compiler_driver_test. Do not sharpen.
desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
- } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass(
+ } else if ((klass != nullptr) && compiler_driver->IsImageClass(
dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
is_in_boot_image = true;
desired_load_kind = codegen->GetCompilerOptions().GetCompilePic()
@@ -175,7 +175,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class,
desired_load_kind = HLoadClass::LoadKind::kBssEntry;
}
} else {
- is_in_boot_image = (klass.Get() != nullptr) &&
+ is_in_boot_image = (klass != nullptr) &&
runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get());
if (runtime->UseJitCompilation()) {
// TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
@@ -183,7 +183,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class,
if (is_in_boot_image) {
// TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
- } else if (klass.Get() != nullptr) {
+ } else if (klass != nullptr) {
desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
} else {
// Class not loaded yet. This happens when the dex code requesting
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f8e01b7537..1bcc8e1ace 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -38,19 +38,14 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_);
current_entry_.register_mask = register_mask;
current_entry_.sp_mask = sp_mask;
- current_entry_.num_dex_registers = num_dex_registers;
current_entry_.inlining_depth = inlining_depth;
- current_entry_.dex_register_locations_start_index = dex_register_locations_.size();
current_entry_.inline_infos_start_index = inline_infos_.size();
- current_entry_.dex_register_map_hash = 0;
- current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound;
current_entry_.stack_mask_index = 0;
- if (num_dex_registers != 0) {
- current_entry_.live_dex_registers_mask =
- ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream);
- } else {
- current_entry_.live_dex_registers_mask = nullptr;
- }
+ current_entry_.dex_register_entry.num_dex_registers = num_dex_registers;
+ current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size();
+ current_entry_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0)
+ ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream)
+ : nullptr;
if (sp_mask != nullptr) {
stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
@@ -65,7 +60,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
}
void StackMapStream::EndStackMapEntry() {
- current_entry_.same_dex_register_map_as_ = FindEntryWithTheSameDexMap();
+ current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry);
stack_maps_.push_back(current_entry_);
current_entry_ = StackMapEntry();
}
@@ -91,19 +86,15 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t
dex_register_locations_.push_back(index);
location_catalog_entries_indices_.Insert(std::make_pair(location, index));
}
-
- if (in_inline_frame_) {
- // TODO: Support sharing DexRegisterMap across InlineInfo.
- DCHECK_LT(current_dex_register_, current_inline_info_.num_dex_registers);
- current_inline_info_.live_dex_registers_mask->SetBit(current_dex_register_);
- } else {
- DCHECK_LT(current_dex_register_, current_entry_.num_dex_registers);
- current_entry_.live_dex_registers_mask->SetBit(current_dex_register_);
- current_entry_.dex_register_map_hash += (1 <<
- (current_dex_register_ % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte)));
- current_entry_.dex_register_map_hash += static_cast<uint32_t>(value);
- current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind);
- }
+ DexRegisterMapEntry* const entry = in_inline_frame_
+ ? &current_inline_info_.dex_register_entry
+ : &current_entry_.dex_register_entry;
+ DCHECK_LT(current_dex_register_, entry->num_dex_registers);
+ entry->live_dex_registers_mask->SetBit(current_dex_register_);
+ entry->hash += (1 <<
+ (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte)));
+ entry->hash += static_cast<uint32_t>(value);
+ entry->hash += static_cast<uint32_t>(kind);
}
current_dex_register_++;
}
@@ -124,20 +115,19 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method,
current_inline_info_.method_index = method->GetDexMethodIndexUnchecked();
}
current_inline_info_.dex_pc = dex_pc;
- current_inline_info_.num_dex_registers = num_dex_registers;
- current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size();
- if (num_dex_registers != 0) {
- current_inline_info_.live_dex_registers_mask =
- ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream);
- } else {
- current_inline_info_.live_dex_registers_mask = nullptr;
- }
+ current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers;
+ current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size();
+ current_inline_info_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0)
+ ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream)
+ : nullptr;
current_dex_register_ = 0;
}
void StackMapStream::EndInlineInfoEntry() {
+ current_inline_info_.dex_register_map_index =
+ AddDexRegisterMapEntry(current_inline_info_.dex_register_entry);
DCHECK(in_inline_frame_);
- DCHECK_EQ(current_dex_register_, current_inline_info_.num_dex_registers)
+ DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers)
<< "Inline information contains less registers than expected";
in_inline_frame_ = false;
inline_infos_.push_back(current_inline_info_);
@@ -193,8 +183,7 @@ size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const {
return size;
}
-size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers,
- const BitVector* live_dex_registers_mask) const {
+size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const {
// For num_dex_registers == 0u live_dex_registers_mask may be null.
if (num_dex_registers == 0u) {
return 0u; // No register map will be emitted.
@@ -208,8 +197,7 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers,
// Compute the size of the set of live Dex register entries.
size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits();
size_t map_entries_size_in_bits =
- DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.size())
- * number_of_live_dex_registers;
+ DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers;
size_t map_entries_size_in_bytes =
RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte;
size += map_entries_size_in_bytes;
@@ -218,18 +206,8 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers,
size_t StackMapStream::ComputeDexRegisterMapsSize() const {
size_t size = 0;
- size_t inline_info_index = 0;
- for (const StackMapEntry& entry : stack_maps_) {
- if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) {
- size += ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask);
- } else {
- // Entries with the same dex map will have the same offset.
- }
- for (size_t j = 0; j < entry.inlining_depth; ++j) {
- InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
- size += ComputeDexRegisterMapSize(inline_entry.num_dex_registers,
- inline_entry.live_dex_registers_mask);
- }
+ for (const DexRegisterMapEntry& entry : dex_register_entries_) {
+ size += entry.ComputeSize(location_catalog_entries_.size());
}
return size;
}
@@ -264,6 +242,30 @@ void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes);
}
+size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry,
+ size_t* current_offset,
+ MemoryRegion dex_register_locations_region) {
+ DCHECK(current_offset != nullptr);
+ if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
+ // No dex register map needed.
+ return StackMap::kNoDexRegisterMap;
+ }
+ if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) {
+ // Not already copied, need to copy and and assign an offset.
+ entry.offset = *current_offset;
+ const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size());
+ DexRegisterMap dex_register_map(
+ dex_register_locations_region.Subregion(entry.offset, entry_size));
+ *current_offset += entry_size;
+ // Fill in the map since it was just added.
+ FillInDexRegisterMap(dex_register_map,
+ entry.num_dex_registers,
+ *entry.live_dex_registers_mask,
+ entry.locations_start_index);
+ }
+ return entry.offset;
+}
+
void StackMapStream::FillIn(MemoryRegion region) {
DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn";
@@ -311,35 +313,10 @@ void StackMapStream::FillIn(MemoryRegion region) {
stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index);
stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index);
- if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
- // No dex map available.
- stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, StackMap::kNoDexRegisterMap);
- } else {
- // Search for an entry with the same dex map.
- if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) {
- // If we have a hit reuse the offset.
- stack_map.SetDexRegisterMapOffset(
- encoding.stack_map.encoding,
- code_info.GetStackMapAt(entry.same_dex_register_map_as_, encoding)
- .GetDexRegisterMapOffset(encoding.stack_map.encoding));
- } else {
- // New dex registers maps should be added to the stack map.
- MemoryRegion register_region = dex_register_locations_region.Subregion(
- next_dex_register_map_offset,
- ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask));
- next_dex_register_map_offset += register_region.size();
- DexRegisterMap dex_register_map(register_region);
- stack_map.SetDexRegisterMapOffset(
- encoding.stack_map.encoding,
- register_region.begin() - dex_register_locations_region.begin());
-
- // Set the dex register location.
- FillInDexRegisterMap(dex_register_map,
- entry.num_dex_registers,
- *entry.live_dex_registers_mask,
- entry.dex_register_locations_start_index);
- }
- }
+ size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index],
+ &next_dex_register_map_offset,
+ dex_register_locations_region);
+ stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset);
// Set the inlining info.
if (entry.inlining_depth != 0) {
@@ -371,29 +348,13 @@ void StackMapStream::FillIn(MemoryRegion region) {
inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1);
}
inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc);
- if (inline_entry.num_dex_registers == 0) {
- // No dex map available.
- inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding,
- depth,
- StackMap::kNoDexRegisterMap);
- DCHECK(inline_entry.live_dex_registers_mask == nullptr);
- } else {
- MemoryRegion register_region = dex_register_locations_region.Subregion(
- next_dex_register_map_offset,
- ComputeDexRegisterMapSize(inline_entry.num_dex_registers,
- inline_entry.live_dex_registers_mask));
- next_dex_register_map_offset += register_region.size();
- DexRegisterMap dex_register_map(register_region);
- inline_info.SetDexRegisterMapOffsetAtDepth(
- encoding.inline_info.encoding,
- depth,
- register_region.begin() - dex_register_locations_region.begin());
-
- FillInDexRegisterMap(dex_register_map,
- inline_entry.num_dex_registers,
- *inline_entry.live_dex_registers_mask,
- inline_entry.dex_register_locations_start_index);
- }
+ size_t dex_register_map_offset = MaybeCopyDexRegisterMap(
+ dex_register_entries_[inline_entry.dex_register_map_index],
+ &next_dex_register_map_offset,
+ dex_register_locations_region);
+ inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding,
+ depth,
+ dex_register_map_offset);
}
} else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo);
@@ -448,34 +409,31 @@ void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map,
}
}
-size_t StackMapStream::FindEntryWithTheSameDexMap() {
- size_t current_entry_index = stack_maps_.size();
- auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash);
+size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) {
+ const size_t current_entry_index = dex_register_entries_.size();
+ auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash);
if (entries_it == dex_map_hash_to_stack_map_indices_.end()) {
// We don't have a perfect hash functions so we need a list to collect all stack maps
// which might have the same dex register map.
ArenaVector<uint32_t> stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream));
stack_map_indices.push_back(current_entry_index);
- dex_map_hash_to_stack_map_indices_.Put(current_entry_.dex_register_map_hash,
- std::move(stack_map_indices));
- return kNoSameDexMapFound;
- }
-
- // We might have collisions, so we need to check whether or not we really have a match.
- for (uint32_t test_entry_index : entries_it->second) {
- if (HaveTheSameDexMaps(GetStackMap(test_entry_index), current_entry_)) {
- return test_entry_index;
+ dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices));
+ } else {
+ // We might have collisions, so we need to check whether or not we really have a match.
+ for (uint32_t test_entry_index : entries_it->second) {
+ if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) {
+ return test_entry_index;
+ }
}
+ entries_it->second.push_back(current_entry_index);
}
- entries_it->second.push_back(current_entry_index);
- return kNoSameDexMapFound;
+ dex_register_entries_.push_back(entry);
+ return current_entry_index;
}
-bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const {
- if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) {
- return true;
- }
- if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) {
+bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a,
+ const DexRegisterMapEntry& b) const {
+ if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) {
return false;
}
if (a.num_dex_registers != b.num_dex_registers) {
@@ -489,12 +447,12 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn
}
size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits();
DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size());
- DCHECK_LE(a.dex_register_locations_start_index,
+ DCHECK_LE(a.locations_start_index,
dex_register_locations_.size() - number_of_live_dex_registers);
- DCHECK_LE(b.dex_register_locations_start_index,
+ DCHECK_LE(b.locations_start_index,
dex_register_locations_.size() - number_of_live_dex_registers);
- auto a_begin = dex_register_locations_.begin() + a.dex_register_locations_start_index;
- auto b_begin = dex_register_locations_.begin() + b.dex_register_locations_start_index;
+ auto a_begin = dex_register_locations_.begin() + a.locations_start_index;
+ auto b_begin = dex_register_locations_.begin() + b.locations_start_index;
if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) {
return false;
}
@@ -597,10 +555,10 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const {
CheckDexRegisterMap(code_info,
code_info.GetDexRegisterMapOf(
- stack_map, encoding, entry.num_dex_registers),
- entry.num_dex_registers,
- entry.live_dex_registers_mask,
- entry.dex_register_locations_start_index);
+ stack_map, encoding, entry.dex_register_entry.num_dex_registers),
+ entry.dex_register_entry.num_dex_registers,
+ entry.dex_register_entry.live_dex_registers_mask,
+ entry.dex_register_entry.locations_start_index);
// Check inline info.
DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0));
@@ -623,10 +581,13 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const {
CheckDexRegisterMap(code_info,
code_info.GetDexRegisterMapAtDepth(
- d, inline_info, encoding, inline_entry.num_dex_registers),
- inline_entry.num_dex_registers,
- inline_entry.live_dex_registers_mask,
- inline_entry.dex_register_locations_start_index);
+ d,
+ inline_info,
+ encoding,
+ inline_entry.dex_register_entry.num_dex_registers),
+ inline_entry.dex_register_entry.num_dex_registers,
+ inline_entry.dex_register_entry.live_dex_registers_mask,
+ inline_entry.dex_register_entry.locations_start_index);
}
}
}
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 08c1d3e3c0..bba3d51e62 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -70,6 +70,7 @@ class StackMapStream : public ValueObject {
inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)),
stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
register_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
+ dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)),
stack_mask_max_(-1),
dex_pc_max_(0),
register_mask_max_(0),
@@ -89,30 +90,42 @@ class StackMapStream : public ValueObject {
code_info_encoding_.reserve(16);
}
+ // A dex register map entry for a single stack map entry, contains what registers are live as
+ // well as indices into the location catalog.
+ class DexRegisterMapEntry {
+ public:
+ static const size_t kOffsetUnassigned = -1;
+
+ BitVector* live_dex_registers_mask;
+ uint32_t num_dex_registers;
+ size_t locations_start_index;
+ // Computed fields
+ size_t hash = 0;
+ size_t offset = kOffsetUnassigned;
+
+ size_t ComputeSize(size_t catalog_size) const;
+ };
+
// See runtime/stack_map.h to know what these fields contain.
struct StackMapEntry {
uint32_t dex_pc;
CodeOffset native_pc_code_offset;
uint32_t register_mask;
BitVector* sp_mask;
- uint32_t num_dex_registers;
uint8_t inlining_depth;
- size_t dex_register_locations_start_index;
size_t inline_infos_start_index;
- BitVector* live_dex_registers_mask;
- uint32_t dex_register_map_hash;
- size_t same_dex_register_map_as_;
uint32_t stack_mask_index;
uint32_t register_mask_index;
+ DexRegisterMapEntry dex_register_entry;
+ size_t dex_register_map_index;
};
struct InlineInfoEntry {
uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods.
ArtMethod* method;
uint32_t method_index;
- uint32_t num_dex_registers;
- BitVector* live_dex_registers_mask;
- size_t dex_register_locations_start_index;
+ DexRegisterMapEntry dex_register_entry;
+ size_t dex_register_map_index;
};
void BeginStackMapEntry(uint32_t dex_pc,
@@ -140,7 +153,8 @@ class StackMapStream : public ValueObject {
}
void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) {
- stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_);
+ stack_maps_[i].native_pc_code_offset =
+ CodeOffset::FromOffset(native_pc_offset, instruction_set_);
}
// Prepares the stream to fill in a memory region. Must be called before FillIn.
@@ -150,8 +164,6 @@ class StackMapStream : public ValueObject {
private:
size_t ComputeDexRegisterLocationCatalogSize() const;
- size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers,
- const BitVector* live_dex_registers_mask) const;
size_t ComputeDexRegisterMapsSize() const;
void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding,
size_t dex_register_maps_bytes);
@@ -164,15 +176,24 @@ class StackMapStream : public ValueObject {
// Returns the number of unique register masks.
size_t PrepareRegisterMasks();
- // Returns the index of an entry with the same dex register map as the current_entry,
- // or kNoSameDexMapFound if no such entry exists.
- size_t FindEntryWithTheSameDexMap();
- bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const;
+ // Deduplicate entry if possible and return the corresponding index into dex_register_entries_
+ // array. If entry is not a duplicate, a new entry is added to dex_register_entries_.
+ size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry);
+
+ // Return true if the two dex register map entries are equal.
+ bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const;
+
+ // Fill in the corresponding entries of a register map.
void FillInDexRegisterMap(DexRegisterMap dex_register_map,
uint32_t num_dex_registers,
const BitVector& live_dex_registers_mask,
uint32_t start_index_in_dex_register_locations) const;
+ // Returns the offset for the dex register inside of the dex register location region. See FillIn.
+ // Only copies the dex register map if the offset for the entry is not already assigned.
+ size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry,
+ size_t* current_offset,
+ MemoryRegion dex_register_locations_region);
void CheckDexRegisterMap(const CodeInfo& code_info,
const DexRegisterMap& dex_register_map,
size_t num_dex_registers,
@@ -199,6 +220,7 @@ class StackMapStream : public ValueObject {
ArenaVector<InlineInfoEntry> inline_infos_;
ArenaVector<uint8_t> stack_masks_;
ArenaVector<uint32_t> register_masks_;
+ ArenaVector<DexRegisterMapEntry> dex_register_entries_;
int stack_mask_max_;
uint32_t dex_pc_max_;
uint32_t register_mask_max_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index bd0aa6dea7..041695187b 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -410,6 +410,100 @@ TEST(StackMapTest, Test2) {
}
}
+TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ StackMapStream stream(&arena, kRuntimeISA);
+ ArtMethod art_method;
+
+ ArenaBitVector sp_mask1(&arena, 0, true);
+ sp_mask1.SetBit(2);
+ sp_mask1.SetBit(4);
+ const size_t number_of_dex_registers = 2;
+ const size_t number_of_dex_registers_in_inline_info = 2;
+ stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1);
+ stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
+ stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info);
+ stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
+ stream.EndInlineInfoEntry();
+ stream.EndStackMapEntry();
+
+ size_t size = stream.PrepareForFillIn();
+ void* memory = arena.Alloc(size, kArenaAllocMisc);
+ MemoryRegion region(memory, size);
+ stream.FillIn(region);
+
+ CodeInfo code_info(region);
+ CodeInfoEncoding encoding = code_info.ExtractEncoding();
+ ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding));
+
+ uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding);
+ ASSERT_EQ(2u, number_of_catalog_entries);
+ DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
+ // The Dex register location catalog contains:
+ // - one 1-byte short Dex register locations, and
+ // - one 5-byte large Dex register location.
+ const size_t expected_location_catalog_size = 1u + 5u;
+ ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
+
+ // First stack map.
+ {
+ StackMap stack_map = code_info.GetStackMapAt(0, encoding);
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
+ ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA));
+ ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map));
+
+ ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1));
+
+ ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding));
+ DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers));
+ ASSERT_TRUE(map.IsDexRegisterLive(0));
+ ASSERT_TRUE(map.IsDexRegisterLive(1));
+ ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
+ // The Dex register map contains:
+ // - one 1-byte live bit mask, and
+ // - one 1-byte set of location catalog entry indices composed of two 2-bit values.
+ size_t expected_map_size = 1u + 1u;
+ ASSERT_EQ(expected_map_size, map.Size());
+
+ ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstant,
+ map.GetLocationKind(1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kInStack,
+ map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstantLargeValue,
+ map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding));
+
+ const size_t index0 =
+ map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries);
+ const size_t index1 =
+ map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries);
+ ASSERT_EQ(0u, index0);
+ ASSERT_EQ(1u, index1);
+ DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0);
+ DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1);
+ ASSERT_EQ(Kind::kInStack, location0.GetKind());
+ ASSERT_EQ(Kind::kConstant, location1.GetKind());
+ ASSERT_EQ(Kind::kInStack, location0.GetInternalKind());
+ ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind());
+ ASSERT_EQ(0, location0.GetValue());
+ ASSERT_EQ(-2, location1.GetValue());
+
+ // Test that the inline info dex register map deduplicated to the same offset as the stack map
+ // one.
+ ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding));
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0),
+ stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding));
+ }
+}
+
TEST(StackMapTest, TestNonLiveDexRegisters) {
ArenaPool pool;
ArenaAllocator arena(&pool);
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index a24d49e08d..5a466e1d5d 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -783,6 +783,79 @@ void X86Assembler::divpd(XmmRegister dst, XmmRegister src) {
}
+void X86Assembler::movdqa(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x6F);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::movdqa(XmmRegister dst, const Address& src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x6F);
+ EmitOperand(dst, src);
+}
+
+
+void X86Assembler::movdqu(XmmRegister dst, const Address& src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitUint8(0x0F);
+ EmitUint8(0x6F);
+ EmitOperand(dst, src);
+}
+
+
+void X86Assembler::movdqa(const Address& dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x7F);
+ EmitOperand(src, dst);
+}
+
+
+void X86Assembler::movdqu(const Address& dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitUint8(0x0F);
+ EmitUint8(0x7F);
+ EmitOperand(src, dst);
+}
+
+
+void X86Assembler::paddd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xFE);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xFA);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pmulld(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x40);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF3);
@@ -990,10 +1063,27 @@ void X86Assembler::xorpd(XmmRegister dst, XmmRegister src) {
}
-void X86Assembler::andps(XmmRegister dst, XmmRegister src) {
+void X86Assembler::xorps(XmmRegister dst, const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
- EmitUint8(0x54);
+ EmitUint8(0x57);
+ EmitOperand(dst, src);
+}
+
+
+void X86Assembler::xorps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0x57);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pxor(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xEF);
EmitXmmRegisterOperand(dst, src);
}
@@ -1007,53 +1097,63 @@ void X86Assembler::andpd(XmmRegister dst, XmmRegister src) {
}
-void X86Assembler::orpd(XmmRegister dst, XmmRegister src) {
+void X86Assembler::andpd(XmmRegister dst, const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
EmitUint8(0x0F);
- EmitUint8(0x56);
+ EmitUint8(0x54);
+ EmitOperand(dst, src);
+}
+
+
+void X86Assembler::andps(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0x54);
EmitXmmRegisterOperand(dst, src);
}
-void X86Assembler::xorps(XmmRegister dst, const Address& src) {
+void X86Assembler::andps(XmmRegister dst, const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
- EmitUint8(0x57);
+ EmitUint8(0x54);
EmitOperand(dst, src);
}
-void X86Assembler::orps(XmmRegister dst, XmmRegister src) {
+void X86Assembler::pand(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
EmitUint8(0x0F);
- EmitUint8(0x56);
+ EmitUint8(0xDB);
EmitXmmRegisterOperand(dst, src);
}
-void X86Assembler::xorps(XmmRegister dst, XmmRegister src) {
+void X86Assembler::orpd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
EmitUint8(0x0F);
- EmitUint8(0x57);
+ EmitUint8(0x56);
EmitXmmRegisterOperand(dst, src);
}
-void X86Assembler::andps(XmmRegister dst, const Address& src) {
+void X86Assembler::orps(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
- EmitUint8(0x54);
- EmitOperand(dst, src);
+ EmitUint8(0x56);
+ EmitXmmRegisterOperand(dst, src);
}
-void X86Assembler::andpd(XmmRegister dst, const Address& src) {
+void X86Assembler::por(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
EmitUint8(0x0F);
- EmitUint8(0x54);
- EmitOperand(dst, src);
+ EmitUint8(0xEB);
+ EmitXmmRegisterOperand(dst, src);
}
@@ -1076,6 +1176,16 @@ void X86Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate& imm
}
+void X86Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x70);
+ EmitXmmRegisterOperand(dst, src);
+ EmitUint8(imm.value());
+}
+
+
void X86Assembler::fldl(const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xDD);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 4056ca67fb..4343e2e734 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -430,6 +430,16 @@ class X86Assembler FINAL : public Assembler {
void mulpd(XmmRegister dst, XmmRegister src);
void divpd(XmmRegister dst, XmmRegister src);
+ void movdqa(XmmRegister dst, XmmRegister src); // move
+ void movdqa(XmmRegister dst, const Address& src); // load aligned
+ void movdqu(XmmRegister dst, const Address& src); // load unaligned
+ void movdqa(const Address& dst, XmmRegister src); // store aligned
+ void movdqu(const Address& dst, XmmRegister src); // store unaligned
+
+ void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void psubd(XmmRegister dst, XmmRegister src);
+ void pmulld(XmmRegister dst, XmmRegister src);
+
void cvtsi2ss(XmmRegister dst, Register src);
void cvtsi2sd(XmmRegister dst, Register src);
@@ -463,17 +473,21 @@ class X86Assembler FINAL : public Assembler {
void xorpd(XmmRegister dst, XmmRegister src);
void xorps(XmmRegister dst, const Address& src);
void xorps(XmmRegister dst, XmmRegister src);
+ void pxor(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void andpd(XmmRegister dst, XmmRegister src);
void andpd(XmmRegister dst, const Address& src);
void andps(XmmRegister dst, XmmRegister src);
void andps(XmmRegister dst, const Address& src);
+ void pand(XmmRegister dst, XmmRegister src); // no addr variant (for now)
- void orpd(XmmRegister dst, XmmRegister src);
+ void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void orps(XmmRegister dst, XmmRegister src);
+ void por(XmmRegister dst, XmmRegister src);
void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm);
void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm);
+ void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm);
void flds(const Address& src);
void fstps(const Address& dst);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 1768d8b715..c6ab893aea 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -467,6 +467,28 @@ TEST_F(AssemblerX86Test, MovupdAddr) {
DriverStr(expected, "movupd_address");
}
+TEST_F(AssemblerX86Test, Movdqa) {
+ DriverStr(RepeatFF(&x86::X86Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movdqa");
+}
+
+TEST_F(AssemblerX86Test, MovdqaAddr) {
+ GetAssembler()->movdqa(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4));
+ GetAssembler()->movdqa(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1));
+ const char* expected =
+ "movdqa 0x4(%ESP), %xmm0\n"
+ "movdqa %xmm1, 0x2(%ESP)\n";
+ DriverStr(expected, "movdqa_address");
+}
+
+TEST_F(AssemblerX86Test, MovdquAddr) {
+ GetAssembler()->movdqu(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4));
+ GetAssembler()->movdqu(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1));
+ const char* expected =
+ "movdqu 0x4(%ESP), %xmm0\n"
+ "movdqu %xmm1, 0x2(%ESP)\n";
+ DriverStr(expected, "movdqu_address");
+}
+
TEST_F(AssemblerX86Test, AddPS) {
DriverStr(RepeatFF(&x86::X86Assembler::addps, "addps %{reg2}, %{reg1}"), "addps");
}
@@ -499,6 +521,54 @@ TEST_F(AssemblerX86Test, DivPD) {
DriverStr(RepeatFF(&x86::X86Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd");
}
+TEST_F(AssemblerX86Test, PAddD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd");
+}
+
+TEST_F(AssemblerX86Test, PSubD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd");
+}
+
+TEST_F(AssemblerX86Test, PMullD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld");
+}
+
+TEST_F(AssemblerX86Test, XorPD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
+}
+
+TEST_F(AssemblerX86Test, XorPS) {
+ DriverStr(RepeatFF(&x86::X86Assembler::xorps, "xorps %{reg2}, %{reg1}"), "xorps");
+}
+
+TEST_F(AssemblerX86Test, PXor) {
+ DriverStr(RepeatFF(&x86::X86Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor");
+}
+
+TEST_F(AssemblerX86Test, AndPD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd");
+}
+
+TEST_F(AssemblerX86Test, AndPS) {
+ DriverStr(RepeatFF(&x86::X86Assembler::andps, "andps %{reg2}, %{reg1}"), "andps");
+}
+
+TEST_F(AssemblerX86Test, PAnd) {
+ DriverStr(RepeatFF(&x86::X86Assembler::pand, "pand %{reg2}, %{reg1}"), "pand");
+}
+
+TEST_F(AssemblerX86Test, OrPD) {
+ DriverStr(RepeatFF(&x86::X86Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
+}
+
+TEST_F(AssemblerX86Test, OrPS) {
+ DriverStr(RepeatFF(&x86::X86Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
+}
+
+TEST_F(AssemblerX86Test, POr) {
+ DriverStr(RepeatFF(&x86::X86Assembler::por, "por %{reg2}, %{reg1}"), "por");
+}
+
TEST_F(AssemblerX86Test, ShufPS) {
DriverStr(RepeatFFI(&x86::X86Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps");
}
@@ -507,6 +577,10 @@ TEST_F(AssemblerX86Test, ShufPD) {
DriverStr(RepeatFFI(&x86::X86Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd");
}
+TEST_F(AssemblerX86Test, PShufD) {
+ DriverStr(RepeatFFI(&x86::X86Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd");
+}
+
/////////////////
// Near labels //
/////////////////
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index c2c44ab58c..b41be80ae4 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -832,6 +832,87 @@ void X86_64Assembler::divpd(XmmRegister dst, XmmRegister src) {
}
+void X86_64Assembler::movdqa(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x6F);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::movdqa(XmmRegister dst, const Address& src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x6F);
+ EmitOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::movdqu(XmmRegister dst, const Address& src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x6F);
+ EmitOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::movdqa(const Address& dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(src, dst);
+ EmitUint8(0x0F);
+ EmitUint8(0x7F);
+ EmitOperand(src.LowBits(), dst);
+}
+
+
+void X86_64Assembler::movdqu(const Address& dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitOptionalRex32(src, dst);
+ EmitUint8(0x0F);
+ EmitUint8(0x7F);
+ EmitOperand(src.LowBits(), dst);
+}
+
+
+void X86_64Assembler::paddd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xFE);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xFA);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::pmulld(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x38);
+ EmitUint8(0x40);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) {
cvtsi2ss(dst, src, false);
}
@@ -1170,6 +1251,16 @@ void X86_64Assembler::xorps(XmmRegister dst, XmmRegister src) {
}
+void X86_64Assembler::pxor(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xEF);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
void X86_64Assembler::andpd(XmmRegister dst, const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -1196,6 +1287,15 @@ void X86_64Assembler::andps(XmmRegister dst, XmmRegister src) {
EmitXmmRegisterOperand(dst.LowBits(), src);
}
+void X86_64Assembler::pand(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xDB);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -1213,6 +1313,14 @@ void X86_64Assembler::orps(XmmRegister dst, XmmRegister src) {
EmitXmmRegisterOperand(dst.LowBits(), src);
}
+void X86_64Assembler::por(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xEB);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
void X86_64Assembler::shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -1235,6 +1343,17 @@ void X86_64Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate&
}
+void X86_64Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x70);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+ EmitUint8(imm.value());
+}
+
+
void X86_64Assembler::fldl(const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xDD);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index e140b45a00..43ea12a4cb 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -446,6 +446,16 @@ class X86_64Assembler FINAL : public Assembler {
void mulpd(XmmRegister dst, XmmRegister src);
void divpd(XmmRegister dst, XmmRegister src);
+ void movdqa(XmmRegister dst, XmmRegister src); // move
+ void movdqa(XmmRegister dst, const Address& src); // load aligned
+ void movdqu(XmmRegister dst, const Address& src); // load unaligned
+ void movdqa(const Address& dst, XmmRegister src); // store aligned
+ void movdqu(const Address& dst, XmmRegister src); // store unaligned
+
+ void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void psubd(XmmRegister dst, XmmRegister src);
+ void pmulld(XmmRegister dst, XmmRegister src);
+
void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version.
void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit);
void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit);
@@ -487,16 +497,20 @@ class X86_64Assembler FINAL : public Assembler {
void xorpd(XmmRegister dst, XmmRegister src);
void xorps(XmmRegister dst, const Address& src);
void xorps(XmmRegister dst, XmmRegister src);
+ void pxor(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void andpd(XmmRegister dst, const Address& src);
void andpd(XmmRegister dst, XmmRegister src);
- void andps(XmmRegister dst, XmmRegister src);
+ void andps(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void pand(XmmRegister dst, XmmRegister src);
- void orpd(XmmRegister dst, XmmRegister src);
+ void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
void orps(XmmRegister dst, XmmRegister src);
+ void por(XmmRegister dst, XmmRegister src);
void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm);
void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm);
+ void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm);
void flds(const Address& src);
void fstps(const Address& dst);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index efa5cc97ea..aeb1911835 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1034,6 +1034,28 @@ TEST_F(AssemblerX86_64Test, Movsd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd");
}
+TEST_F(AssemblerX86_64Test, Movdqa) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movapd");
+}
+
+TEST_F(AssemblerX86_64Test, MovdqaAddr) {
+ GetAssembler()->movdqa(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4));
+ GetAssembler()->movdqa(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1));
+ const char* expected =
+ "movdqa 0x4(%RSP), %xmm0\n"
+ "movdqa %xmm1, 0x2(%RSP)\n";
+ DriverStr(expected, "movdqa_address");
+}
+
+TEST_F(AssemblerX86_64Test, MovdquAddr) {
+ GetAssembler()->movdqu(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4));
+ GetAssembler()->movdqu(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1));
+ const char* expected =
+ "movdqu 0x4(%RSP), %xmm0\n"
+ "movdqu %xmm1, 0x2(%RSP)\n";
+ DriverStr(expected, "movdqu_address");
+}
+
TEST_F(AssemblerX86_64Test, Movd1) {
DriverStr(RepeatFR(&x86_64::X86_64Assembler::movd, "movd %{reg2}, %{reg1}"), "movd.1");
}
@@ -1106,6 +1128,18 @@ TEST_F(AssemblerX86_64Test, Divpd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd");
}
+TEST_F(AssemblerX86_64Test, Paddd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd");
+}
+
+TEST_F(AssemblerX86_64Test, Psubd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd");
+}
+
+TEST_F(AssemblerX86_64Test, Pmulld) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld");
+}
+
TEST_F(AssemblerX86_64Test, Cvtsi2ss) {
DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss");
}
@@ -1187,6 +1221,10 @@ TEST_F(AssemblerX86_64Test, Xorpd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
}
+TEST_F(AssemblerX86_64Test, Pxor) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor");
+}
+
TEST_F(AssemblerX86_64Test, Andps) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::andps, "andps %{reg2}, %{reg1}"), "andps");
}
@@ -1195,6 +1233,10 @@ TEST_F(AssemblerX86_64Test, Andpd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd");
}
+TEST_F(AssemblerX86_64Test, Pand) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::pand, "pand %{reg2}, %{reg1}"), "pand");
+}
+
TEST_F(AssemblerX86_64Test, Orps) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
}
@@ -1203,6 +1245,10 @@ TEST_F(AssemblerX86_64Test, Orpd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
}
+TEST_F(AssemblerX86_64Test, Por) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::por, "por %{reg2}, %{reg1}"), "por");
+}
+
TEST_F(AssemblerX86_64Test, Shufps) {
DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps");
}
@@ -1211,6 +1257,10 @@ TEST_F(AssemblerX86_64Test, Shufpd) {
DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd");
}
+TEST_F(AssemblerX86_64Test, PShufd) {
+ DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd");
+}
+
TEST_F(AssemblerX86_64Test, UcomissAddress) {
GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(
x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 5fc9972d09..c892b25ed3 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -233,7 +233,7 @@ class VerifierDepsTest : public CommonCompilerTest {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
cls.Assign(class_linker_->FindClass(soa.Self(), descriptor, class_loader_handle));
- if (cls.Get() == nullptr) {
+ if (cls == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
} else if (set.find(class_def.class_idx_) == set.end()) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 192fc270f9..026a567fe0 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -419,12 +419,9 @@ class WatchDog {
} while (false)
public:
- explicit WatchDog(bool is_watch_dog_enabled) {
- is_watch_dog_enabled_ = is_watch_dog_enabled;
- if (!is_watch_dog_enabled_) {
- return;
- }
- shutting_down_ = false;
+ explicit WatchDog(int64_t timeout_in_milliseconds)
+ : timeout_in_milliseconds_(timeout_in_milliseconds),
+ shutting_down_(false) {
const char* reason = "dex2oat watch dog thread startup";
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_init, (&mutex_, nullptr), reason);
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_cond_init, (&cond_, nullptr), reason);
@@ -433,9 +430,6 @@ class WatchDog {
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_attr_destroy, (&attr_), reason);
}
~WatchDog() {
- if (!is_watch_dog_enabled_) {
- return;
- }
const char* reason = "dex2oat watch dog thread shutdown";
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
shutting_down_ = true;
@@ -448,6 +442,23 @@ class WatchDog {
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_destroy, (&mutex_), reason);
}
+ // TODO: tune the multiplier for GC verification, the following is just to make the timeout
+ // large.
+ static constexpr int64_t kWatchdogVerifyMultiplier =
+ kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
+
+ // When setting timeouts, keep in mind that the build server may not be as fast as your
+ // desktop. Debug builds are slower so they have larger timeouts.
+ static constexpr int64_t kWatchdogSlowdownFactor = kIsDebugBuild ? 5U : 1U;
+
+ // 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager
+ // watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort
+ // itself before that watchdog would take down the system server.
+ static constexpr int64_t kWatchDogTimeoutSeconds = kWatchdogSlowdownFactor * (9 * 60 + 30);
+
+ static constexpr int64_t kDefaultWatchdogTimeoutInMS =
+ kWatchdogVerifyMultiplier * kWatchDogTimeoutSeconds * 1000;
+
private:
static void* CallBack(void* arg) {
WatchDog* self = reinterpret_cast<WatchDog*>(arg);
@@ -470,18 +481,15 @@ class WatchDog {
}
void Wait() {
- // TODO: tune the multiplier for GC verification, the following is just to make the timeout
- // large.
- constexpr int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
timespec timeout_ts;
- InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts);
+ InitTimeSpec(true, CLOCK_REALTIME, timeout_in_milliseconds_, 0, &timeout_ts);
const char* reason = "dex2oat watch dog thread waiting";
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
while (!shutting_down_) {
int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &timeout_ts));
if (rc == ETIMEDOUT) {
Fatal(StringPrintf("dex2oat did not finish after %" PRId64 " seconds",
- kWatchDogTimeoutSeconds));
+ timeout_in_milliseconds_/1000));
} else if (rc != 0) {
std::string message(StringPrintf("pthread_cond_timedwait failed: %s",
strerror(errno)));
@@ -491,16 +499,7 @@ class WatchDog {
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_unlock, (&mutex_), reason);
}
- // When setting timeouts, keep in mind that the build server may not be as fast as your desktop.
- // Debug builds are slower so they have larger timeouts.
- static constexpr int64_t kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
-
- // 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager
- // watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort
- // itself before that watchdog would take down the system server.
- static constexpr int64_t kWatchDogTimeoutSeconds = kSlowdownFactor * (9 * 60 + 30);
-
- bool is_watch_dog_enabled_;
+ const int64_t timeout_in_milliseconds_;
bool shutting_down_;
// TODO: Switch to Mutex when we can guarantee it won't prevent shutdown in error cases.
pthread_mutex_t mutex_;
@@ -591,6 +590,7 @@ class Dex2Oat FINAL {
struct ParserOptions {
std::vector<const char*> oat_symbols;
std::string boot_image_filename;
+ int64_t watch_dog_timeout_in_ms = -1;
bool watch_dog_enabled = true;
bool requested_specific_compiler = false;
std::string error_msg;
@@ -919,7 +919,10 @@ class Dex2Oat FINAL {
// Done with usage checks, enable watchdog if requested
if (parser_options->watch_dog_enabled) {
- watchdog_.reset(new WatchDog(true));
+ int64_t timeout = parser_options->watch_dog_timeout_in_ms > 0
+ ? parser_options->watch_dog_timeout_in_ms
+ : WatchDog::kDefaultWatchdogTimeoutInMS;
+ watchdog_.reset(new WatchDog(timeout));
}
// Fill some values into the key-value store for the oat header.
@@ -1150,6 +1153,11 @@ class Dex2Oat FINAL {
parser_options->watch_dog_enabled = true;
} else if (option == "--no-watch-dog") {
parser_options->watch_dog_enabled = false;
+ } else if (option.starts_with("--watchdog-timeout=")) {
+ ParseIntOption(option,
+ "--watchdog-timeout",
+ &parser_options->watch_dog_timeout_in_ms,
+ Usage);
} else if (option.starts_with("-j")) {
ParseJ(option);
} else if (option.starts_with("--image=")) {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index c2275aca95..5dcdd9e69a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -66,7 +66,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
bool success = Dex2Oat(args, &error_msg);
if (expect_success) {
- ASSERT_TRUE(success) << error_msg;
+ ASSERT_TRUE(success) << error_msg << std::endl << output_;
// Verify the odex file was generated as expected.
std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
@@ -231,7 +231,7 @@ class Dex2oatSwapTest : public Dex2oatTest {
}
virtual std::string GetTestDexFileName() {
- return GetDexSrc1();
+ return Dex2oatEnvironmentTest::GetTestDexFileName("VerifierDeps");
}
virtual void CheckResult(bool expect_use) {
@@ -658,4 +658,41 @@ TEST_F(Dex2oatLayoutTest, TestLayout) {
RunTest();
}
+class Dex2oatWatchdogTest : public Dex2oatTest {
+ protected:
+ void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) {
+ std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+ std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+ Copy(GetTestDexFileName(), dex_location);
+
+ std::vector<std::string> copy(extra_args);
+
+ std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
+ copy.push_back("--swap-file=" + swap_location);
+ GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeed,
+ copy,
+ expect_success);
+ }
+
+ std::string GetTestDexFileName() {
+ return GetDexSrc1();
+ }
+};
+
+TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) {
+ // Check with default.
+ RunTest(true);
+
+ // Check with ten minutes.
+ RunTest(true, { "--watchdog-timeout=600000" });
+}
+
+TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) {
+ // Check with ten milliseconds.
+ RunTest(false, { "--watchdog-timeout=10" });
+}
+
} // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index d5776fa61e..5656ddd59c 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -881,26 +881,30 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile,
outSize = snprintf(buf.get(), bufSize, "[obj+%0*x]", width, index);
break;
case Instruction::kIndexMethodAndProtoRef: {
- std::string method("<method?>");
- std::string proto("<proto?>");
- if (index < pDexFile->GetHeader().method_ids_size_) {
- const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
- const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
- const Signature signature = pDexFile->GetMethodSignature(pMethodId);
- const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
- method = android::base::StringPrintf("%s.%s:%s",
- backDescriptor,
- name,
- signature.ToString().c_str());
- }
- if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
- const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
- const Signature signature = pDexFile->GetProtoSignature(protoId);
- proto = signature.ToString();
- }
- outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x",
- method.c_str(), proto.c_str(), width, index, width, secondary_index);
+ std::string method("<method?>");
+ std::string proto("<proto?>");
+ if (index < pDexFile->GetHeader().method_ids_size_) {
+ const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index);
+ const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+ const Signature signature = pDexFile->GetMethodSignature(pMethodId);
+ const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
+ method = android::base::StringPrintf("%s.%s:%s",
+ backDescriptor,
+ name,
+ signature.ToString().c_str());
}
+ if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
+ const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
+ const Signature signature = pDexFile->GetProtoSignature(protoId);
+ proto = signature.ToString();
+ }
+ outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x",
+ method.c_str(), proto.c_str(), width, index, width, secondary_index);
+ break;
+ }
+ case Instruction::kIndexCallSiteRef:
+ // Call site information is too large to detail in disassembly so just output the index.
+ outSize = snprintf(buf.get(), bufSize, "call_site@%0*x", width, index);
break;
// SOME NOT SUPPORTED:
// case Instruction::kIndexVaries:
@@ -1581,6 +1585,198 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) {
free(accessStr);
}
+static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
+ const DexFile::MethodHandleItem& mh = pDexFile->GetMethodHandle(idx);
+ bool is_invoke = false;
+ const char* type;
+ switch (static_cast<DexFile::MethodHandleType>(mh.method_handle_type_)) {
+ case DexFile::MethodHandleType::kStaticPut:
+ type = "put-static";
+ break;
+ case DexFile::MethodHandleType::kStaticGet:
+ type = "get-static";
+ break;
+ case DexFile::MethodHandleType::kInstancePut:
+ type = "put-instance";
+ break;
+ case DexFile::MethodHandleType::kInstanceGet:
+ type = "get-instance";
+ break;
+ case DexFile::MethodHandleType::kInvokeStatic:
+ type = "invoke-static";
+ is_invoke = true;
+ break;
+ case DexFile::MethodHandleType::kInvokeInstance:
+ type = "invoke-instance";
+ is_invoke = true;
+ break;
+ case DexFile::MethodHandleType::kInvokeConstructor:
+ type = "invoke-constructor";
+ is_invoke = true;
+ break;
+ }
+
+ const char* declaring_class;
+ const char* member;
+ std::string member_type;
+ if (is_invoke) {
+ const DexFile::MethodId& method_id = pDexFile->GetMethodId(mh.field_or_method_idx_);
+ declaring_class = pDexFile->GetMethodDeclaringClassDescriptor(method_id);
+ member = pDexFile->GetMethodName(method_id);
+ member_type = pDexFile->GetMethodSignature(method_id).ToString();
+ } else {
+ const DexFile::FieldId& field_id = pDexFile->GetFieldId(mh.field_or_method_idx_);
+ declaring_class = pDexFile->GetFieldDeclaringClassDescriptor(field_id);
+ member = pDexFile->GetFieldName(field_id);
+ member_type = pDexFile->GetFieldTypeDescriptor(field_id);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ fprintf(gOutFile, "Method handle #%u:\n", idx);
+ fprintf(gOutFile, " type : %s\n", type);
+ fprintf(gOutFile, " target : %s %s\n", declaring_class, member);
+ fprintf(gOutFile, " target_type : %s\n", member_type.c_str());
+ } else {
+ fprintf(gOutFile, "<method_handle index=\"%u\"\n", idx);
+ fprintf(gOutFile, " type=\"%s\"\n", type);
+ fprintf(gOutFile, " target_class=\"%s\"\n", declaring_class);
+ fprintf(gOutFile, " target_member=\"%s\"\n", member);
+ fprintf(gOutFile, " target_member_type=");
+ dumpEscapedString(member_type.c_str());
+ fprintf(gOutFile, "\n>\n</method_handle>\n");
+ }
+}
+
+static void dumpCallSite(const DexFile* pDexFile, u4 idx) {
+ const DexFile::CallSiteIdItem& call_site_id = pDexFile->GetCallSiteId(idx);
+ CallSiteArrayValueIterator it(*pDexFile, call_site_id);
+ if (it.Size() < 3) {
+ fprintf(stderr, "ERROR: Call site %u has too few values.\n", idx);
+ return;
+ }
+
+ uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ it.Next();
+ dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
+ const char* method_name = pDexFile->StringDataByIdx(method_name_idx);
+ it.Next();
+ uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx);
+ std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString();
+ it.Next();
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ fprintf(gOutFile, "Call site #%u:\n", idx);
+ fprintf(gOutFile, " link_argument[0] : %u (MethodHandle)\n", method_handle_idx);
+ fprintf(gOutFile, " link_argument[1] : %s (String)\n", method_name);
+ fprintf(gOutFile, " link_argument[2] : %s (MethodType)\n", method_type.c_str());
+ } else {
+ fprintf(gOutFile, "<call_site index=\"%u\">\n", idx);
+ fprintf(gOutFile,
+ "<link_argument index=\"0\" type=\"MethodHandle\" value=\"%u\"/>\n",
+ method_handle_idx);
+ fprintf(gOutFile,
+ "<link_argument index=\"1\" type=\"String\" values=\"%s\"/>\n",
+ method_name);
+ fprintf(gOutFile,
+ "<link_argument index=\"2\" type=\"MethodType\" value=\"%s\"/>\n",
+ method_type.c_str());
+ }
+
+ size_t argument = 3;
+ while (it.HasNext()) {
+ const char* type;
+ std::string value;
+ switch (it.GetValueType()) {
+ case EncodedArrayValueIterator::ValueType::kByte:
+ type = "byte";
+ value = android::base::StringPrintf("%u", it.GetJavaValue().b);
+ break;
+ case EncodedArrayValueIterator::ValueType::kShort:
+ type = "short";
+ value = android::base::StringPrintf("%d", it.GetJavaValue().s);
+ break;
+ case EncodedArrayValueIterator::ValueType::kChar:
+ type = "char";
+ value = android::base::StringPrintf("%u", it.GetJavaValue().c);
+ break;
+ case EncodedArrayValueIterator::ValueType::kInt:
+ type = "int";
+ value = android::base::StringPrintf("%d", it.GetJavaValue().i);
+ break;
+ case EncodedArrayValueIterator::ValueType::kLong:
+ type = "long";
+ value = android::base::StringPrintf("%" PRId64, it.GetJavaValue().j);
+ break;
+ case EncodedArrayValueIterator::ValueType::kFloat:
+ type = "float";
+ value = android::base::StringPrintf("%g", it.GetJavaValue().f);
+ break;
+ case EncodedArrayValueIterator::ValueType::kDouble:
+ type = "double";
+ value = android::base::StringPrintf("%g", it.GetJavaValue().d);
+ break;
+ case EncodedArrayValueIterator::ValueType::kMethodType: {
+ type = "MethodType";
+ uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx);
+ value = pDexFile->GetProtoSignature(proto_id).ToString();
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kMethodHandle:
+ type = "MethodHandle";
+ value = android::base::StringPrintf("%d", it.GetJavaValue().i);
+ break;
+ case EncodedArrayValueIterator::ValueType::kString: {
+ type = "String";
+ dex::StringIndex string_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i);
+ value = pDexFile->StringDataByIdx(string_idx);
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kType: {
+ type = "Class";
+ dex::TypeIndex type_idx = static_cast<dex::TypeIndex>(it.GetJavaValue().i);
+ const DexFile::ClassDef* class_def = pDexFile->FindClassDef(type_idx);
+ value = pDexFile->GetClassDescriptor(*class_def);
+ value = descriptorClassToDot(value.c_str()).get();
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kField:
+ case EncodedArrayValueIterator::ValueType::kMethod:
+ case EncodedArrayValueIterator::ValueType::kEnum:
+ case EncodedArrayValueIterator::ValueType::kArray:
+ case EncodedArrayValueIterator::ValueType::kAnnotation:
+ // Unreachable based on current EncodedArrayValueIterator::Next().
+ UNIMPLEMENTED(FATAL) << " type " << type;
+ UNREACHABLE();
+ break;
+ case EncodedArrayValueIterator::ValueType::kNull:
+ type = "Null";
+ value = "null";
+ break;
+ case EncodedArrayValueIterator::ValueType::kBoolean:
+ type = "boolean";
+ value = it.GetJavaValue().z ? "true" : "false";
+ break;
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ fprintf(gOutFile, " link_argument[%zu] : %s (%s)\n", argument, value.c_str(), type);
+ } else {
+ fprintf(gOutFile, "<link_argument index=\"%zu\" type=\"%s\" value=", argument, type);
+ dumpEscapedString(value.c_str());
+ fprintf(gOutFile, "/>\n");
+ }
+
+ it.Next();
+ argument++;
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ fprintf(gOutFile, "</call_site>\n");
+ }
+}
+
/*
* Dumps the requested sections of the file.
*/
@@ -1612,6 +1808,16 @@ static void processDexFile(const char* fileName,
dumpClass(pDexFile, i, &package);
} // for
+ // Iterate over all method handles.
+ for (u4 i = 0; i < pDexFile->NumMethodHandles(); ++i) {
+ dumpMethodHandle(pDexFile, i);
+ } // for
+
+ // Iterate over all call site ids.
+ for (u4 i = 0; i < pDexFile->NumCallSiteIds(); ++i) {
+ dumpCallSite(pDexFile, i);
+ } // for
+
// Free the last package allocated.
if (package != nullptr) {
fprintf(gOutFile, "</package>\n");
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 9f49ec6f60..ff05733345 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -859,6 +859,22 @@ DISASSEMBLER_ENTRY(cmp,
has_modrm = true;
store = true;
break;
+ case 0x7F:
+ if (prefix[2] == 0x66) {
+ src_reg_file = dst_reg_file = SSE;
+ opcode1 = "movdqa";
+ prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
+ } else if (prefix[0] == 0xF3) {
+ src_reg_file = dst_reg_file = SSE;
+ opcode1 = "movdqu";
+ prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
+ } else {
+ dst_reg_file = MMX;
+ opcode1 = "movq";
+ }
+ store = true;
+ has_modrm = true;
+ break;
case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
opcode1 = "j";
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index ce63e18921..9a3b28b16e 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1473,7 +1473,7 @@ class OatDumper {
Runtime* const runtime = Runtime::Current();
Handle<mirror::DexCache> dex_cache(
hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file, nullptr)));
- CHECK(dex_cache.Get() != nullptr);
+ CHECK(dex_cache != nullptr);
DCHECK(options_.class_loader_ != nullptr);
return verifier::MethodVerifier::VerifyMethodAndDump(
soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_,
@@ -2950,7 +2950,7 @@ class IMTDumper {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
h_klass.Assign(class_linker->FindClass(self, descriptor, h_class_loader));
- if (h_klass.Get() == nullptr) {
+ if (h_klass == nullptr) {
std::cerr << "Warning: could not load " << descriptor << std::endl;
continue;
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 9a73830f99..b9be5f2605 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -688,6 +688,16 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots
orig_dex_cache->FixupResolvedMethodTypes(RelocatedCopyOf(orig_method_types),
RelocatedPointerVisitor(this));
}
+
+ GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites();
+ GcRoot<mirror::CallSite>* relocated_call_sites = RelocatedAddressOfPointer(orig_call_sites);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedCallSitesOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_call_sites)));
+ if (orig_call_sites != nullptr) {
+ orig_dex_cache->FixupResolvedCallSites(RelocatedCopyOf(orig_call_sites),
+ RelocatedPointerVisitor(this));
+ }
}
}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index a6c3cf067b..d395c170bf 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -94,6 +94,54 @@ class ProfileAssistantTest : public CommonRuntimeTest {
std::string error;
return ExecAndReturnCode(argv_str, &error);
}
+
+ bool CreateProfile(std::string class_file_contents, const std::string& filename) {
+ ScratchFile class_names_file;
+ File* file = class_names_file.GetFile();
+ EXPECT_TRUE(file->WriteFully(class_file_contents.c_str(), class_file_contents.length()));
+ EXPECT_EQ(0, file->Flush());
+ EXPECT_TRUE(file->ResetOffset());
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename());
+ argv_str.push_back("--reference-profile-file=" + filename);
+ argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
+ argv_str.push_back("--dex-location=classes.dex");
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
+ return true;
+ }
+
+ bool DumpClasses(const std::string& filename, std::string* file_contents) {
+ ScratchFile class_names_file;
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--dump-classes");
+ argv_str.push_back("--profile-file=" + filename);
+ argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
+ argv_str.push_back("--dex-location=classes.dex");
+ argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(class_names_file)));
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
+ File* file = class_names_file.GetFile();
+ EXPECT_EQ(0, file->Flush());
+ EXPECT_TRUE(file->ResetOffset());
+ int64_t length = file->GetLength();
+ std::unique_ptr<char[]> buf(new char[length]);
+ EXPECT_EQ(file->Read(buf.get(), length, 0), length);
+ *file_contents = std::string(buf.get(), length);
+ return true;
+ }
+
+ bool CreateAndDump(const std::string& input_file_contents, std::string* output_file_contents) {
+ ScratchFile profile_file;
+ EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename()));
+ profile_file.GetFile()->ResetOffset();
+ EXPECT_TRUE(DumpClasses(profile_file.GetFilename(), output_file_contents));
+ return true;
+ }
};
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
@@ -307,4 +355,55 @@ TEST_F(ProfileAssistantTest, TestProfileGeneration) {
ASSERT_TRUE(info.Load(GetFd(profile)));
}
+TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
+ // Class names put here need to be in sorted order.
+ std::vector<std::string> class_names = {
+ "java.lang.Comparable",
+ "java.lang.Math",
+ "java.lang.Object"
+ };
+ std::string input_file_contents;
+ for (std::string& class_name : class_names) {
+ input_file_contents += class_name + std::string("\n");
+ }
+ std::string output_file_contents;
+ ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
+ ASSERT_EQ(output_file_contents, input_file_contents);
+}
+
+TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
+ // Class names put here need to be in sorted order.
+ std::vector<std::string> class_names = {
+ "doesnt.match.this.one",
+ "java.lang.Comparable",
+ "java.lang.Object"
+ };
+ std::string input_file_contents;
+ for (std::string& class_name : class_names) {
+ input_file_contents += class_name + std::string("\n");
+ }
+ std::string output_file_contents;
+ ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
+ std::string expected_contents =
+ class_names[1] + std::string("\n") + class_names[2] + std::string("\n");
+ ASSERT_EQ(output_file_contents, expected_contents);
+}
+
+TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
+ // Class names put here need to be in sorted order.
+ std::vector<std::string> class_names = {
+ "doesnt.match.this.one",
+ "doesnt.match.this.one.either",
+ "nor.this.one"
+ };
+ std::string input_file_contents;
+ for (std::string& class_name : class_names) {
+ input_file_contents += class_name + std::string("\n");
+ }
+ std::string output_file_contents;
+ ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
+ std::string expected_contents("");
+ ASSERT_EQ(output_file_contents, expected_contents);
+}
+
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index b0cbed1ef9..f6b145aa76 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -21,8 +21,11 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <fstream>
#include <iostream>
+#include <set>
#include <string>
+#include <unordered_set>
#include <vector>
#include "android-base/stringprintf.h"
@@ -84,8 +87,10 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --dump-only: dumps the content of the specified profile files");
UsageError(" to standard output (default) in a human readable form.");
UsageError("");
- UsageError(" --dump-output-to-fd=<number>: redirects --dump-info-for output to a file");
- UsageError(" descriptor.");
+ UsageError(" --dump-output-to-fd=<number>: redirects --dump-only output to a file descriptor.");
+ UsageError("");
+ UsageError(" --dump-classes: dumps a sorted list of classes that are in the specified profile");
+ UsageError(" file to standard output (default) in a human readable form.");
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
@@ -103,6 +108,7 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
UsageError(" accepts a file descriptor. Cannot be used together with");
UsageError(" --reference-profile-file.");
+ UsageError("");
UsageError(" --generate-test-profile=<filename>: generates a random profile file for testing.");
UsageError(" --generate-test-profile-num-dex=<number>: number of dex files that should be");
UsageError(" included in the generated profile. Defaults to 20.");
@@ -111,12 +117,15 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --generate-test-profile-class-ratio=<number>: the percentage from the maximum");
UsageError(" number of classes that should be generated. Defaults to 5.");
UsageError("");
+ UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes.");
+ UsageError("");
UsageError("");
UsageError(" --dex-location=<string>: location string to use with corresponding");
UsageError(" apk-fd to find dex files");
UsageError("");
UsageError(" --apk-fd=<number>: file descriptor containing an open APK to");
UsageError(" search for dex files");
+ UsageError(" --apk-=<filename>: an APK to search for dex files");
UsageError("");
exit(EXIT_FAILURE);
@@ -132,6 +141,7 @@ class ProfMan FINAL {
ProfMan() :
reference_profile_file_fd_(kInvalidFd),
dump_only_(false),
+ dump_classes_(false),
dump_output_to_fd_(kInvalidFd),
test_profile_num_dex_(kDefaultTestProfileNumDex),
test_profile_method_ratio_(kDefaultTestProfileMethodRatio),
@@ -164,6 +174,10 @@ class ProfMan FINAL {
}
if (option == "--dump-only") {
dump_only_ = true;
+ } else if (option == "--dump-classes") {
+ dump_classes_ = true;
+ } else if (option.starts_with("--create-profile-from=")) {
+ create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString();
} else if (option.starts_with("--dump-output-to-fd=")) {
ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
} else if (option.starts_with("--profile-file=")) {
@@ -178,6 +192,8 @@ class ProfMan FINAL {
dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString());
} else if (option.starts_with("--apk-fd=")) {
ParseFdForCollection(option, "--apk-fd", &apks_fd_);
+ } else if (option.starts_with("--apk=")) {
+ apk_files_.push_back(option.substr(strlen("--apk=")).ToString());
} else if (option.starts_with("--generate-test-profile=")) {
test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString();
} else if (option.starts_with("--generate-test-profile-num-dex=")) {
@@ -213,14 +229,34 @@ class ProfMan FINAL {
}
return;
}
- // --dump-only may be specified with only --reference-profiles present.
- if (!dump_only_ && !has_profiles) {
+ if (!apk_files_.empty() && !apks_fd_.empty()) {
+ Usage("APK files should not be specified with both --apk-fd and --apk");
+ }
+ if (!create_profile_from_file_.empty()) {
+ if (apk_files_.empty() && apks_fd_.empty()) {
+ Usage("APK files must be specified");
+ }
+ if (dex_locations_.empty()) {
+ Usage("DEX locations must be specified");
+ }
+ if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
+ Usage("Reference profile must be specified with --reference-profile-file or "
+ "--reference-profile-file-fd");
+ }
+ if (has_profiles) {
+ Usage("Profile must be specified with --reference-profile-file or "
+ "--reference-profile-file-fd");
+ }
+ return;
+ }
+ // --dump-only and --dump-classes may be specified with only --reference-profiles present.
+ if (!dump_only_ && !dump_classes_ && !has_profiles) {
Usage("No profile files specified.");
}
if (!profile_files_.empty() && !profile_files_fd_.empty()) {
Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
}
- if (!dump_only_ && !has_reference_profile) {
+ if (!dump_only_ && !dump_classes_ && !has_reference_profile) {
Usage("No reference profile file specified.");
}
if (!reference_profile_file_.empty() && FdIsValid(reference_profile_file_fd_)) {
@@ -248,6 +284,46 @@ class ProfMan FINAL {
return result;
}
+ void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+ bool use_apk_fd_list = !apks_fd_.empty();
+ if (use_apk_fd_list) {
+ CHECK(apk_files_.empty());
+ CHECK_EQ(dex_locations_.size(), apks_fd_.size());
+ } else {
+ CHECK_EQ(dex_locations_.size(), apk_files_.size());
+ CHECK(!apk_files_.empty());
+ }
+ static constexpr bool kVerifyChecksum = true;
+ for (size_t i = 0; i < dex_locations_.size(); ++i) {
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
+ if (use_apk_fd_list) {
+ if (DexFile::OpenZip(apks_fd_[i],
+ dex_locations_[i],
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files_for_location)) {
+ } else {
+ LOG(WARNING) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg;
+ continue;
+ }
+ } else {
+ if (DexFile::Open(apk_files_[i].c_str(),
+ dex_locations_[i],
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files_for_location)) {
+ } else {
+ LOG(WARNING) << "Open failed for '" << dex_locations_[i] << "' " << error_msg;
+ continue;
+ }
+ }
+ for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) {
+ dex_files->emplace_back(std::move(dex_file));
+ }
+ }
+ }
+
int DumpOneProfile(const std::string& banner,
const std::string& filename,
int fd,
@@ -256,13 +332,13 @@ class ProfMan FINAL {
if (!filename.empty()) {
fd = open(filename.c_str(), O_RDWR);
if (fd < 0) {
- std::cerr << "Cannot open " << filename << strerror(errno);
+ LOG(ERROR) << "Cannot open " << filename << strerror(errno);
return -1;
}
}
ProfileCompilationInfo info;
if (!info.Load(fd)) {
- std::cerr << "Cannot load profile info from fd=" << fd << "\n";
+ LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n";
return -1;
}
std::string this_dump = banner + "\n" + info.DumpInfo(dex_files) + "\n";
@@ -281,25 +357,7 @@ class ProfMan FINAL {
// Open apk/zip files and and read dex files.
MemMap::Init(); // for ZipArchive::OpenFromFd
std::vector<std::unique_ptr<const DexFile>> dex_files;
- assert(dex_locations_.size() == apks_fd_.size());
- static constexpr bool kVerifyChecksum = true;
- for (size_t i = 0; i < dex_locations_.size(); ++i) {
- std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
- if (DexFile::OpenZip(apks_fd_[i],
- dex_locations_[i],
- kVerifyChecksum,
- &error_msg,
- &dex_files_for_location)) {
- } else {
- LOG(WARNING) << "OpenFromZip failed for '" << dex_locations_[i] << "' " << error_msg;
- continue;
- }
- for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) {
- dex_files.emplace_back(std::move(dex_file));
- }
- }
-
+ OpenApkFilesFromLocations(&dex_files);
std::string dump;
// Dump individual profile files.
if (!profile_files_fd_.empty()) {
@@ -358,17 +416,207 @@ class ProfMan FINAL {
return dump_only_;
}
+ bool GetClassNames(int fd,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* class_names) {
+ ProfileCompilationInfo profile_info;
+ if (!profile_info.Load(fd)) {
+ LOG(ERROR) << "Cannot load profile info";
+ return false;
+ }
+ profile_info.GetClassNames(dex_files, class_names);
+ return true;
+ }
+
+ bool GetClassNames(std::string profile_file,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* class_names) {
+ int fd = open(profile_file.c_str(), O_RDONLY);
+ if (!FdIsValid(fd)) {
+ LOG(ERROR) << "Cannot open " << profile_file << strerror(errno);
+ return false;
+ }
+ if (!GetClassNames(fd, dex_files, class_names)) {
+ return false;
+ }
+ if (close(fd) < 0) {
+ PLOG(WARNING) << "Failed to close descriptor";
+ }
+ return true;
+ }
+
+ int DumpClasses() {
+ // Open apk/zip files and and read dex files.
+ MemMap::Init(); // for ZipArchive::OpenFromFd
+ // Open the dex files to get the names for classes.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ OpenApkFilesFromLocations(&dex_files);
+ // Build a vector of class names from individual profile files.
+ std::set<std::string> class_names;
+ if (!profile_files_fd_.empty()) {
+ for (int profile_file_fd : profile_files_fd_) {
+ if (!GetClassNames(profile_file_fd, &dex_files, &class_names)) {
+ return -1;
+ }
+ }
+ }
+ if (!profile_files_.empty()) {
+ for (const std::string& profile_file : profile_files_) {
+ if (!GetClassNames(profile_file, &dex_files, &class_names)) {
+ return -1;
+ }
+ }
+ }
+ // Concatenate class names from reference profile file.
+ if (FdIsValid(reference_profile_file_fd_)) {
+ if (!GetClassNames(reference_profile_file_fd_, &dex_files, &class_names)) {
+ return -1;
+ }
+ }
+ if (!reference_profile_file_.empty()) {
+ if (!GetClassNames(reference_profile_file_, &dex_files, &class_names)) {
+ return -1;
+ }
+ }
+ // Dump the class names.
+ std::string dump;
+ for (const std::string& class_name : class_names) {
+ dump += class_name + std::string("\n");
+ }
+ if (!FdIsValid(dump_output_to_fd_)) {
+ std::cout << dump;
+ } else {
+ unix_file::FdFile out_fd(dump_output_to_fd_, false /*check_usage*/);
+ if (!out_fd.WriteFully(dump.c_str(), dump.length())) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ bool ShouldOnlyDumpClasses() {
+ return dump_classes_;
+ }
+
+ // Read lines from the given file, dropping comments and empty lines. Post-process each line with
+ // the given function.
+ template <typename T>
+ static T* ReadCommentedInputFromFile(
+ const char* input_filename, std::function<std::string(const char*)>* process) {
+ std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in));
+ if (input_file.get() == nullptr) {
+ LOG(ERROR) << "Failed to open input file " << input_filename;
+ return nullptr;
+ }
+ std::unique_ptr<T> result(
+ ReadCommentedInputStream<T>(*input_file, process));
+ input_file->close();
+ return result.release();
+ }
+
+ // Read lines from the given stream, dropping comments and empty lines. Post-process each line
+ // with the given function.
+ template <typename T>
+ static T* ReadCommentedInputStream(
+ std::istream& in_stream,
+ std::function<std::string(const char*)>* process) {
+ std::unique_ptr<T> output(new T());
+ while (in_stream.good()) {
+ std::string dot;
+ std::getline(in_stream, dot);
+ if (android::base::StartsWith(dot, "#") || dot.empty()) {
+ continue;
+ }
+ if (process != nullptr) {
+ std::string descriptor((*process)(dot.c_str()));
+ output->insert(output->end(), descriptor);
+ } else {
+ output->insert(output->end(), dot);
+ }
+ }
+ return output.release();
+ }
+
+ int CreateProfile() {
+ MemMap::Init(); // for ZipArchive::OpenFromFd
+ // Open the profile output file if needed.
+ int fd = reference_profile_file_fd_;
+ if (!FdIsValid(fd)) {
+ CHECK(!reference_profile_file_.empty());
+ fd = open(reference_profile_file_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd < 0) {
+ LOG(ERROR) << "Cannot open " << reference_profile_file_ << strerror(errno);
+ return -1;
+ }
+ }
+ // Read the user-specified list of classes (dot notation rather than descriptors).
+ std::unique_ptr<std::unordered_set<std::string>>
+ user_class_list(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
+ create_profile_from_file_.c_str(), nullptr)); // No post-processing.
+ std::unordered_set<std::string> matched_user_classes;
+ // Open the dex files to look up class names.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ OpenApkFilesFromLocations(&dex_files);
+ // Iterate over the dex files looking for class names in the input stream.
+ std::set<DexCacheResolvedClasses> resolved_class_set;
+ for (auto& dex_file : dex_files) {
+ // Compute the set of classes to be added for this dex file first. This
+ // avoids creating an entry in the profile information for dex files that
+ // contribute no classes.
+ std::unordered_set<dex::TypeIndex> classes_to_be_added;
+ for (const auto& klass : *user_class_list) {
+ std::string descriptor = DotToDescriptor(klass.c_str());
+ const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor.c_str());
+ if (type_id == nullptr) {
+ continue;
+ }
+ classes_to_be_added.insert(dex_file->GetIndexForTypeId(*type_id));
+ matched_user_classes.insert(klass);
+ }
+ if (classes_to_be_added.empty()) {
+ continue;
+ }
+ // Insert the DexCacheResolved Classes into the set expected for
+ // AddMethodsAndClasses.
+ std::set<DexCacheResolvedClasses>::iterator dex_resolved_classes =
+ resolved_class_set.emplace(dex_file->GetLocation(),
+ dex_file->GetBaseLocation(),
+ dex_file->GetLocationChecksum()).first;
+ dex_resolved_classes->AddClasses(classes_to_be_added.begin(), classes_to_be_added.end());
+ }
+ // Warn the user if we didn't find matches for every class.
+ for (const auto& klass : *user_class_list) {
+ if (matched_user_classes.find(klass) == matched_user_classes.end()) {
+ LOG(WARNING) << "requested class '" << klass << "' was not matched in any dex file";
+ }
+ }
+ // Generate the profile data structure.
+ ProfileCompilationInfo info;
+ std::vector<MethodReference> methods; // No methods for now.
+ info.AddMethodsAndClasses(methods, resolved_class_set);
+ // Write the profile file.
+ CHECK(info.Save(fd));
+ if (close(fd) < 0) {
+ PLOG(WARNING) << "Failed to close descriptor";
+ }
+ return 0;
+ }
+
+ bool ShouldCreateProfile() {
+ return !create_profile_from_file_.empty();
+ }
+
int GenerateTestProfile() {
int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (profile_test_fd < 0) {
- std::cerr << "Cannot open " << test_profile_ << strerror(errno);
+ LOG(ERROR) << "Cannot open " << test_profile_ << strerror(errno);
return -1;
}
bool result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
- test_profile_num_dex_,
- test_profile_method_ratio_,
- test_profile_class_ratio_);
+ test_profile_num_dex_,
+ test_profile_method_ratio_,
+ test_profile_class_ratio_);
close(profile_test_fd); // ignore close result.
return result ? 0 : -1;
}
@@ -405,12 +653,15 @@ class ProfMan FINAL {
std::vector<std::string> profile_files_;
std::vector<int> profile_files_fd_;
std::vector<std::string> dex_locations_;
+ std::vector<std::string> apk_files_;
std::vector<int> apks_fd_;
std::string reference_profile_file_;
int reference_profile_file_fd_;
bool dump_only_;
+ bool dump_classes_;
int dump_output_to_fd_;
std::string test_profile_;
+ std::string create_profile_from_file_;
uint16_t test_profile_num_dex_;
uint16_t test_profile_method_ratio_;
uint16_t test_profile_class_ratio_;
@@ -430,6 +681,12 @@ static int profman(int argc, char** argv) {
if (profman.ShouldOnlyDumpProfile()) {
return profman.DumpProfileInfo();
}
+ if (profman.ShouldOnlyDumpClasses()) {
+ return profman.DumpClasses();
+ }
+ if (profman.ShouldCreateProfile()) {
+ return profman.CreateProfile();
+ }
// Process profile information and assess if we need to do a profile guided compilation.
// This operation involves I/O.
return profman.ProcessProfiles();
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 9585ba2d8e..d3a81a9add 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -123,6 +123,7 @@ cc_defaults {
"memory_region.cc",
"method_handles.cc",
"mirror/array.cc",
+ "mirror/call_site.cc",
"mirror/class.cc",
"mirror/class_ext.cc",
"mirror/dex_cache.cc",
@@ -131,6 +132,7 @@ cc_defaults {
"mirror/field.cc",
"mirror/method.cc",
"mirror/method_handle_impl.cc",
+ "mirror/method_handles_lookup.cc",
"mirror/method_type.cc",
"mirror/object.cc",
"mirror/reference.cc",
@@ -546,6 +548,7 @@ art_cc_test {
"gc/reference_queue_test.cc",
"gc/space/dlmalloc_space_static_test.cc",
"gc/space/dlmalloc_space_random_test.cc",
+ "gc/space/image_space_test.cc",
"gc/space/large_object_space_test.cc",
"gc/space/rosalloc_space_static_test.cc",
"gc/space/rosalloc_space_random_test.cc",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a443a4060d..cfe8406fbf 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -965,9 +965,27 @@ ENTRY \name
END \name
.endm
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+// Macro for string and type resolution and initialization.
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+ .extern \entrypoint
+ENTRY \name
+ SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC
+ mov r1, r9 @ pass Thread::Current
+ bl \entrypoint @ (uint32_t index, Thread*)
+ cbz r0, 1f @ If result is null, deliver the OOME.
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
+ bx lr
+ .cfi_restore_state
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+END \name
+.endm
+
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
/*
* Called by managed code to resolve a static field and load a non-wide value.
@@ -1066,27 +1084,6 @@ ENTRY art_quick_set64_static
DELIVER_PENDING_EXCEPTION
END art_quick_set64_static
- /*
- * Entry from managed code to resolve a string, this stub will
- * check the dex cache for a matching string (the fast path), and if not found,
- * it will allocate a String and deliver an exception on error.
- * On success the String is returned. R0 holds the string index.
- */
-
-ENTRY art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC
- mov r1, r9 @ pass Thread::Current
- bl artResolveStringFromCode @ (uint32_t type_idx, Thread*)
- cbz r0, 1f @ If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
- bx lr
- .cfi_restore_state
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END art_quick_resolve_string
-
-
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
// Comment out allocators that have arm specific asm.
@@ -2057,7 +2054,9 @@ ENTRY \name
beq .Lret_forwarding_address\name
.Lslow_rb_\name:
- // Save IP: the kSaveEverything entrypoint art_quick_resolve_string makes a tail call here.
+ // Save IP: The kSaveEverything entrypoint art_quick_resolve_string used to
+ // make a tail call here. Currently, it serves only for stack alignment but
+ // we may reintroduce kSaveEverything calls here in the future.
push {r0-r4, r9, ip, lr} @ save return address, core caller-save registers and ip
.cfi_adjust_cfa_offset 32
.cfi_rel_offset r0, 0
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 219d8b447a..bfbe4816ba 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1553,6 +1553,24 @@ ENTRY \name
END \name
.endm
+// Macro for string and type resolution and initialization.
+.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint
+ .extern \entrypoint
+ENTRY \name
+ SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl
+ mov x1, xSELF // pass Thread::Current
+ bl \entrypoint // (int32_t index, Thread* self)
+ cbz w0, 1f // If result is null, deliver the OOME.
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
+ ret // return
+ .cfi_restore_state
+ .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING // workaround for clang bug: 31975598
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+END \name
+.endm
+
.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
cbz w0, 1f // result zero branch over
ret // return
@@ -1571,10 +1589,11 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode,
* initializer and deliver the exception on error. On success the static storage base is
* returned.
*/
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
@@ -1604,27 +1623,6 @@ THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCod
THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
- /*
- * Entry from managed code to resolve a string, this stub will
- * check the dex cache for a matching string (the fast path), and if not found,
- * it will allocate a String and deliver an exception on error.
- * On success the String is returned. R0 holds the string index.
- */
-
-ENTRY art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl
- mov x1, xSELF // pass Thread::Current
- bl artResolveStringFromCode // (int32_t string_idx, Thread* self)
- cbz w0, 1f // If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
- ret // return
- .cfi_restore_state
- .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING // workaround for clang bug: 31975598
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END art_quick_resolve_string
-
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS
// Comment out allocators that have arm64 specific asm.
@@ -2380,13 +2378,6 @@ END art_quick_indexof
ENTRY \name
// Reference is null, no work to do at all.
cbz \wreg, .Lret_rb_\name
- /*
- * Allocate 46 stack slots * 8 = 368 bytes:
- * - 20 slots for core registers X0-X19
- * - 24 slots for floating-point registers D0-D7 and D16-D31
- * - 1 slot for return address register XLR
- * - 1 padding slot for 16-byte stack alignment
- */
// Use wIP0 as temp and check the mark bit of the reference. wIP0 is not used by the compiler.
ldr wIP0, [\xreg, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
tbz wIP0, #LOCK_WORD_MARK_BIT_SHIFT, .Lnot_marked_rb_\name
@@ -2398,10 +2389,15 @@ ENTRY \name
cmp wzr, wIP0, lsr #30
beq .Lret_forwarding_address\name
.Lslow_rb_\name:
- // We must not clobber IP0 since art_quick_resolve_string makes a tail call here and relies on
- // IP0 being restored.
+ /*
+ * Allocate 44 stack slots * 8 = 352 bytes:
+ * - 20 slots for core registers X0-15, X17-X19, LR
+ * - 24 slots for floating-point registers D0-D7 and D16-D31
+ */
+ // We must not clobber IP1 since code emitted for HLoadClass and HLoadString
+ // relies on IP1 being preserved.
// Save all potentially live caller-save core registers.
- SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 368
+ SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 352
SAVE_TWO_REGS x2, x3, 16
SAVE_TWO_REGS x4, x5, 32
SAVE_TWO_REGS x6, x7, 48
@@ -2409,8 +2405,8 @@ ENTRY \name
SAVE_TWO_REGS x10, x11, 80
SAVE_TWO_REGS x12, x13, 96
SAVE_TWO_REGS x14, x15, 112
- SAVE_TWO_REGS x16, x17, 128
- SAVE_TWO_REGS x18, x19, 144
+ SAVE_TWO_REGS x17, x18, 128 // Skip x16, i.e. IP0.
+ SAVE_TWO_REGS x19, xLR, 144 // Save also return address.
// Save all potentially live caller-save floating-point registers.
stp d0, d1, [sp, #160]
stp d2, d3, [sp, #176]
@@ -2424,9 +2420,6 @@ ENTRY \name
stp d26, d27, [sp, #304]
stp d28, d29, [sp, #320]
stp d30, d31, [sp, #336]
- // Save return address.
- // (sp + #352 is a padding slot)
- SAVE_REG xLR, 360
.ifnc \wreg, w0
mov w0, \wreg // Pass arg1 - obj from `wreg`
@@ -2446,8 +2439,8 @@ ENTRY \name
POP_REGS_NE x10, x11, 80, \xreg
POP_REGS_NE x12, x13, 96, \xreg
POP_REGS_NE x14, x15, 112, \xreg
- POP_REGS_NE x16, x17, 128, \xreg
- POP_REGS_NE x18, x19, 144, \xreg
+ POP_REGS_NE x17, x18, 128, \xreg
+ POP_REGS_NE x19, xLR, 144, \xreg // Restore also return address.
// Restore floating-point registers.
ldp d0, d1, [sp, #160]
ldp d2, d3, [sp, #176]
@@ -2461,9 +2454,8 @@ ENTRY \name
ldp d26, d27, [sp, #304]
ldp d28, d29, [sp, #320]
ldp d30, d31, [sp, #336]
- // Restore return address and remove padding.
- RESTORE_REG xLR, 360
- DECREASE_FRAME 368
+ // Remove frame and return.
+ DECREASE_FRAME 352
ret
.Lret_forwarding_address\name:
mvn wIP0, wIP0
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 663cb6c62f..ec8ae85722 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1576,9 +1576,87 @@ END \name
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+// A hand-written override for:
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
+.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name
+ENTRY \c_name
+ # Fast path rosalloc allocation
+ # a0: type
+ # s1: Thread::Current
+ # -----------------------------
+ # t1: object size
+ # t2: rosalloc run
+ # t3: thread stack top offset
+ # t4: thread stack bottom offset
+ # v0: free list head
+ #
+ # t5, t6 : temps
+ lw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation
+ lw $t4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # stack has any room left.
+ bgeu $t3, $t4, .Lslow_path_\c_name
+
+ lw $t1, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0) # Load object size (t1).
+ li $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local
+ # allocation. Also does the
+ # initialized and finalizable checks.
+ bgtu $t1, $t5, .Lslow_path_\c_name
+
+ # Compute the rosalloc bracket index from the size. Since the size is already aligned we can
+ # combine the two shifts together.
+ srl $t1, $t1, (ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
+
+ addu $t2, $t1, $s1
+ lw $t2, (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)($t2) # Load rosalloc run (t2).
+
+ # Load the free list head (v0).
+ # NOTE: this will be the return val.
+ lw $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+ beqz $v0, .Lslow_path_\c_name
+ nop
+
+ # Load the next pointer of the head and update the list head with the next pointer.
+ lw $t5, ROSALLOC_SLOT_NEXT_OFFSET($v0)
+ sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+
+ # Store the class pointer in the header. This also overwrites the first pointer. The offsets are
+ # asserted to match.
+
+#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
+#error "Class pointer needs to overwrite next pointer."
+#endif
+
+ POISON_HEAP_REF $a0
+ sw $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)
+
+ # Push the new object onto the thread local allocation stack and increment the thread local
+ # allocation stack top.
+ sw $v0, 0($t3)
+ addiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
+ sw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
+
+ # Decrement the size of the free list.
+ lw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+ addiu $t5, $t5, -1
+ sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+
+ sync # Fence.
+
+ jalr $zero, $ra
+ nop
+
+ .Lslow_path_\c_name:
+ SETUP_SAVE_REFS_ONLY_FRAME
+ la $t9, \cxx_name
+ jalr $t9
+ move $a1, $s1 # Pass self as argument.
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \c_name
+.endm
+
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
@@ -1964,67 +2042,158 @@ ENTRY_NO_GP art_quick_indexof
/* $a0 holds address of "this" */
/* $a1 holds "ch" */
/* $a2 holds "fromIndex" */
- lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
- slt $t1, $a2, $zero # if fromIndex < 0
+#if (STRING_COMPRESSION_FEATURE)
+ lw $a3, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this
+#else
+ lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
+#endif
+ slt $t1, $a2, $zero # if fromIndex < 0
#if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6)
- seleqz $a2, $a2, $t1 # fromIndex = 0;
+ seleqz $a2, $a2, $t1 # fromIndex = 0;
#else
- movn $a2, $zero, $t1 # fromIndex = 0;
+ movn $a2, $zero, $t1 # fromIndex = 0;
#endif
- subu $t0, $t0, $a2 # this.length() - fromIndex
- blez $t0, 6f # if this.length()-fromIndex <= 0
- li $v0, -1 # return -1;
-
- sll $v0, $a2, 1 # $a0 += $a2 * 2
- addu $a0, $a0, $v0 # " ditto "
- move $v0, $a2 # Set i to fromIndex.
+#if (STRING_COMPRESSION_FEATURE)
+ srl $t0, $a3, 1 # $a3 holds count (with flag) and $t0 holds actual length
+#endif
+ subu $t0, $t0, $a2 # this.length() - fromIndex
+ blez $t0, 6f # if this.length()-fromIndex <= 0
+ li $v0, -1 # return -1;
+
+#if (STRING_COMPRESSION_FEATURE)
+ sll $a3, $a3, 31 # Extract compression flag.
+ beqz $a3, .Lstring_indexof_compressed
+ move $t2, $a0 # Save a copy in $t2 to later compute result (in branch delay slot).
+#endif
+ sll $v0, $a2, 1 # $a0 += $a2 * 2
+ addu $a0, $a0, $v0 # " ditto "
+ move $v0, $a2 # Set i to fromIndex.
1:
- lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch
- beq $t3, $a1, 6f # return i;
- addu $a0, $a0, 2 # i++
- subu $t0, $t0, 1 # this.length() - i
- bnez $t0, 1b # while this.length() - i > 0
- addu $v0, $v0, 1 # i++
+ lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch
+ beq $t3, $a1, 6f # return i;
+ addu $a0, $a0, 2 # i++
+ subu $t0, $t0, 1 # this.length() - i
+ bnez $t0, 1b # while this.length() - i > 0
+ addu $v0, $v0, 1 # i++
- li $v0, -1 # if this.length() - i <= 0
- # return -1;
+ li $v0, -1 # if this.length() - i <= 0
+ # return -1;
6:
- j $ra
- nop
+ j $ra
+ nop
+
+#if (STRING_COMPRESSION_FEATURE)
+.Lstring_indexof_compressed:
+ addu $a0, $a0, $a2 # $a0 += $a2
+
+.Lstring_indexof_compressed_loop:
+ lbu $t3, MIRROR_STRING_VALUE_OFFSET($a0)
+ beq $t3, $a1, .Lstring_indexof_compressed_matched
+ subu $t0, $t0, 1
+ bgtz $t0, .Lstring_indexof_compressed_loop
+ addu $a0, $a0, 1
+
+.Lstring_indexof_nomatch:
+ jalr $zero, $ra
+ li $v0, -1 # return -1;
+
+.Lstring_indexof_compressed_matched:
+ jalr $zero, $ra
+ subu $v0, $a0, $t2 # return (current - start);
+#endif
END art_quick_indexof
/* java.lang.String.compareTo(String anotherString) */
ENTRY_NO_GP art_quick_string_compareto
/* $a0 holds address of "this" */
/* $a1 holds address of "anotherString" */
- beq $a0, $a1, 9f # this and anotherString are the same object
- move $v0, $zero
+ beq $a0, $a1, .Lstring_compareto_length_diff # this and anotherString are the same object
+ move $a3, $a2 # trick to return 0 (it returns a2 - a3)
+
+#if (STRING_COMPRESSION_FEATURE)
+ lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this
+ lw $t1, MIRROR_STRING_COUNT_OFFSET($a1) # 'count' field of anotherString
+ sra $a2, $t0, 1 # this.length()
+ sra $a3, $t1, 1 # anotherString.length()
+#else
+ lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
+ lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length()
+#endif
- lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
- lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length()
- MINu $t2, $a2, $a3
-# $t2 now holds min(this.length(),anotherString.length())
+ MINu $t2, $a2, $a3
+ # $t2 now holds min(this.length(),anotherString.length())
- beqz $t2, 9f # while min(this.length(),anotherString.length())-i != 0
- subu $v0, $a2, $a3 # if $t2==0 return
- # (this.length() - anotherString.length())
-1:
- lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i)
- lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
- bne $t0, $t1, 9f # if this.charAt(i) != anotherString.charAt(i)
- subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i))
- addiu $a0, $a0, 2 # point at this.charAt(i++)
- subu $t2, $t2, 1 # new value of
- # min(this.length(),anotherString.length())-i
- bnez $t2, 1b
- addiu $a1, $a1, 2 # point at anotherString.charAt(i++)
- subu $v0, $a2, $a3
-
-9:
- j $ra
- nop
+ # while min(this.length(),anotherString.length())-i != 0
+ beqz $t2, .Lstring_compareto_length_diff # if $t2==0
+ nop # return (this.length() - anotherString.length())
+
+#if (STRING_COMPRESSION_FEATURE)
+ # Differ cases:
+ sll $t3, $t0, 31
+ beqz $t3, .Lstring_compareto_this_is_compressed
+ sll $t3, $t1, 31 # In branch delay slot.
+ beqz $t3, .Lstring_compareto_that_is_compressed
+ nop
+ b .Lstring_compareto_both_not_compressed
+ nop
+
+.Lstring_compareto_this_is_compressed:
+ beqz $t3, .Lstring_compareto_both_compressed
+ nop
+ /* If (this->IsCompressed() && that->IsCompressed() == false) */
+.Lstring_compareto_loop_comparison_this_compressed:
+ lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+ lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bne $t0, $t1, .Lstring_compareto_char_diff
+ addiu $a0, $a0, 1 # point at this.charAt(i++) - compressed
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_loop_comparison_this_compressed
+ addiu $a1, $a1, 2 # point at anotherString.charAt(i++) - uncompressed
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+
+.Lstring_compareto_that_is_compressed:
+ lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+ lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bne $t0, $t1, .Lstring_compareto_char_diff
+ addiu $a0, $a0, 2 # point at this.charAt(i++) - uncompressed
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_that_is_compressed
+ addiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+
+.Lstring_compareto_both_compressed:
+ lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+ lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bne $t0, $t1, .Lstring_compareto_char_diff
+ addiu $a0, $a0, 1 # point at this.charAt(i++) - compressed
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_both_compressed
+ addiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+#endif
+
+.Lstring_compareto_both_not_compressed:
+ lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i)
+ lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bne $t0, $t1, .Lstring_compareto_char_diff # if this.charAt(i) != anotherString.charAt(i)
+ # return (this.charAt(i) - anotherString.charAt(i))
+ addiu $a0, $a0, 2 # point at this.charAt(i++)
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_both_not_compressed
+ addiu $a1, $a1, 2 # point at anotherString.charAt(i++)
+
+.Lstring_compareto_length_diff:
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+
+.Lstring_compareto_char_diff:
+ jalr $zero, $ra
+ subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i))
END art_quick_string_compareto
.extern artInvokePolymorphic
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 5fee575331..28d7c77938 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1533,8 +1533,85 @@ END \name
// Generate the allocation entrypoints for each allocator.
GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
+// A hand-written override for:
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc)
+.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name
+ENTRY \c_name
+ # Fast path rosalloc allocation
+ # a0: type
+ # s1: Thread::Current
+ # -----------------------------
+ # t1: object size
+ # t2: rosalloc run
+ # t3: thread stack top offset
+ # a4: thread stack bottom offset
+ # v0: free list head
+ #
+ # a5, a6 : temps
+ ld $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation stack
+ ld $a4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # has any room left.
+ bgeuc $t3, $a4, .Lslow_path_\c_name
+
+ lwu $t1, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0) # Load object size (t1).
+ li $a5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local
+ # allocation. Also does the initialized
+ # and finalizable checks.
+ bltuc $a5, $t1, .Lslow_path_\c_name
+
+ # Compute the rosalloc bracket index from the size. Since the size is already aligned we can
+ # combine the two shifts together.
+ dsrl $t1, $t1, (ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT)
+
+ daddu $t2, $t1, $s1
+ ld $t2, (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)($t2) # Load rosalloc run (t2).
+
+ # Load the free list head (v0).
+ # NOTE: this will be the return val.
+ ld $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+ beqzc $v0, .Lslow_path_\c_name
+
+ # Load the next pointer of the head and update the list head with the next pointer.
+ ld $a5, ROSALLOC_SLOT_NEXT_OFFSET($v0)
+ sd $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2)
+
+ # Store the class pointer in the header. This also overwrites the first pointer. The offsets are
+ # asserted to match.
+
+#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
+#error "Class pointer needs to overwrite next pointer."
+#endif
+
+ POISON_HEAP_REF $a0
+ sw $a0, MIRROR_OBJECT_CLASS_OFFSET($v0)
+
+ # Push the new object onto the thread local allocation stack and increment the thread local
+ # allocation stack top.
+ sd $v0, 0($t3)
+ daddiu $t3, $t3, COMPRESSED_REFERENCE_SIZE
+ sd $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1)
+
+ # Decrement the size of the free list.
+ lw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+ addiu $a5, $a5, -1
+ sw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2)
+
+ sync # Fence.
+
+ jalr $zero, $ra
+ .cpreturn # Restore gp from t8 in branch delay slot.
+
+.Lslow_path_\c_name:
+ SETUP_SAVE_REFS_ONLY_FRAME
+ jal \cxx_name
+ move $a1 ,$s1 # Pass self as argument.
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END \c_name
+.endm
+
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc
+ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc
+
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
@@ -1823,32 +1900,91 @@ END art_quick_deoptimize_from_compiled_code
ENTRY_NO_GP art_quick_string_compareto
/* $a0 holds address of "this" */
/* $a1 holds address of "anotherString" */
- beq $a0,$a1,9f # this and anotherString are the same object
- move $v0,$zero
+ move $a2, $zero
+ beq $a0, $a1, .Lstring_compareto_length_diff # this and anotherString are the same object
+ move $a3, $zero # return 0 (it returns a2 - a3)
+
+#if (STRING_COMPRESSION_FEATURE)
+ lw $a4, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this
+ lw $a5, MIRROR_STRING_COUNT_OFFSET($a1) # 'count' field of anotherString
+ sra $a2, $a4, 1 # this.length()
+ sra $a3, $a5, 1 # anotherString.length()
+#else
+ lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
+ lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length()
+#endif
- lw $a2,MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
- lw $a3,MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length()
- MINu $t2, $a2, $a3
-# $t2 now holds min(this.length(),anotherString.length())
+ MINu $t2, $a2, $a3
+ # $t2 now holds min(this.length(),anotherString.length())
- beqz $t2,9f # while min(this.length(),anotherString.length())-i != 0
- subu $v0,$a2,$a3 # if $t2==0 return
- # (this.length() - anotherString.length())
-1:
- lhu $t0,MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i)
- lhu $t1,MIRROR_STRING_VALUE_OFFSET($a1)
- bne $t0,$t1,9f # if this.charAt(i) != anotherString.charAt(i)
- subu $v0,$t0,$t1 # return (this.charAt(i) - anotherString.charAt(i))
- daddiu $a0,$a0,2 # point at this.charAt(i++)
- subu $t2,$t2,1 # new value of
- # min(this.length(),anotherString.length())-i
- bnez $t2,1b
- daddiu $a1,$a1,2 # point at anotherString.charAt(i++)
- subu $v0,$a2,$a3
-
-9:
- j $ra
- nop
+ # while min(this.length(),anotherString.length())-i != 0
+ beqzc $t2, .Lstring_compareto_length_diff # if $t2==0
+ # return (this.length() - anotherString.length())
+
+#if (STRING_COMPRESSION_FEATURE)
+ # Differ cases:
+ dext $a6, $a4, 0, 1
+ beqz $a6, .Lstring_compareto_this_is_compressed
+ dext $a6, $a5, 0, 1 # In branch delay slot.
+ beqz $a6, .Lstring_compareto_that_is_compressed
+ nop
+ b .Lstring_compareto_both_not_compressed
+ nop
+
+.Lstring_compareto_this_is_compressed:
+ beqzc $a6, .Lstring_compareto_both_compressed
+ /* If (this->IsCompressed() && that->IsCompressed() == false) */
+.Lstring_compareto_loop_comparison_this_compressed:
+ lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+ lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bnec $t0, $t1, .Lstring_compareto_char_diff
+ daddiu $a0, $a0, 1 # point at this.charAt(i++) - compressed
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_loop_comparison_this_compressed
+ daddiu $a1, $a1, 2 # point at anotherString.charAt(i++) - uncompressed
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+
+.Lstring_compareto_that_is_compressed:
+ lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+ lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bnec $t0, $t1, .Lstring_compareto_char_diff
+ daddiu $a0, $a0, 2 # point at this.charAt(i++) - uncompressed
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_that_is_compressed
+ daddiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+
+.Lstring_compareto_both_compressed:
+ lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0)
+ lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bnec $t0, $t1, .Lstring_compareto_char_diff
+ daddiu $a0, $a0, 1 # point at this.charAt(i++) - compressed
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_both_compressed
+ daddiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+#endif
+
+.Lstring_compareto_both_not_compressed:
+ lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i)
+ lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1)
+ bnec $t0, $t1, .Lstring_compareto_char_diff # if this.charAt(i) != anotherString.charAt(i)
+ # return (this.charAt(i) - anotherString.charAt(i))
+ daddiu $a0, $a0, 2 # point at this.charAt(i++)
+ subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i
+ bnez $t2, .Lstring_compareto_both_not_compressed
+ daddiu $a1, $a1, 2 # point at anotherString.charAt(i++)
+
+.Lstring_compareto_length_diff:
+ jalr $zero, $ra
+ subu $v0, $a2, $a3 # return (this.length() - anotherString.length())
+
+.Lstring_compareto_char_diff:
+ jalr $zero, $ra
+ subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i))
END art_quick_string_compareto
/* java.lang.String.indexOf(int ch, int fromIndex=0) */
@@ -1856,31 +1992,64 @@ ENTRY_NO_GP art_quick_indexof
/* $a0 holds address of "this" */
/* $a1 holds "ch" */
/* $a2 holds "fromIndex" */
- lw $t0,MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
- slt $at, $a2, $zero # if fromIndex < 0
- seleqz $a2, $a2, $at # fromIndex = 0;
- subu $t0,$t0,$a2 # this.length() - fromIndex
- blez $t0,6f # if this.length()-fromIndex <= 0
- li $v0,-1 # return -1;
+#if (STRING_COMPRESSION_FEATURE)
+ lw $a3, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this
+#else
+ lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length()
+#endif
+ slt $at, $a2, $zero # if fromIndex < 0
+ seleqz $a2, $a2, $at # fromIndex = 0;
+#if (STRING_COMPRESSION_FEATURE)
+ srl $t0, $a3, 1 # $a3 holds count (with flag) and $t0 holds actual length
+#endif
+ subu $t0, $t0, $a2 # this.length() - fromIndex
+ blez $t0, 6f # if this.length()-fromIndex <= 0
+ li $v0, -1 # return -1;
- sll $v0,$a2,1 # $a0 += $a2 * 2
- daddu $a0,$a0,$v0 # " ditto "
- move $v0,$a2 # Set i to fromIndex.
+#if (STRING_COMPRESSION_FEATURE)
+ dext $a3, $a3, 0, 1 # Extract compression flag.
+ beqzc $a3, .Lstring_indexof_compressed
+#endif
+
+ sll $v0, $a2, 1 # $a0 += $a2 * 2
+ daddu $a0, $a0, $v0 # " ditto "
+ move $v0, $a2 # Set i to fromIndex.
1:
- lhu $t3,MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch
- beq $t3,$a1,6f # return i;
- daddu $a0,$a0,2 # i++
- subu $t0,$t0,1 # this.length() - i
- bnez $t0,1b # while this.length() - i > 0
- addu $v0,$v0,1 # i++
+ lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch
+ beq $t3, $a1, 6f # return i;
+ daddu $a0, $a0, 2 # i++
+ subu $t0, $t0, 1 # this.length() - i
+ bnez $t0, 1b # while this.length() - i > 0
+ addu $v0, $v0, 1 # i++
- li $v0,-1 # if this.length() - i <= 0
- # return -1;
+ li $v0, -1 # if this.length() - i <= 0
+ # return -1;
6:
- j $ra
- nop
+ j $ra
+ nop
+
+#if (STRING_COMPRESSION_FEATURE)
+.Lstring_indexof_compressed:
+ move $a4, $a0 # Save a copy in $a4 to later compute result.
+ daddu $a0, $a0, $a2 # $a0 += $a2
+
+.Lstring_indexof_compressed_loop:
+ lbu $t3, MIRROR_STRING_VALUE_OFFSET($a0)
+ beq $t3, $a1, .Lstring_indexof_compressed_matched
+ subu $t0, $t0, 1
+ bgtz $t0, .Lstring_indexof_compressed_loop
+ daddu $a0, $a0, 1
+
+.Lstring_indexof_nomatch:
+ jalr $zero, $ra
+ li $v0, -1 # return -1;
+
+.Lstring_indexof_compressed_matched:
+ jalr $zero, $ra
+ dsubu $v0, $a0, $a4 # return (current - start);
+#endif
END art_quick_indexof
.extern artInvokePolymorphic
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 0bf08a6d97..207bf9d365 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -984,7 +984,7 @@ TEST_F(StubTest, AllocObject) {
while (length > 10) {
Handle<mirror::Object> h(hsp->NewHandle<mirror::Object>(
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), length / 4)));
- if (self->IsExceptionPending() || h.Get() == nullptr) {
+ if (self->IsExceptionPending() || h == nullptr) {
self->ClearException();
// Try a smaller length
@@ -1003,7 +1003,7 @@ TEST_F(StubTest, AllocObject) {
// Allocate simple objects till it fails.
while (!self->IsExceptionPending()) {
Handle<mirror::Object> h = hsp->NewHandle(c->AllocObject(soa.Self()));
- if (!self->IsExceptionPending() && h.Get() != nullptr) {
+ if (!self->IsExceptionPending() && h != nullptr) {
handles.push_back(h);
}
}
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 76615e843b..8c907e0790 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -922,6 +922,31 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
END_FUNCTION VAR(c_name)
END_MACRO
+// Macro for string and type resolution and initialization.
+MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name)
+ DEFINE_FUNCTION VAR(c_name)
+ SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save ref containing registers for GC
+ // Outgoing argument set up
+ subl MACRO_LITERAL(8), %esp // push padding
+ CFI_ADJUST_CFA_OFFSET(8)
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH eax // pass arg1
+ call CALLVAR(cxx_name) // cxx_name(arg1, Thread*)
+ addl MACRO_LITERAL(16), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-16)
+ testl %eax, %eax // If result is null, deliver the OOME.
+ jz 1f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX // restore frame up to return address
+ ret // return
+ CFI_RESTORE_STATE
+ CFI_DEF_CFA(esp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+ END_FUNCTION VAR(c_name)
+END_MACRO
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
testl %eax, %eax // eax == 0 ?
jz 1f // if eax == 0 goto 1
@@ -1245,31 +1270,10 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr
GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
-DEFINE_FUNCTION art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
- // Outgoing argument set up
- subl LITERAL(8), %esp // push padding
- CFI_ADJUST_CFA_OFFSET(8)
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH eax // pass arg1
- call SYMBOL(artResolveStringFromCode)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
- testl %eax, %eax // If result is null, deliver the OOME.
- jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX
- ret
- CFI_RESTORE_STATE
- CFI_DEF_CFA(esp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_resolve_string
-
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a1ae858735..f1be52eeb6 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -950,6 +950,26 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
END_FUNCTION VAR(c_name)
END_MACRO
+// Macro for string and type resolution and initialization.
+MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name)
+ DEFINE_FUNCTION VAR(c_name)
+ SETUP_SAVE_EVERYTHING_FRAME // save everything for GC
+ // Outgoing argument set up
+ movl %eax, %edi // pass string index
+ movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
+ call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
+ testl %eax, %eax // If result is null, deliver the OOME.
+ jz 1f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address
+ ret
+ CFI_RESTORE_STATE
+ CFI_DEF_CFA(rsp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
+1:
+ DELIVER_PENDING_EXCEPTION_FRAME_READY
+ END_FUNCTION VAR(c_name)
+END_MACRO
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
testq %rax, %rax // rax == 0 ?
jz 1f // if rax == 0 goto 1
@@ -1270,27 +1290,10 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab
ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB
END_FUNCTION art_quick_alloc_object_initialized_region_tlab
-DEFINE_FUNCTION art_quick_resolve_string
- SETUP_SAVE_EVERYTHING_FRAME
- // Outgoing argument set up
- movl %eax, %edi // pass string index
- movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
- call SYMBOL(artResolveStringFromCode) // artResolveStringFromCode(arg0, Thread*)
-
- testl %eax, %eax // If result is null, deliver the OOME.
- jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address
- ret
- CFI_RESTORE_STATE
- CFI_DEF_CFA(rsp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598
-1:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_resolve_string
-
-ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 6cb8544617..9d74e7c92b 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -67,7 +67,6 @@ ArtMethod* ArtMethod::GetNonObsoleteMethod() {
}
ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) {
- DCHECK(!IsNative());
if (!IsAbstract()) {
// A non-abstract's single implementation is itself.
return this;
@@ -275,7 +274,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type,
*has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION);
}
// Put the exception back.
- if (exception.Get() != nullptr) {
+ if (exception != nullptr) {
self->SetException(exception.Get());
}
return found_dex_pc;
@@ -442,12 +441,56 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
UNREACHABLE();
}
+// We use the method's DexFile and declaring class name to find the OatMethod for an obsolete
+// method. This is extremely slow but we need it if we want to be able to have obsolete native
+// methods since we need this to find the size of its stack frames.
+//
+// NB We could (potentially) do this differently and rely on the way the transformation is applied
+// in order to use the entrypoint to find this information. However, for debugging reasons (most
+// notably making sure that new invokes of obsolete methods fail) we choose to instead get the data
+// directly from the dex file.
+static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, bool* found)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(method->IsObsolete() && method->IsNative());
+ const DexFile* dex_file = method->GetDexFile();
+
+ // recreate the class_def_index from the descriptor.
+ std::string descriptor_storage;
+ const DexFile::TypeId* declaring_class_type_id =
+ dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage));
+ CHECK(declaring_class_type_id != nullptr);
+ dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id);
+ const DexFile::ClassDef* declaring_class_type_def =
+ dex_file->FindClassDef(declaring_class_type_index);
+ CHECK(declaring_class_type_def != nullptr);
+ uint16_t declaring_class_def_index = dex_file->GetIndexForClassDef(*declaring_class_type_def);
+
+ size_t oat_method_index = GetOatMethodIndexFromMethodIndex(*dex_file,
+ declaring_class_def_index,
+ method->GetDexMethodIndex());
+
+ OatFile::OatClass oat_class = OatFile::FindOatClass(*dex_file,
+ declaring_class_def_index,
+ found);
+ if (!(*found)) {
+ return OatFile::OatMethod::Invalid();
+ }
+ return oat_class.GetOatMethod(oat_method_index);
+}
+
static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method,
PointerSize pointer_size,
bool* found)
REQUIRES_SHARED(Locks::mutator_lock_) {
- // We shouldn't be calling this with obsolete methods.
- DCHECK(!method->IsObsolete());
+ if (UNLIKELY(method->IsObsolete())) {
+ // We shouldn't be calling this with obsolete methods except for native obsolete methods for
+ // which we need to use the oat method to figure out how large the quick frame is.
+ DCHECK(method->IsNative()) << "We should only be finding the OatMethod of obsolete methods in "
+ << "order to allow stack walking. Other obsolete methods should "
+ << "never need to access this information.";
+ DCHECK_EQ(pointer_size, kRuntimePointerSize) << "Obsolete method in compiler!";
+ return FindOatMethodFromDexFileFor(method, found);
+ }
// Although we overwrite the trampoline of non-static methods, we may get here via the resolution
// method for direct methods (or virtual methods made direct).
mirror::Class* declaring_class = method->GetDeclaringClass();
@@ -490,7 +533,7 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param
const auto& proto_id = dex_file->GetMethodPrototype(method_id);
const DexFile::TypeList* proto_params = dex_file->GetProtoParameters(proto_id);
auto count = proto_params != nullptr ? proto_params->Size() : 0u;
- auto param_len = params.Get() != nullptr ? params->GetLength() : 0u;
+ auto param_len = params != nullptr ? params->GetLength() : 0u;
if (param_len != count) {
return false;
}
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 383630363e..3d51fdde94 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -432,6 +432,7 @@ class ArtMethod FINAL {
}
ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) {
+ DCHECK(!IsNative());
return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size));
}
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 6e102be1a1..7bba944ca8 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -72,6 +72,7 @@ Uninterruptible Roles::uninterruptible_;
ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr;
Mutex* Locks::jni_weak_globals_lock_ = nullptr;
ReaderWriterMutex* Locks::dex_lock_ = nullptr;
+std::vector<BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_;
struct AllMutexData {
// A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait).
@@ -146,7 +147,10 @@ class ScopedContentionRecorder FINAL : public ValueObject {
const uint64_t start_nano_time_;
};
-BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {
+BaseMutex::BaseMutex(const char* name, LockLevel level)
+ : level_(level),
+ name_(name),
+ should_respond_to_empty_checkpoint_request_(false) {
if (kLogLockContentions) {
ScopedAllMutexesLock mu(this);
std::set<BaseMutex*>** all_mutexes_ptr = &gAllMutexData->all_mutexes;
@@ -377,6 +381,9 @@ void Mutex::ExclusiveLock(Thread* self) {
// Failed to acquire, hang up.
ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
num_contenders_++;
+ if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) {
+ self->CheckEmptyCheckpointFromMutex();
+ }
if (futex(state_.Address(), FUTEX_WAIT, 1, nullptr, nullptr, 0) != 0) {
// EAGAIN and EINTR both indicate a spurious failure, try again from the beginning.
// We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock.
@@ -519,6 +526,18 @@ std::ostream& operator<<(std::ostream& os, const Mutex& mu) {
return os;
}
+void Mutex::WakeupToRespondToEmptyCheckpoint() {
+#if ART_USE_FUTEXES
+ // Wake up all the waiters so they will respond to the emtpy checkpoint.
+ DCHECK(should_respond_to_empty_checkpoint_request_);
+ if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) {
+ futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0);
+ }
+#else
+ LOG(FATAL) << "Non futex case isn't supported.";
+#endif
+}
+
ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level)
: BaseMutex(name, level)
#if ART_USE_FUTEXES
@@ -563,6 +582,9 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) {
// Failed to acquire, hang up.
ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
++num_pending_writers_;
+ if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) {
+ self->CheckEmptyCheckpointFromMutex();
+ }
if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) {
// EAGAIN and EINTR both indicate a spurious failure, try again from the beginning.
// We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock.
@@ -639,6 +661,9 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32
}
ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
++num_pending_writers_;
+ if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) {
+ self->CheckEmptyCheckpointFromMutex();
+ }
if (futex(state_.Address(), FUTEX_WAIT, cur_state, &rel_ts, nullptr, 0) != 0) {
if (errno == ETIMEDOUT) {
--num_pending_writers_;
@@ -677,6 +702,9 @@ void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_sta
// Owner holds it exclusively, hang up.
ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self));
++num_pending_readers_;
+ if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) {
+ self->CheckEmptyCheckpointFromMutex();
+ }
if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) {
if (errno != EAGAIN && errno != EINTR) {
PLOG(FATAL) << "futex wait failed for " << name_;
@@ -749,6 +777,19 @@ std::ostream& operator<<(std::ostream& os, const MutatorMutex& mu) {
return os;
}
+void ReaderWriterMutex::WakeupToRespondToEmptyCheckpoint() {
+#if ART_USE_FUTEXES
+ // Wake up all the waiters so they will respond to the emtpy checkpoint.
+ DCHECK(should_respond_to_empty_checkpoint_request_);
+ if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 ||
+ num_pending_writers_.LoadRelaxed() > 0)) {
+ futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0);
+ }
+#else
+ LOG(FATAL) << "Non futex case isn't supported.";
+#endif
+}
+
ConditionVariable::ConditionVariable(const char* name, Mutex& guard)
: name_(name), guard_(guard) {
#if ART_USE_FUTEXES
@@ -1121,6 +1162,12 @@ void Locks::Init() {
#undef UPDATE_CURRENT_LOCK_LEVEL
+ // List of mutexes that we may hold when accessing a weak ref.
+ dex_lock_->SetShouldRespondToEmptyCheckpointRequest(true);
+ expected_mutexes_on_weak_ref_access_.push_back(dex_lock_);
+ classlinker_classes_lock_->SetShouldRespondToEmptyCheckpointRequest(true);
+ expected_mutexes_on_weak_ref_access_.push_back(classlinker_classes_lock_);
+
InitConditions();
}
}
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index ffe18c6a50..9b6938f9bf 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -152,6 +152,16 @@ class BaseMutex {
static void DumpAll(std::ostream& os);
+ bool ShouldRespondToEmptyCheckpointRequest() const {
+ return should_respond_to_empty_checkpoint_request_;
+ }
+
+ void SetShouldRespondToEmptyCheckpointRequest(bool value) {
+ should_respond_to_empty_checkpoint_request_ = value;
+ }
+
+ virtual void WakeupToRespondToEmptyCheckpoint() = 0;
+
protected:
friend class ConditionVariable;
@@ -168,6 +178,7 @@ class BaseMutex {
const LockLevel level_; // Support for lock hierarchy.
const char* const name_;
+ bool should_respond_to_empty_checkpoint_request_;
// A log entry that records contention but makes no guarantee that either tid will be held live.
struct ContentionLogEntry {
@@ -266,6 +277,8 @@ class LOCKABLE Mutex : public BaseMutex {
// For negative capabilities in clang annotations.
const Mutex& operator!() const { return *this; }
+ void WakeupToRespondToEmptyCheckpoint() OVERRIDE;
+
private:
#if ART_USE_FUTEXES
// 0 is unheld, 1 is held.
@@ -386,6 +399,8 @@ class SHARED_LOCKABLE ReaderWriterMutex : public BaseMutex {
// For negative capabilities in clang annotations.
const ReaderWriterMutex& operator!() const { return *this; }
+ void WakeupToRespondToEmptyCheckpoint() OVERRIDE;
+
private:
#if ART_USE_FUTEXES
// Out-of-inline path for handling contention for a SharedLock.
@@ -713,6 +728,12 @@ class Locks {
// Have an exclusive logging thread.
static Mutex* logging_lock_ ACQUIRED_AFTER(unexpected_signal_lock_);
+
+ // List of mutexes that we expect a thread may hold when accessing weak refs. This is used to
+ // avoid a deadlock in the empty checkpoint while weak ref access is disabled (b/34964016). If we
+ // encounter an unexpected mutex on accessing weak refs,
+ // Thread::CheckEmptyCheckpointFromWeakRefAccess will detect it.
+ static std::vector<BaseMutex*> expected_mutexes_on_weak_ref_access_;
};
class Roles {
diff --git a/runtime/cha.cc b/runtime/cha.cc
index d11b12f700..eaba01b2ce 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -200,7 +200,8 @@ void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify
if (verify_method != excluded_method) {
DCHECK(!verify_method->HasSingleImplementation())
<< "class: " << verify_class->PrettyClass()
- << " verify_method: " << verify_method->PrettyMethod(true);
+ << " verify_method: " << verify_method->PrettyMethod(true)
+ << " excluded_method: " << excluded_method->PrettyMethod(true);
if (verify_method->IsAbstract()) {
DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr);
}
@@ -257,9 +258,6 @@ void ClassHierarchyAnalysis::CheckSingleImplementationInfo(
return;
}
- // Native methods don't have single-implementation flag set.
- DCHECK(!method_in_super->IsNative());
-
uint16_t method_index = method_in_super->GetMethodIndex();
if (method_in_super->IsAbstract()) {
if (kIsDebugBuild) {
@@ -374,12 +372,12 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class>
// used for static methods or methods of final classes.
return;
}
- if (method->IsNative()) {
- // Native method's invocation overhead is already high and it
- // cannot be inlined. It's not worthwhile to devirtualize the
- // call which can add a deoptimization point.
- DCHECK(!method->HasSingleImplementation());
- } else if (method->IsAbstract()) {
+ if (method->IsAbstract()) {
+ // single-implementation of abstract method shares the same field
+ // that's used for JNI function of native method. It's fine since a method
+ // cannot be both abstract and native.
+ DCHECK(!method->IsNative()) << "Abstract method cannot be native";
+
if (method->GetDeclaringClass()->IsInstantiable()) {
// Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali).
// Do not attempt to devirtualize it.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7db83688e2..d02cf17d44 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -70,6 +70,7 @@
#include "jni_internal.h"
#include "leb128.h"
#include "linear_alloc.h"
+#include "mirror/call_site.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
@@ -82,6 +83,7 @@
#include "mirror/method.h"
#include "mirror/method_type.h"
#include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
@@ -405,7 +407,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_);
Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>(
heap->AllocNonMovableObject<true>(self, nullptr, class_class_size, VoidFunctor()))));
- CHECK(java_lang_Class.Get() != nullptr);
+ CHECK(java_lang_Class != nullptr);
mirror::Class::SetClassClass(java_lang_Class.Get());
java_lang_Class->SetClass(java_lang_Class.Get());
if (kUseBakerReadBarrier) {
@@ -425,7 +427,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
// java_lang_Object comes next so that object_array_class can be created.
Handle<mirror::Class> java_lang_Object(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_))));
- CHECK(java_lang_Object.Get() != nullptr);
+ CHECK(java_lang_Object != nullptr);
// backfill Object as the super class of Class.
java_lang_Class->SetSuperClass(java_lang_Object.Get());
mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self);
@@ -624,9 +626,9 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
// Setup the single, global copy of "iftable".
auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;"));
- CHECK(java_lang_Cloneable.Get() != nullptr);
+ CHECK(java_lang_Cloneable != nullptr);
auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;"));
- CHECK(java_io_Serializable.Get() != nullptr);
+ CHECK(java_io_Serializable != nullptr);
// We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
// crawl up and explicitly list all of the supers as well.
array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get());
@@ -695,6 +697,18 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
mirror::MethodHandleImpl::SetClass(class_root);
+ // Create java.lang.invoke.MethodHandles.Lookup.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root);
+ mirror::MethodHandlesLookup::SetClass(class_root);
+
+ // Create java.lang.invoke.CallSite.class root
+ class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kJavaLangInvokeCallSite, class_root);
+ mirror::CallSite::SetClass(class_root);
+
class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
CHECK(class_root != nullptr);
SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root);
@@ -981,6 +995,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) {
mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType));
mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl));
+ mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup));
+ mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite));
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
@@ -1231,12 +1247,13 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
if (dex_file->NumProtoIds() < num_method_types) {
num_method_types = dex_file->NumProtoIds();
}
-
+ const size_t num_call_sites = dex_file->NumCallSiteIds();
CHECK_EQ(num_strings, dex_cache->NumStrings());
CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
+ CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
DexCacheArraysLayout layout(image_pointer_size_, dex_file);
uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
if (num_strings != 0u) {
@@ -1316,6 +1333,22 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
mirror::MethodTypeDexCachePair::Initialize(method_types);
dex_cache->SetResolvedMethodTypes(method_types);
}
+ if (num_call_sites != 0u) {
+ GcRoot<mirror::CallSite>* const image_resolved_call_sites =
+ dex_cache->GetResolvedCallSites();
+ GcRoot<mirror::CallSite>* const call_sites =
+ reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+ for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
+ DCHECK(call_sites[j].IsNull());
+ }
+ CopyNonNull(image_resolved_call_sites,
+ num_call_sites,
+ call_sites,
+ [](const GcRoot<mirror::CallSite>& elem) {
+ return elem.IsNull();
+ });
+ dex_cache->SetResolvedCallSites(call_sites);
+ }
}
{
WriterMutexLock mu2(self, *Locks::dex_lock_);
@@ -1615,7 +1648,7 @@ bool ClassLinker::AddImageSpace(
DCHECK(out_dex_files != nullptr);
DCHECK(error_msg != nullptr);
const uint64_t start_time = NanoTime();
- const bool app_image = class_loader.Get() != nullptr;
+ const bool app_image = class_loader != nullptr;
const ImageHeader& header = space->GetImageHeader();
ObjPtr<mirror::Object> dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
DCHECK(dex_caches_object != nullptr);
@@ -1645,7 +1678,7 @@ bool ClassLinker::AddImageSpace(
"Class loader should be the last image root.");
MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
- DCHECK(class_roots.Get() != nullptr);
+ DCHECK(class_roots != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
class_roots->GetLength(),
@@ -2074,7 +2107,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) {
ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
classes.Assign(
mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
- CHECK(classes.Get() != nullptr); // OOME.
+ CHECK(classes != nullptr); // OOME.
GetClassInToObjectArray accumulator(classes.Get());
VisitClasses(&accumulator);
if (accumulator.Succeeded()) {
@@ -2115,6 +2148,8 @@ ClassLinker::~ClassLinker() {
mirror::ShortArray::ResetArrayClass();
mirror::MethodType::ResetClass();
mirror::MethodHandleImpl::ResetClass();
+ mirror::MethodHandlesLookup::ResetClass();
+ mirror::CallSite::ResetClass();
mirror::EmulatedStackFrame::ResetClass();
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
@@ -2152,7 +2187,7 @@ mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr<mirror::String>* out_locatio
DCHECK(out_location != nullptr);
auto dex_cache(hs.NewHandle(ObjPtr<mirror::DexCache>::DownCast(
GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
- if (dex_cache.Get() == nullptr) {
+ if (dex_cache == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
@@ -2453,7 +2488,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self,
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
- if (descriptor[0] != '[' && class_loader.Get() == nullptr) {
+ if (descriptor[0] != '[' && class_loader == nullptr) {
// Non-array class and the boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
@@ -2616,14 +2651,14 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
}
}
- if (klass.Get() == nullptr) {
+ if (klass == 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
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
- if (UNLIKELY(klass.Get() == nullptr)) {
+ if (UNLIKELY(klass == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
}
@@ -2716,7 +2751,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
return nullptr;
}
self->AssertNoPendingException();
- CHECK(h_new_class.Get() != nullptr) << descriptor;
+ CHECK(h_new_class != nullptr) << descriptor;
CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
// Instrumentation may have updated entrypoints for all methods of all
@@ -2997,7 +3032,7 @@ void ClassLinker::SetupClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass,
ObjPtr<mirror::ClassLoader> class_loader) {
- CHECK(klass.Get() != nullptr);
+ CHECK(klass != nullptr);
CHECK(klass->GetDexCache() != nullptr);
CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
const char* descriptor = dex_file.GetClassDescriptor(dex_class_def);
@@ -3112,6 +3147,7 @@ void ClassLinker::LoadClassMembers(Thread* self,
last_field_idx = field_idx;
}
}
+
// Load instance fields.
LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
allocator,
@@ -3128,6 +3164,7 @@ void ClassLinker::LoadClassMembers(Thread* self,
last_field_idx = field_idx;
}
}
+
if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
UNLIKELY(num_ifields != it.NumInstanceFields())) {
LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
@@ -3367,7 +3404,7 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file,
WriterMutexLock mu(self, *Locks::dex_lock_);
old_data = FindDexCacheDataLocked(dex_file);
old_dex_cache = DecodeDexCache(self, old_data);
- if (old_dex_cache == nullptr && h_dex_cache.Get() != nullptr) {
+ if (old_dex_cache == nullptr && h_dex_cache != nullptr) {
// Do InitializeDexCache while holding dex lock to make sure two threads don't call it at the
// same time with the same dex cache. Since the .bss is shared this can cause failing DCHECK
// that the arrays are null.
@@ -3383,12 +3420,12 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file,
if (old_dex_cache != nullptr) {
// Another thread managed to initialize the dex cache faster, so use that DexCache.
// If this thread encountered OOME, ignore it.
- DCHECK_EQ(h_dex_cache.Get() == nullptr, self->IsExceptionPending());
+ DCHECK_EQ(h_dex_cache == nullptr, self->IsExceptionPending());
self->ClearException();
// We cannot call EnsureSameClassLoader() while holding the dex_lock_.
return EnsureSameClassLoader(self, old_dex_cache, old_data, h_class_loader.Get());
}
- if (h_dex_cache.Get() == nullptr) {
+ if (h_dex_cache == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
@@ -3517,12 +3554,12 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto
StackHandleScope<2> hs(self);
MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1,
class_loader)));
- if (component_type.Get() == nullptr) {
+ if (component_type == nullptr) {
DCHECK(self->IsExceptionPending());
// We need to accept erroneous classes as component types.
const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get()));
- if (component_type.Get() == nullptr) {
+ if (component_type == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
} else {
@@ -3583,9 +3620,9 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto
new_class.Assign(GetClassRoot(kLongArrayClass));
}
}
- if (new_class.Get() == nullptr) {
+ if (new_class == nullptr) {
new_class.Assign(AllocClass(self, mirror::Array::ClassSize(image_pointer_size_)));
- if (new_class.Get() == nullptr) {
+ if (new_class == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
@@ -3818,8 +3855,8 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
Handle<mirror::Class> klass,
Handle<mirror::Class> supertype) {
DCHECK(self != nullptr);
- DCHECK(klass.Get() != nullptr);
- DCHECK(supertype.Get() != nullptr);
+ DCHECK(klass != nullptr);
+ DCHECK(supertype != nullptr);
if (!supertype->IsVerified() && !supertype->IsErroneous()) {
VerifyClass(self, supertype);
@@ -3836,13 +3873,13 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
LOG(WARNING) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
StackHandleScope<1> hs(self);
Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException()));
- if (cause.Get() != nullptr) {
+ if (cause != nullptr) {
// Set during VerifyClass call (if at all).
self->ClearException();
}
// Change into a verify error.
ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
- if (cause.Get() != nullptr) {
+ if (cause != nullptr) {
self->GetException()->SetCause(cause.Get());
}
ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
@@ -3921,7 +3958,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
StackHandleScope<2> hs(self);
MutableHandle<mirror::Class> supertype(hs.NewHandle(klass->GetSuperClass()));
// If we have a superclass and we get a hard verification failure we can return immediately.
- if (supertype.Get() != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) {
+ if (supertype != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) {
CHECK(self->IsExceptionPending()) << "Verification error should be pending.";
return verifier::MethodVerifier::kHardFailure;
}
@@ -3936,14 +3973,14 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
// but choose not to for an optimization. If the interfaces is being verified due to a class
// initialization (which would need all the default interfaces to be verified) the class code
// will trigger the recursive verification anyway.
- if ((supertype.Get() == nullptr || supertype->IsVerified()) // See (1)
+ if ((supertype == nullptr || supertype->IsVerified()) // See (1)
&& !klass->IsInterface()) { // See (2)
int32_t iftable_count = klass->GetIfTableCount();
MutableHandle<mirror::Class> iface(hs.NewHandle<mirror::Class>(nullptr));
// Loop through all interfaces this class has defined. It doesn't matter the order.
for (int32_t i = 0; i < iftable_count; i++) {
iface.Assign(klass->GetIfTable()->GetInterface(i));
- DCHECK(iface.Get() != nullptr);
+ DCHECK(iface != nullptr);
// We only care if we have default interfaces and can skip if we are already verified...
if (LIKELY(!iface->HasDefaultMethods() || iface->IsVerified())) {
continue;
@@ -3963,7 +4000,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
// At this point if verification failed, then supertype is the "first" supertype that failed
// verification (without a specific order). If verification succeeded, then supertype is either
// null or the original superclass of klass and is verified.
- DCHECK(supertype.Get() == nullptr ||
+ DCHECK(supertype == nullptr ||
supertype.Get() == klass->GetSuperClass() ||
!supertype->IsVerified());
@@ -4004,7 +4041,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
if (verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Even though there were no verifier failures we need to respect whether the super-class and
// super-default-interfaces were verified or requiring runtime reverification.
- if (supertype.Get() == nullptr || supertype->IsVerified()) {
+ if (supertype == nullptr || supertype->IsVerified()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
} else {
CHECK_EQ(supertype->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
@@ -4187,7 +4224,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
StackHandleScope<10> hs(self);
MutableHandle<mirror::Class> klass(hs.NewHandle(
AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class))));
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
CHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
@@ -4611,7 +4648,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
for (size_t i = 0; i < num_direct_interfaces; i++) {
handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i));
- CHECK(handle_scope_iface.Get() != nullptr);
+ CHECK(handle_scope_iface != nullptr);
CHECK(handle_scope_iface->IsInterface());
if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
// We have already done this for this interface. Skip it.
@@ -4890,7 +4927,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
{
StackHandleScope<1> hs(self);
Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
- if (UNLIKELY(return_type.Get() == nullptr)) {
+ if (UNLIKELY(return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
return false;
}
@@ -4940,7 +4977,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
Handle<mirror::Class> param_type(hs.NewHandle(
method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
- if (UNLIKELY(param_type.Get() == nullptr)) {
+ if (UNLIKELY(param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method1, i, param_type_idx);
return false;
@@ -5022,7 +5059,7 @@ bool ClassLinker::EnsureInitialized(Thread* self,
Handle<mirror::Class> c,
bool can_init_fields,
bool can_init_parents) {
- DCHECK(c.Get() != nullptr);
+ DCHECK(c != nullptr);
if (c->IsInitialized()) {
EnsureSkipAccessChecksMethods(c, image_pointer_size_);
self->AssertNoPendingException();
@@ -5202,7 +5239,7 @@ bool ClassLinker::LinkClass(Thread* self,
klass->SetMethodsPtrUnchecked(nullptr, 0, 0);
klass->SetSFieldsPtrUnchecked(nullptr);
klass->SetIFieldsPtrUnchecked(nullptr);
- if (UNLIKELY(h_new_class.Get() == nullptr)) {
+ if (UNLIKELY(h_new_class == nullptr)) {
self->AssertPendingOOMException();
mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return false;
@@ -5746,7 +5783,7 @@ bool ClassLinker::LinkVirtualMethods(
MutableHandle<mirror::PointerArray> vtable;
if (super_class->ShouldHaveEmbeddedVTable()) {
vtable = hs.NewHandle(AllocPointerArray(self, max_count));
- if (UNLIKELY(vtable.Get() == nullptr)) {
+ if (UNLIKELY(vtable == nullptr)) {
self->AssertPendingOOMException();
return false;
}
@@ -5775,7 +5812,7 @@ bool ClassLinker::LinkVirtualMethods(
}
vtable = hs.NewHandle(down_cast<mirror::PointerArray*>(
super_vtable->CopyOf(self, max_count)));
- if (UNLIKELY(vtable.Get() == nullptr)) {
+ if (UNLIKELY(vtable == nullptr)) {
self->AssertPendingOOMException();
return false;
}
@@ -5911,7 +5948,7 @@ bool ClassLinker::LinkVirtualMethods(
CHECK_LE(actual_count, max_count);
if (actual_count < max_count) {
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, actual_count)));
- if (UNLIKELY(vtable.Get() == nullptr)) {
+ if (UNLIKELY(vtable == nullptr)) {
self->AssertPendingOOMException();
return false;
}
@@ -5964,8 +6001,8 @@ static bool ContainsOverridingMethodOf(Thread* self,
PointerSize image_pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(self != nullptr);
- DCHECK(iface.Get() != nullptr);
- DCHECK(iftable.Get() != nullptr);
+ DCHECK(iface != nullptr);
+ DCHECK(iftable != nullptr);
DCHECK_GE(ifstart, 0u);
DCHECK_LT(ifstart, iftable->Count());
DCHECK_EQ(iface.Get(), iftable->GetInterface(ifstart));
@@ -6050,7 +6087,7 @@ ClassLinker::DefaultMethodSearchResult ClassLinker::FindDefaultMethodImplementat
<< "This will be a fatal error in subsequent versions of android. "
<< "Continuing anyway.";
}
- if (UNLIKELY(chosen_iface.Get() != nullptr)) {
+ if (UNLIKELY(chosen_iface != nullptr)) {
// We have multiple default impls of the same method. This is a potential default conflict.
// We need to check if this possibly conflicting method is either a superclass of the chosen
// default implementation or is overridden by a non-default interface method. In either case
@@ -6505,7 +6542,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class>
StackHandleScope<1> hs(self);
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 bool have_interfaces = interfaces != nullptr;
const size_t num_interfaces =
have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
if (num_interfaces == 0) {
@@ -6551,7 +6588,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class>
}
// Create the interface function table.
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
- if (UNLIKELY(iftable.Get() == nullptr)) {
+ if (UNLIKELY(iftable == nullptr)) {
self->AssertPendingOOMException();
return false;
}
@@ -6589,7 +6626,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class>
DCHECK_NE(num_interfaces, 0U);
iftable.Assign(down_cast<mirror::IfTable*>(
iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax)));
- if (UNLIKELY(iftable.Get() == nullptr)) {
+ if (UNLIKELY(iftable == nullptr)) {
self->AssertPendingOOMException();
return false;
}
@@ -6630,7 +6667,7 @@ static void CheckClassOwnsVTableEntries(Thread* self,
Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ObjPtr<mirror::Class> super_temp = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr;
Handle<mirror::Class> superclass(hs.NewHandle(super_temp));
- int32_t super_vtable_length = (superclass.Get() != nullptr) ? superclass->GetVTableLength() : 0;
+ int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0;
for (int32_t i = 0; i < check_vtable->GetLength(); ++i) {
ArtMethod* m = check_vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
CHECK(m != nullptr);
@@ -7289,7 +7326,7 @@ bool ClassLinker::LinkInterfaceMethods(
// For a new interface, however, we need the whole vtable in case a new
// interface method is implemented in the whole superclass.
using_virtuals = false;
- DCHECK(vtable.Get() != nullptr);
+ DCHECK(vtable != nullptr);
input_vtable_array = vtable;
input_array_length = input_vtable_array->GetLength();
}
@@ -7432,7 +7469,7 @@ bool ClassLinker::LinkInterfaceMethods(
if (fill_tables) {
vtable.Assign(helper.UpdateVtable(default_translations, vtable.Get()));
- if (UNLIKELY(vtable.Get() == nullptr)) {
+ if (UNLIKELY(vtable == nullptr)) {
// The helper has already called self->AssertPendingOOMException();
return false;
}
@@ -7452,12 +7489,12 @@ bool ClassLinker::LinkInterfaceMethods(
}
bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) {
- CHECK(klass.Get() != nullptr);
+ CHECK(klass != nullptr);
return LinkFields(self, klass, false, nullptr);
}
bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) {
- CHECK(klass.Get() != nullptr);
+ CHECK(klass != nullptr);
return LinkFields(self, klass, true, class_size);
}
@@ -7713,7 +7750,7 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) {
mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
dex::StringIndex string_idx,
Handle<mirror::DexCache> dex_cache) {
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
Thread::PoisonObjectPointersIfDebug();
ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx);
if (resolved != nullptr) {
@@ -7729,7 +7766,7 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
mirror::String* ClassLinker::LookupString(const DexFile& dex_file,
dex::StringIndex string_idx,
Handle<mirror::DexCache> dex_cache) {
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx);
if (resolved != nullptr) {
return resolved.Ptr();
@@ -7783,7 +7820,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
dex::TypeIndex type_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
Thread::PoisonObjectPointersIfDebug();
ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx);
if (resolved == nullptr) {
@@ -7821,7 +7858,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
Handle<mirror::ClassLoader> class_loader,
ArtMethod* referrer,
InvokeType type) {
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
// Check for hit in the dex cache.
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
Thread::PoisonObjectPointersIfDebug();
@@ -8060,7 +8097,7 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
bool is_static) {
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
@@ -8101,7 +8138,7 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file,
uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
Thread::PoisonObjectPointersIfDebug();
if (resolved != nullptr) {
@@ -8132,7 +8169,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- DCHECK(dex_cache.Get() != nullptr);
+ DCHECK(dex_cache != nullptr);
ObjPtr<mirror::MethodType> resolved = dex_cache->GetResolvedMethodType(proto_idx);
if (resolved != nullptr) {
@@ -8146,7 +8183,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx);
Handle<mirror::Class> return_type(hs.NewHandle(
ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader)));
- if (return_type.Get() == nullptr) {
+ if (return_type == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
@@ -8161,7 +8198,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args)));
- if (method_params.Get() == nullptr) {
+ if (method_params == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
@@ -8172,7 +8209,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
for (; it.HasNext(); it.Next()) {
const dex::TypeIndex type_idx = it.GetTypeIdx();
param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader));
- if (param_class.Get() == nullptr) {
+ if (param_class == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
@@ -8189,6 +8226,148 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file,
return type.Get();
}
+mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx,
+ ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* const self = Thread::Current();
+ const DexFile* const dex_file = referrer->GetDexFile();
+ const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx);
+
+ union {
+ ArtField* field;
+ ArtMethod* method;
+ uintptr_t field_or_method;
+ } target;
+ uint32_t num_params;
+ mirror::MethodHandle::Kind kind;
+ DexFile::MethodHandleType handle_type =
+ static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
+ switch (handle_type) {
+ case DexFile::MethodHandleType::kStaticPut: {
+ kind = mirror::MethodHandle::Kind::kStaticPut;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ num_params = 1;
+ break;
+ }
+ case DexFile::MethodHandleType::kStaticGet: {
+ kind = mirror::MethodHandle::Kind::kStaticGet;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ num_params = 0;
+ break;
+ }
+ case DexFile::MethodHandleType::kInstancePut: {
+ kind = mirror::MethodHandle::Kind::kInstancePut;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ num_params = 2;
+ break;
+ }
+ case DexFile::MethodHandleType::kInstanceGet: {
+ kind = mirror::MethodHandle::Kind::kInstanceGet;
+ target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ num_params = 1;
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic: {
+ kind = mirror::MethodHandle::Kind::kInvokeStatic;
+ target.method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kStatic);
+ uint32_t shorty_length;
+ target.method->GetShorty(&shorty_length);
+ num_params = shorty_length - 1; // Remove 1 for return value.
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInstance: {
+ kind = mirror::MethodHandle::Kind::kInvokeVirtual;
+ target.method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kVirtual);
+ uint32_t shorty_length;
+ target.method->GetShorty(&shorty_length);
+ num_params = shorty_length - 1; // Remove 1 for return value.
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+ num_params = 0;
+ }
+ }
+
+ StackHandleScope<5> hs(self);
+ ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+ ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type);
+ Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle(
+ mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params)));
+ if (method_params.Get() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ Handle<mirror::Class> return_type;
+ switch (handle_type) {
+ case DexFile::MethodHandleType::kStaticPut: {
+ method_params->Set(0, target.field->GetType<true>());
+ return_type = hs.NewHandle(FindPrimitiveClass('V'));
+ break;
+ }
+ case DexFile::MethodHandleType::kStaticGet: {
+ return_type = hs.NewHandle(target.field->GetType<true>());
+ break;
+ }
+ case DexFile::MethodHandleType::kInstancePut: {
+ method_params->Set(0, target.field->GetDeclaringClass());
+ method_params->Set(1, target.field->GetType<true>());
+ return_type = hs.NewHandle(FindPrimitiveClass('V'));
+ break;
+ }
+ case DexFile::MethodHandleType::kInstanceGet: {
+ method_params->Set(0, target.field->GetDeclaringClass());
+ return_type = hs.NewHandle(target.field->GetType<true>());
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic:
+ case DexFile::MethodHandleType::kInvokeInstance: {
+ // TODO(oth): This will not work for varargs methods as this
+ // requires instantiating a Transformer. This resolution step
+ // would be best done in managed code rather than in the run
+ // time (b/35235705)
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+ DexFileParameterIterator it(*dex_file, target.method->GetPrototype());
+ for (int32_t i = 0; it.HasNext(); i++, it.Next()) {
+ const dex::TypeIndex type_idx = it.GetTypeIdx();
+ mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
+ if (nullptr == klass) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ method_params->Set(i, klass);
+ }
+ return_type = hs.NewHandle(target.method->GetReturnType(true));
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ // TODO(oth): b/35235705
+ UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+ }
+ }
+
+ if (return_type.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ Handle<mirror::MethodType>
+ mt(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params)));
+ if (mt.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt);
+}
+
bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
return (entry_point == GetQuickResolutionStub()) ||
(quick_resolution_trampoline_ == entry_point);
@@ -8304,7 +8483,9 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
"[Ljava/lang/reflect/Constructor;",
"[Ljava/lang/reflect/Field;",
"[Ljava/lang/reflect/Method;",
+ "Ljava/lang/invoke/CallSite;",
"Ljava/lang/invoke/MethodHandleImpl;",
+ "Ljava/lang/invoke/MethodHandles$Lookup;",
"Ljava/lang/invoke/MethodType;",
"Ljava/lang/ClassLoader;",
"Ljava/lang/Throwable;",
@@ -8352,7 +8533,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
Handle<mirror::Class> dex_elements_class(hs.NewHandle(dex_elements_field->GetType<true>()));
- DCHECK(dex_elements_class.Get() != nullptr);
+ DCHECK(dex_elements_class != nullptr);
DCHECK(dex_elements_class->IsArrayClass());
Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements(hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(self,
@@ -8381,21 +8562,21 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
self,
kDexFileIndexStart + 1));
- DCHECK(h_long_array.Get() != nullptr);
+ DCHECK(h_long_array != nullptr);
h_long_array->Set(kDexFileIndexStart, reinterpret_cast<intptr_t>(dex_file));
Handle<mirror::Object> h_dex_file = hs2.NewHandle(
cookie_field->GetDeclaringClass()->AllocObject(self));
- DCHECK(h_dex_file.Get() != nullptr);
+ DCHECK(h_dex_file != nullptr);
cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get());
Handle<mirror::String> h_file_name = hs2.NewHandle(
mirror::String::AllocFromModifiedUtf8(self, dex_file->GetLocation().c_str()));
- DCHECK(h_file_name.Get() != nullptr);
+ DCHECK(h_file_name != nullptr);
file_name_field->SetObject<false>(h_dex_file.Get(), h_file_name.Get());
Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self));
- DCHECK(h_element.Get() != nullptr);
+ DCHECK(h_element != nullptr);
element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get());
h_dex_elements->Set(index, h_element.Get());
@@ -8406,7 +8587,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
// Create DexPathList.
Handle<mirror::Object> h_dex_path_list = hs.NewHandle(
dex_elements_field->GetDeclaringClass()->AllocObject(self));
- DCHECK(h_dex_path_list.Get() != nullptr);
+ DCHECK(h_dex_path_list != nullptr);
// Set elements.
dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
@@ -8415,7 +8596,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
h_path_class_class->AllocObject(self));
- DCHECK(h_path_class_loader.Get() != nullptr);
+ DCHECK(h_path_class_loader != nullptr);
// Set DexPathList.
ArtField* path_list_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 62d3c29a19..e27a53d15c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -55,6 +55,8 @@ namespace mirror {
class DexCacheMethodHandlesTest_Open_Test;
class DexCacheTest_Open_Test;
class IfTable;
+ class MethodHandle;
+ class MethodHandlesLookup;
class MethodType;
template<class T> class ObjectArray;
class StackTraceElement;
@@ -106,7 +108,9 @@ class ClassLinker {
kJavaLangReflectConstructorArrayClass,
kJavaLangReflectFieldArrayClass,
kJavaLangReflectMethodArrayClass,
+ kJavaLangInvokeCallSite,
kJavaLangInvokeMethodHandleImpl,
+ kJavaLangInvokeMethodHandlesLookup,
kJavaLangInvokeMethodType,
kJavaLangClassLoader,
kJavaLangThrowable,
@@ -366,6 +370,12 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+ // Resolve a method handle with a given ID from the DexFile. The
+ // result is not cached in the DexCache as the instance will only be
+ // used once in most circumstances.
+ mirror::MethodHandle* ResolveMethodHandle(uint32_t method_handle_idx, ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true on success, false if there's an exception pending.
// can_run_clinit=false allows the compiler to attempt to init a class,
// given the restriction that no <clinit> execution is possible.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 03105cb6fb..07f3744b69 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -32,6 +32,7 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/heap.h"
#include "mirror/accessible_object.h"
+#include "mirror/call_site.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
#include "mirror/dex_cache.h"
@@ -40,6 +41,7 @@
#include "mirror/field.h"
#include "mirror/method_type.h"
#include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
@@ -185,7 +187,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
void AssertArrayClass(const std::string& array_descriptor, Handle<mirror::Class> array)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ASSERT_TRUE(array.Get() != nullptr);
+ ASSERT_TRUE(array != nullptr);
ASSERT_TRUE(array->GetClass() != nullptr);
ASSERT_EQ(array->GetClass(), array->GetClass()->GetClass());
EXPECT_TRUE(array->GetClass()->GetSuperClass() != nullptr);
@@ -409,7 +411,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
StackHandleScope<1> hs(self);
Handle<mirror::Class> klass(
hs.NewHandle(class_linker_->FindSystemClass(self, descriptor.c_str())));
- ASSERT_TRUE(klass.Get() != nullptr);
+ ASSERT_TRUE(klass != nullptr);
std::string temp;
EXPECT_STREQ(descriptor.c_str(), klass.Get()->GetDescriptor(&temp));
EXPECT_EQ(class_loader, klass->GetClassLoader());
@@ -669,11 +671,13 @@ struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> {
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
@@ -762,6 +766,14 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> {
}
};
+struct MethodHandlesLookupOffsets : public CheckOffsets<mirror::MethodHandlesLookup> {
+ MethodHandlesLookupOffsets() : CheckOffsets<mirror::MethodHandlesLookup>(
+ false, "Ljava/lang/invoke/MethodHandles$Lookup;") {
+ addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, allowed_modes_), "allowedModes");
+ addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, lookup_class_), "lookupClass");
+ }
+};
+
struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
false, "Ldalvik/system/EmulatedStackFrame;") {
@@ -772,6 +784,13 @@ struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFram
}
};
+struct CallSiteOffsets : public CheckOffsets<mirror::CallSite> {
+ CallSiteOffsets() : CheckOffsets<mirror::CallSite>(
+ false, "Ljava/lang/invoke/CallSite;") {
+ addOffset(OFFSETOF_MEMBER(mirror::CallSite, target_), "target");
+ }
+};
+
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
// ClassLinker::LinkFields.
@@ -794,7 +813,9 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) {
EXPECT_TRUE(MethodTypeOffsets().Check());
EXPECT_TRUE(MethodHandleOffsets().Check());
EXPECT_TRUE(MethodHandleImplOffsets().Check());
+ EXPECT_TRUE(MethodHandlesLookupOffsets().Check());
EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
+ EXPECT_TRUE(CallSiteOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -1411,13 +1432,13 @@ TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) {
// java.lang.Object is a bootstrap class.
Handle<mirror::Class> jlo_class(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- ASSERT_TRUE(jlo_class.Get() != nullptr);
+ ASSERT_TRUE(jlo_class != nullptr);
EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded());
// Statics is not a bootstrap class.
Handle<mirror::Class> statics(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
- ASSERT_TRUE(statics.Get() != nullptr);
+ ASSERT_TRUE(statics != nullptr);
EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded());
}
@@ -1431,11 +1452,11 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) {
ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_);
for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
dex_cache.Assign(soa.Self()->DecodeJObject(data.weak_root)->AsDexCache());
- if (dex_cache.Get() != nullptr) {
+ if (dex_cache != nullptr) {
break;
}
}
- ASSERT_TRUE(dex_cache.Get() != nullptr);
+ ASSERT_TRUE(dex_cache != nullptr);
}
// Make a copy of the dex cache and change the name.
dex_cache.Assign(dex_cache->Clone(soa.Self())->AsDexCache());
@@ -1487,7 +1508,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader));
// Assert that the method type was resolved successfully.
- ASSERT_TRUE(method1_type.Get() != nullptr);
+ ASSERT_TRUE(method1_type != nullptr);
// Assert that the return type and the method arguments are as we expect.
Handle<mirror::Class> string_class(
diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc
index f1248eb00c..18c2b827fe 100644
--- a/runtime/class_table_test.cc
+++ b/runtime/class_table_test.cc
@@ -80,7 +80,7 @@ TEST_F(ClassTableTest, ClassTable) {
Handle<mirror::Class> h_Y(
hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
Handle<mirror::Object> obj_X = hs.NewHandle(h_X->AllocObject(soa.Self()));
- ASSERT_TRUE(obj_X.Get() != nullptr);
+ ASSERT_TRUE(obj_X != nullptr);
ClassTable table;
EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u);
EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index a44f79e193..4f4bed0169 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -126,6 +126,22 @@ void ThrowArrayStoreException(ObjPtr<mirror::Class> element_class,
mirror::Class::PrettyDescriptor(array_class).c_str()).c_str());
}
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+ va_end(args);
+}
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowWrappedException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args);
+ va_end(args);
+}
+
// ClassCastException
void ThrowClassCastException(ObjPtr<mirror::Class> dest_type, ObjPtr<mirror::Class> src_type) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 76ea2ae6c8..55a89388ea 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -56,6 +56,14 @@ void ThrowArrayStoreException(ObjPtr<mirror::Class> element_class,
ObjPtr<mirror::Class> array_class)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// BootstrapMethodError
+
+void ThrowBootstrapMethodError(const char* fmt, ...)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+void ThrowWrappedBootstrapMethodError(const char* fmt, ...)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// ClassCircularityError
void ThrowClassCircularityError(ObjPtr<mirror::Class> c)
@@ -236,7 +244,7 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
-// WrontMethodTypeException
+// WrongMethodTypeException
void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
mirror::MethodType* callsite_type)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 1a0cec075c..cfdc6e1afb 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1765,13 +1765,13 @@ static JDWP::JdwpError GetFieldValueImpl(JDWP::RefTypeId ref_type_id, JDWP::Obje
StackHandleScope<2> hs(self);
MutableHandle<mirror::Object>
o(hs.NewHandle(Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error)));
- if ((!is_static && o.Get() == nullptr) || error != JDWP::ERR_NONE) {
+ if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
ArtField* f = FromFieldId(field_id);
mirror::Class* receiver_class = c;
- if (receiver_class == nullptr && o.Get() != nullptr) {
+ if (receiver_class == nullptr && o != nullptr) {
receiver_class = o->GetClass();
}
@@ -1899,7 +1899,7 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId
StackHandleScope<2> hs(self);
MutableHandle<mirror::Object>
o(hs.NewHandle(Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error)));
- if ((!is_static && o.Get() == nullptr) || error != JDWP::ERR_NONE) {
+ if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
ArtField* f = FromFieldId(field_id);
@@ -2867,7 +2867,7 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec
StackHandleScope<1> hs(self);
Handle<mirror::Throwable> pending_exception(hs.NewHandle(self->GetException()));
self->ClearException();
- if (kIsDebugBuild && pending_exception.Get() != nullptr) {
+ if (kIsDebugBuild && pending_exception != nullptr) {
const DexFile::CodeItem* code_item = location.method->GetCodeItem();
const Instruction* instr = Instruction::At(&code_item->insns_[location.dex_pc]);
CHECK_EQ(Instruction::MOVE_EXCEPTION, instr->Opcode());
@@ -2875,7 +2875,7 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec
gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value);
- if (pending_exception.Get() != nullptr) {
+ if (pending_exception != nullptr) {
self->SetException(pending_exception.Get());
}
}
@@ -4027,7 +4027,7 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
ExecuteMethodWithoutPendingException(soa, pReq);
// If an exception was pending before the invoke, restore it now.
- if (old_exception.Get() != nullptr) {
+ if (old_exception != nullptr) {
soa.Self()->SetException(old_exception.Get());
}
}
@@ -4356,9 +4356,9 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
ScopedObjectAccessUnchecked soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
Handle<mirror::String> name(hs.NewHandle(t->GetThreadName()));
- size_t char_count = (name.Get() != nullptr) ? name->GetLength() : 0;
- const jchar* chars = (name.Get() != nullptr) ? name->GetValue() : nullptr;
- bool is_compressed = (name.Get() != nullptr) ? name->IsCompressed() : false;
+ size_t char_count = (name != nullptr) ? name->GetLength() : 0;
+ const jchar* chars = (name != nullptr) ? name->GetValue() : nullptr;
+ bool is_compressed = (name != nullptr) ? name->IsCompressed() : false;
std::vector<uint8_t> bytes;
JDWP::Append4BE(bytes, t->GetThreadId());
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 8b0c51c998..e58c6f541e 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -53,7 +53,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest {
ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
// Verify the environment is as we expect
- uint32_t checksum;
+ std::vector<uint32_t> checksums;
std::string error_msg;
ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
<< "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
@@ -61,7 +61,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest {
<< "Expected dex file to be at: " << GetDexSrc1();
ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
<< "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
- ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+ ASSERT_FALSE(DexFile::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg))
<< "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
<< "Expected dex file to be at: " << GetDexSrc2();
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index f59420d332..b6a2e09719 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -72,23 +72,13 @@ struct DexFile::AnnotationValue {
uint8_t type_;
};
-bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
- CHECK(checksum != nullptr);
+bool DexFile::GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg) {
+ CHECK(checksums != nullptr);
uint32_t magic;
- // Strip ":...", which is the location
- const char* zip_entry_name = kClassesDex;
- const char* file_part = filename;
- std::string file_part_storage;
-
- if (DexFile::IsMultiDexLocation(filename)) {
- file_part_storage = GetBaseLocation(filename);
- file_part = file_part_storage.c_str();
- zip_entry_name = filename + file_part_storage.size() + 1;
- DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator);
- }
-
- File fd = OpenAndReadMagic(file_part, &magic, error_msg);
+ File fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd.Fd() == -1) {
DCHECK(!error_msg->empty());
return false;
@@ -97,17 +87,25 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string*
std::unique_ptr<ZipArchive> zip_archive(
ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
if (zip_archive.get() == nullptr) {
- *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", file_part,
+ *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
error_msg->c_str());
return false;
}
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg));
+
+ uint32_t i = 0;
+ std::string zip_entry_name = GetMultiDexClassesDexName(i++);
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part,
- zip_entry_name, error_msg->c_str());
+ *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
+ zip_entry_name.c_str(), error_msg->c_str());
return false;
}
- *checksum = zip_entry->GetCrc32();
+
+ do {
+ checksums->push_back(zip_entry->GetCrc32());
+ zip_entry_name = DexFile::GetMultiDexClassesDexName(i++);
+ zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
+ } while (zip_entry.get() != nullptr);
return true;
}
if (IsDexMagic(magic)) {
@@ -116,7 +114,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string*
if (dex_file.get() == nullptr) {
return false;
}
- *checksum = dex_file->GetHeader().checksum_;
+ checksums->push_back(dex_file->GetHeader().checksum_);
return true;
}
*error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
@@ -333,7 +331,32 @@ std::unique_ptr<const DexFile> DexFile::OpenOneDexFileFromZip(const ZipArchive&
*error_code = ZipOpenErrorCode::kDexFileError;
return nullptr;
}
- std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
+
+ std::unique_ptr<MemMap> map;
+ if (zip_entry->IsUncompressed()) {
+ if (!zip_entry->IsAlignedTo(alignof(Header))) {
+ // Do not mmap unaligned ZIP entries because
+ // doing so would fail dex verification which requires 4 byte alignment.
+ LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+ << "please zipalign to " << alignof(Header) << " bytes. "
+ << "Falling back to extracting file.";
+ } else {
+ // Map uncompressed files within zip as file-backed to avoid a dirty copy.
+ map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
+ if (map == nullptr) {
+ LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
+ << "is your ZIP file corrupted? Falling back to extraction.";
+ // Try again with Extraction which still has a chance of recovery.
+ }
+ }
+ }
+
+ if (map == nullptr) {
+ // Default path for compressed ZIP entries,
+ // and fallback for stored ZIP entries.
+ map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
+ }
+
if (map == nullptr) {
*error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
error_msg->c_str());
@@ -415,7 +438,7 @@ bool DexFile::OpenAllDexFilesFromZip(const ZipArchive& zip_archive,
&error_code));
if (next_dex_file.get() == nullptr) {
if (error_code != ZipOpenErrorCode::kEntryNotFound) {
- LOG(WARNING) << error_msg;
+ LOG(WARNING) << "Zip open failed: " << *error_msg;
}
break;
} else {
@@ -497,9 +520,19 @@ DexFile::DexFile(const uint8_t* base,
method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
+ method_handles_(nullptr),
+ num_method_handles_(0),
+ call_site_ids_(nullptr),
+ num_call_site_ids_(0),
oat_dex_file_(oat_dex_file) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
+ // Check base (=header) alignment.
+ // Must be 4-byte aligned to avoid undefined behavior when accessing
+ // any of the sections via a pointer.
+ CHECK_ALIGNED(begin_, alignof(Header));
+
+ InitializeSectionsFromMapList();
}
DexFile::~DexFile() {
@@ -540,6 +573,29 @@ bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
return true;
}
+void DexFile::InitializeSectionsFromMapList() {
+ const MapList* map_list = reinterpret_cast<const MapList*>(begin_ + header_->map_off_);
+ const size_t count = map_list->size_;
+
+ size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
+ if (header_->map_off_ >= map_limit || map_limit > size_) {
+ // Overflow or out out of bounds. The dex file verifier runs after
+ // this method and will reject the file as it is malformed.
+ return;
+ }
+
+ for (size_t i = 0; i < count; ++i) {
+ const MapItem& map_item = map_list->list_[i];
+ if (map_item.type_ == kDexTypeMethodHandleItem) {
+ method_handles_ = reinterpret_cast<const MethodHandleItem*>(begin_ + map_item.offset_);
+ num_method_handles_ = map_item.size_;
+ } else if (map_item.type_ == kDexTypeCallSiteIdItem) {
+ call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(begin_ + map_item.offset_);
+ num_call_site_ids_ = map_item.size_;
+ }
+ }
+}
+
bool DexFile::IsMagicValid(const uint8_t* magic) {
return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
}
@@ -1339,24 +1395,20 @@ void ClassDataItemIterator::ReadClassDataMethod() {
}
}
-EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(const DexFile& dex_file,
- const DexFile::ClassDef& class_def)
+EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file,
+ const uint8_t* array_data)
: dex_file_(dex_file),
array_size_(),
pos_(-1),
+ ptr_(array_data),
type_(kByte) {
- ptr_ = dex_file_.GetEncodedStaticFieldValuesArray(class_def);
- if (ptr_ == nullptr) {
- array_size_ = 0;
- } else {
- array_size_ = DecodeUnsignedLeb128(&ptr_);
- }
+ array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0;
if (array_size_ > 0) {
Next();
}
}
-void EncodedStaticFieldValueIterator::Next() {
+void EncodedArrayValueIterator::Next() {
pos_++;
if (pos_ >= array_size_) {
return;
@@ -1396,6 +1448,8 @@ void EncodedStaticFieldValueIterator::Next() {
break;
case kString:
case kType:
+ case kMethodType:
+ case kMethodHandle:
jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false);
break;
case kField:
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cb7f174787..58b8e792ee 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -103,7 +103,7 @@ class DexFile {
};
// Map item type codes.
- enum {
+ enum MapItemType : uint16_t { // private
kDexTypeHeaderItem = 0x0000,
kDexTypeStringIdItem = 0x0001,
kDexTypeTypeIdItem = 0x0002,
@@ -111,6 +111,8 @@ class DexFile {
kDexTypeFieldIdItem = 0x0004,
kDexTypeMethodIdItem = 0x0005,
kDexTypeClassDefItem = 0x0006,
+ kDexTypeCallSiteIdItem = 0x0007,
+ kDexTypeMethodHandleItem = 0x0008,
kDexTypeMapList = 0x1000,
kDexTypeTypeList = 0x1001,
kDexTypeAnnotationSetRefList = 0x1002,
@@ -260,6 +262,37 @@ class DexFile {
DISALLOW_COPY_AND_ASSIGN(TypeList);
};
+ // MethodHandle Types
+ enum class MethodHandleType : uint16_t { // private
+ kStaticPut = 0x0000, // a setter for a given static field.
+ kStaticGet = 0x0001, // a getter for a given static field.
+ kInstancePut = 0x0002, // a setter for a given instance field.
+ kInstanceGet = 0x0003, // a getter for a given instance field.
+ kInvokeStatic = 0x0004, // an invoker for a given static method.
+ kInvokeInstance = 0x0005, // invoke_instance : an invoker for a given instance method. This
+ // can be any non-static method on any class (or interface) except
+ // for “<init>”.
+ kInvokeConstructor = 0x0006, // an invoker for a given constructor.
+ kLast = kInvokeConstructor
+ };
+
+ // raw method_handle_item
+ struct MethodHandleItem {
+ uint16_t method_handle_type_;
+ uint16_t reserved1_; // Reserved for future use.
+ uint16_t field_or_method_idx_; // Field index for accessors, method index otherwise.
+ uint16_t reserved2_; // Reserved for future use.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MethodHandleItem);
+ };
+
+ // raw call_site_id_item
+ struct CallSiteIdItem {
+ uint32_t data_off_; // Offset into data section pointing to encoded array items.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem);
+ };
+
// Raw code_item.
struct CodeItem {
uint16_t registers_size_; // the number of registers used by this code
@@ -302,6 +335,8 @@ class DexFile {
kDexAnnotationLong = 0x06,
kDexAnnotationFloat = 0x10,
kDexAnnotationDouble = 0x11,
+ kDexAnnotationMethodType = 0x15,
+ kDexAnnotationMethodHandle = 0x16,
kDexAnnotationString = 0x17,
kDexAnnotationType = 0x18,
kDexAnnotationField = 0x19,
@@ -389,11 +424,18 @@ class DexFile {
struct AnnotationValue;
- // Returns the checksum of a file for comparison with GetLocationChecksum().
- // For .dex files, this is the header checksum.
- // For zip files, this is the classes.dex zip entry CRC32 checksum.
- // Return true if the checksum could be found, false otherwise.
- static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
+ // Returns the checksums of a file for comparison with GetLocationChecksum().
+ // For .dex files, this is the single header checksum.
+ // For zip files, this is the zip entry CRC32 checksum for classes.dex and
+ // each additional multidex entry classes2.dex, classes3.dex, etc.
+ // Return true if the checksums could be found, false otherwise.
+ static bool GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg);
+
+ // Check whether a location denotes a multidex dex file. This is a very simple check: returns
+ // whether the string contains the separator character.
+ static bool IsMultiDexLocation(const char* location);
// Opens .dex file, backed by existing memory
static std::unique_ptr<const DexFile> Open(const uint8_t* base,
@@ -683,6 +725,24 @@ class DexFile {
}
}
+ uint32_t NumMethodHandles() const {
+ return num_method_handles_;
+ }
+
+ const MethodHandleItem& GetMethodHandle(uint32_t idx) const {
+ CHECK_LT(idx, NumMethodHandles());
+ return method_handles_[idx];
+ }
+
+ uint32_t NumCallSiteIds() const {
+ return num_call_site_ids_;
+ }
+
+ const CallSiteIdItem& GetCallSiteId(uint32_t idx) const {
+ CHECK_LT(idx, NumCallSiteIds());
+ return call_site_ids_[idx];
+ }
+
// Returns a pointer to the raw memory mapped class_data_item
const uint8_t* GetClassData(const ClassDef& class_def) const {
if (class_def.class_data_off_ == 0) {
@@ -761,6 +821,10 @@ class DexFile {
}
}
+ const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const {
+ return begin_ + call_site_id.data_off_;
+ }
+
static const TryItem* GetTryItems(const CodeItem& code_item, uint32_t offset);
// Get the base of the encoded data for the given DexCode.
@@ -1101,9 +1165,8 @@ class DexFile {
// Returns true if the header magic and version numbers are of the expected values.
bool CheckMagicAndVersion(std::string* error_msg) const;
- // Check whether a location denotes a multidex dex file. This is a very simple check: returns
- // whether the string contains the separator character.
- static bool IsMultiDexLocation(const char* location);
+ // Initialize section info for sections only found in map. Returns true on success.
+ void InitializeSectionsFromMapList();
// The base address of the memory mapping.
const uint8_t* const begin_;
@@ -1143,6 +1206,18 @@ class DexFile {
// Points to the base of the class definition list.
const ClassDef* const class_defs_;
+ // Points to the base of the method handles list.
+ const MethodHandleItem* method_handles_;
+
+ // Number of elements in the method handles list.
+ size_t num_method_handles_;
+
+ // Points to the base of the call sites id list.
+ const CallSiteIdItem* call_site_ids_;
+
+ // Number of elements in the call sites list.
+ size_t num_call_site_ids_;
+
// If this dex file was loaded from an oat file, oat_dex_file_ contains a
// pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
// null.
@@ -1409,32 +1484,33 @@ class ClassDataItemIterator {
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator);
};
-class EncodedStaticFieldValueIterator {
+class EncodedArrayValueIterator {
public:
- EncodedStaticFieldValueIterator(const DexFile& dex_file,
- const DexFile::ClassDef& class_def);
+ EncodedArrayValueIterator(const DexFile& dex_file, const uint8_t* array_data);
bool HasNext() const { return pos_ < array_size_; }
void Next();
enum ValueType {
- kByte = 0x00,
- kShort = 0x02,
- kChar = 0x03,
- kInt = 0x04,
- kLong = 0x06,
- kFloat = 0x10,
- kDouble = 0x11,
- kString = 0x17,
- kType = 0x18,
- kField = 0x19,
- kMethod = 0x1a,
- kEnum = 0x1b,
- kArray = 0x1c,
- kAnnotation = 0x1d,
- kNull = 0x1e,
- kBoolean = 0x1f
+ kByte = 0x00,
+ kShort = 0x02,
+ kChar = 0x03,
+ kInt = 0x04,
+ kLong = 0x06,
+ kFloat = 0x10,
+ kDouble = 0x11,
+ kMethodType = 0x15,
+ kMethodHandle = 0x16,
+ kString = 0x17,
+ kType = 0x18,
+ kField = 0x19,
+ kMethod = 0x1a,
+ kEnum = 0x1b,
+ kArray = 0x1c,
+ kAnnotation = 0x1d,
+ kNull = 0x1e,
+ kBoolean = 0x1f,
};
ValueType GetValueType() const { return type_; }
@@ -1452,10 +1528,38 @@ class EncodedStaticFieldValueIterator {
jvalue jval_; // Value of current encoded value.
private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedArrayValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const EncodedArrayValueIterator::ValueType& code);
+
+class EncodedStaticFieldValueIterator : public EncodedArrayValueIterator {
+ public:
+ EncodedStaticFieldValueIterator(const DexFile& dex_file,
+ const DexFile::ClassDef& class_def)
+ : EncodedArrayValueIterator(dex_file,
+ dex_file.GetEncodedStaticFieldValuesArray(class_def))
+ {}
+
+ private:
DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedStaticFieldValueIterator);
};
std::ostream& operator<<(std::ostream& os, const EncodedStaticFieldValueIterator::ValueType& code);
+class CallSiteArrayValueIterator : public EncodedArrayValueIterator {
+ public:
+ CallSiteArrayValueIterator(const DexFile& dex_file,
+ const DexFile::CallSiteIdItem& call_site_id)
+ : EncodedArrayValueIterator(dex_file,
+ dex_file.GetCallSiteEncodedValuesArray(call_site_id))
+ {}
+
+ uint32_t Size() const { return array_size_; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CallSiteArrayValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code);
+
class CatchHandlerIterator {
public:
CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address);
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 16a447b0a6..a95f94cabb 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -252,7 +252,7 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> annotation_class(hs.NewHandle(
class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get())));
- if (annotation_class.Get() == nullptr) {
+ if (annotation_class == nullptr) {
LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index;
DCHECK(Thread::Current()->IsExceptionPending());
Thread::Current()->ClearException();
@@ -481,7 +481,7 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass,
break;
}
case DexFile::kDexAnnotationArray:
- if (result_style == DexFile::kAllRaw || array_class.Get() == nullptr) {
+ if (result_style == DexFile::kAllRaw || array_class == nullptr) {
return false;
} else {
ScopedObjectAccessUnchecked soa(self);
@@ -491,7 +491,7 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass,
Handle<mirror::Array> new_array(hs.NewHandle(mirror::Array::Alloc<true>(
self, array_class.Get(), size, array_class->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
- if (new_array.Get() == nullptr) {
+ if (new_array == nullptr) {
LOG(ERROR) << "Annotation element array allocation failed with size " << size;
return false;
}
@@ -631,8 +631,8 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass,
}
Handle<mirror::Method> method_object(hs.NewHandle(method_obj_ptr));
- if (new_member.Get() == nullptr || string_name.Get() == nullptr ||
- method_object.Get() == nullptr || method_return.Get() == nullptr) {
+ if (new_member == nullptr || string_name == nullptr ||
+ method_object == nullptr || method_return == nullptr) {
LOG(ERROR) << StringPrintf("Failed creating annotation element (m=%p n=%p a=%p r=%p",
new_member.Get(), string_name.Get(), method_object.Get(), method_return.Get());
return nullptr;
@@ -740,7 +740,7 @@ mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> kla
ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
Handle<mirror::Class> string_array_class(hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
- if (string_array_class.Get() == nullptr) {
+ if (string_array_class == nullptr) {
return nullptr;
}
mirror::Object* obj =
@@ -766,7 +766,7 @@ mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass,
ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
Handle<mirror::Class> class_array_class(hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class)));
- if (class_array_class.Get() == nullptr) {
+ if (class_array_class == nullptr) {
return nullptr;
}
mirror::Object* obj =
@@ -796,7 +796,7 @@ mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet(
uint32_t size = annotation_set->size_;
Handle<mirror::ObjectArray<mirror::Object>> result(hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), size)));
- if (result.Get() == nullptr) {
+ if (result == nullptr) {
return nullptr;
}
@@ -854,7 +854,7 @@ mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList(
}
Handle<mirror::ObjectArray<mirror::Object>> annotation_array_array(hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_array_class, size)));
- if (annotation_array_array.Get() == nullptr) {
+ if (annotation_array_array == nullptr) {
LOG(ERROR) << "Annotation set ref array allocation failed";
return nullptr;
}
@@ -1056,7 +1056,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method,
ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
Handle<mirror::Class> string_array_class(hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
- if (UNLIKELY(string_array_class.Get() == nullptr)) {
+ if (UNLIKELY(string_array_class == nullptr)) {
return false;
}
@@ -1067,13 +1067,13 @@ bool GetParametersMetadataForMethod(ArtMethod* method,
"names",
string_array_class,
DexFile::kDexAnnotationArray));
- if (names_obj.Get() == nullptr) {
+ if (names_obj == nullptr) {
return false;
}
// Extract the parameters' access flags int[].
Handle<mirror::Class> int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass()));
- if (UNLIKELY(int_array_class.Get() == nullptr)) {
+ if (UNLIKELY(int_array_class == nullptr)) {
return false;
}
Handle<mirror::Object> access_flags_obj =
@@ -1082,7 +1082,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method,
"accessFlags",
int_array_class,
DexFile::kDexAnnotationArray));
- if (access_flags_obj.Get() == nullptr) {
+ if (access_flags_obj == nullptr) {
return false;
}
@@ -1146,7 +1146,7 @@ mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> kla
ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
Handle<mirror::Class> class_array_class(hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class)));
- if (class_array_class.Get() == nullptr) {
+ if (class_array_class == nullptr) {
return nullptr;
}
mirror::Object* obj =
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 9dca4c0621..9131715fec 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -326,12 +326,32 @@ TEST_F(DexFileTest, GetLocationChecksum) {
}
TEST_F(DexFileTest, GetChecksum) {
- uint32_t checksum;
+ std::vector<uint32_t> checksums;
ScopedObjectAccess soa(Thread::Current());
std::string error_msg;
- EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileNames()[0].c_str(), &checksum, &error_msg))
+ EXPECT_TRUE(DexFile::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), &checksums, &error_msg))
<< error_msg;
- EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
+ ASSERT_EQ(1U, checksums.size());
+ EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]);
+}
+
+TEST_F(DexFileTest, GetMultiDexChecksums) {
+ std::string error_msg;
+ std::vector<uint32_t> checksums;
+ std::string multidex_file = GetTestDexFileName("MultiDex");
+ EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(),
+ &checksums,
+ &error_msg)) << error_msg;
+
+ std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex");
+ ASSERT_EQ(2U, dexes.size());
+ ASSERT_EQ(2U, checksums.size());
+
+ EXPECT_EQ(dexes[0]->GetLocation(), DexFile::GetMultiDexLocation(0, multidex_file.c_str()));
+ EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]);
+
+ EXPECT_EQ(dexes[1]->GetLocation(), DexFile::GetMultiDexLocation(1, multidex_file.c_str()));
+ EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
}
TEST_F(DexFileTest, ClassDefs) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 318123edcd..0b3f16a3cb 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -46,8 +46,8 @@ static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) {
return (high == 0);
}
-static uint32_t MapTypeToBitMask(uint32_t map_type) {
- switch (map_type) {
+static uint32_t MapTypeToBitMask(DexFile::MapItemType map_item_type) {
+ switch (map_item_type) {
case DexFile::kDexTypeHeaderItem: return 1 << 0;
case DexFile::kDexTypeStringIdItem: return 1 << 1;
case DexFile::kDexTypeTypeIdItem: return 1 << 2;
@@ -55,23 +55,25 @@ static uint32_t MapTypeToBitMask(uint32_t map_type) {
case DexFile::kDexTypeFieldIdItem: return 1 << 4;
case DexFile::kDexTypeMethodIdItem: return 1 << 5;
case DexFile::kDexTypeClassDefItem: return 1 << 6;
- case DexFile::kDexTypeMapList: return 1 << 7;
- case DexFile::kDexTypeTypeList: return 1 << 8;
- case DexFile::kDexTypeAnnotationSetRefList: return 1 << 9;
- case DexFile::kDexTypeAnnotationSetItem: return 1 << 10;
- case DexFile::kDexTypeClassDataItem: return 1 << 11;
- case DexFile::kDexTypeCodeItem: return 1 << 12;
- case DexFile::kDexTypeStringDataItem: return 1 << 13;
- case DexFile::kDexTypeDebugInfoItem: return 1 << 14;
- case DexFile::kDexTypeAnnotationItem: return 1 << 15;
- case DexFile::kDexTypeEncodedArrayItem: return 1 << 16;
- case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 17;
+ case DexFile::kDexTypeCallSiteIdItem: return 1 << 7;
+ case DexFile::kDexTypeMethodHandleItem: return 1 << 8;
+ case DexFile::kDexTypeMapList: return 1 << 9;
+ case DexFile::kDexTypeTypeList: return 1 << 10;
+ case DexFile::kDexTypeAnnotationSetRefList: return 1 << 11;
+ case DexFile::kDexTypeAnnotationSetItem: return 1 << 12;
+ case DexFile::kDexTypeClassDataItem: return 1 << 13;
+ case DexFile::kDexTypeCodeItem: return 1 << 14;
+ case DexFile::kDexTypeStringDataItem: return 1 << 15;
+ case DexFile::kDexTypeDebugInfoItem: return 1 << 16;
+ case DexFile::kDexTypeAnnotationItem: return 1 << 17;
+ case DexFile::kDexTypeEncodedArrayItem: return 1 << 18;
+ case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 19;
}
return 0;
}
-static bool IsDataSectionType(uint32_t map_type) {
- switch (map_type) {
+static bool IsDataSectionType(DexFile::MapItemType map_item_type) {
+ switch (map_item_type) {
case DexFile::kDexTypeHeaderItem:
case DexFile::kDexTypeStringIdItem:
case DexFile::kDexTypeTypeIdItem:
@@ -80,6 +82,20 @@ static bool IsDataSectionType(uint32_t map_type) {
case DexFile::kDexTypeMethodIdItem:
case DexFile::kDexTypeClassDefItem:
return false;
+ case DexFile::kDexTypeCallSiteIdItem:
+ case DexFile::kDexTypeMethodHandleItem:
+ case DexFile::kDexTypeMapList:
+ case DexFile::kDexTypeTypeList:
+ case DexFile::kDexTypeAnnotationSetRefList:
+ case DexFile::kDexTypeAnnotationSetItem:
+ case DexFile::kDexTypeClassDataItem:
+ case DexFile::kDexTypeCodeItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ case DexFile::kDexTypeAnnotationsDirectoryItem:
+ return true;
}
return true;
}
@@ -455,7 +471,8 @@ bool DexFileVerifier::CheckMap() {
return false;
}
- if (IsDataSectionType(item->type_)) {
+ DexFile::MapItemType item_type = static_cast<DexFile::MapItemType>(item->type_);
+ if (IsDataSectionType(item_type)) {
uint32_t icount = item->size_;
if (UNLIKELY(icount > data_items_left)) {
ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount);
@@ -465,7 +482,7 @@ bool DexFileVerifier::CheckMap() {
data_item_count += icount;
}
- uint32_t bit = MapTypeToBitMask(item->type_);
+ uint32_t bit = MapTypeToBitMask(item_type);
if (UNLIKELY(bit == 0)) {
ErrorStringPrintf("Unknown map section type %x", item->type_);
@@ -837,6 +854,28 @@ bool DexFileVerifier::CheckEncodedValue() {
return false;
}
break;
+ case DexFile::kDexAnnotationMethodType: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method type size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, header_->proto_ids_size_, "method_type value")) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationMethodHandle: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method handle size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, dex_file_->NumMethodHandles(), "method_handle value")) {
+ return false;
+ }
+ break;
+ }
default:
ErrorStringPrintf("Bogus encoded_value value_type %x", value_type);
return false;
@@ -1455,7 +1494,7 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
}
bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count,
- uint16_t type) {
+ DexFile::MapItemType type) {
// Get the right alignment mask for the type of section.
size_t alignment_mask;
switch (type) {
@@ -1481,6 +1520,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c
}
// Check depending on the section type.
+ const uint8_t* start_ptr = ptr_;
switch (type) {
case DexFile::kDexTypeStringIdItem: {
if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) {
@@ -1524,6 +1564,20 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c
ptr_ += sizeof(DexFile::ClassDef);
break;
}
+ case DexFile::kDexTypeCallSiteIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::CallSiteIdItem), "call_site_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::CallSiteIdItem);
+ break;
+ }
+ case DexFile::kDexTypeMethodHandleItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodHandleItem), "method_handles")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::MethodHandleItem);
+ break;
+ }
case DexFile::kDexTypeTypeList: {
if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) {
return false;
@@ -1584,9 +1638,14 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c
}
break;
}
- default:
- ErrorStringPrintf("Unknown map item type %x", type);
- return false;
+ case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeMapList:
+ break;
+ }
+
+ if (start_ptr == ptr_) {
+ ErrorStringPrintf("Unknown map item type %x", type);
+ return false;
}
if (IsDataSectionType(type)) {
@@ -1610,7 +1669,9 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c
return true;
}
-bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count, uint16_t type) {
+bool DexFileVerifier::CheckIntraIdSection(size_t offset,
+ uint32_t count,
+ DexFile::MapItemType type) {
uint32_t expected_offset;
uint32_t expected_size;
@@ -1658,7 +1719,9 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count, uint16_
return CheckIntraSectionIterate(offset, count, type);
}
-bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count, uint16_t type) {
+bool DexFileVerifier::CheckIntraDataSection(size_t offset,
+ uint32_t count,
+ DexFile::MapItemType type) {
size_t data_start = header_->data_off_;
size_t data_end = data_start + header_->data_size_;
@@ -1684,16 +1747,16 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count, uint1
bool DexFileVerifier::CheckIntraSection() {
const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
const DexFile::MapItem* item = map->list_;
-
- uint32_t count = map->size_;
size_t offset = 0;
+ uint32_t count = map->size_;
ptr_ = begin_;
// Check the items listed in the map.
while (count--) {
+ const size_t current_offset = offset;
uint32_t section_offset = item->offset_;
uint32_t section_count = item->size_;
- uint16_t type = item->type_;
+ DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
// Check for padding and overlap between items.
if (!CheckPadding(offset, section_offset)) {
@@ -1741,6 +1804,11 @@ bool DexFileVerifier::CheckIntraSection() {
ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
break;
+ case DexFile::kDexTypeMethodHandleItem:
+ case DexFile::kDexTypeCallSiteIdItem:
+ CheckIntraSectionIterate(section_offset, section_count, type);
+ offset = ptr_ - begin_;
+ break;
case DexFile::kDexTypeTypeList:
case DexFile::kDexTypeAnnotationSetRefList:
case DexFile::kDexTypeAnnotationSetItem:
@@ -1756,7 +1824,9 @@ bool DexFileVerifier::CheckIntraSection() {
}
offset = ptr_ - begin_;
break;
- default:
+ }
+
+ if (offset == current_offset) {
ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -2237,6 +2307,92 @@ bool DexFileVerifier::CheckInterClassDefItem() {
return true;
}
+bool DexFileVerifier::CheckInterCallSiteIdItem() {
+ const DexFile::CallSiteIdItem* item = reinterpret_cast<const DexFile::CallSiteIdItem*>(ptr_);
+
+ // Check call site referenced by item is in encoded array section.
+ if (!CheckOffsetToTypeMap(item->data_off_, DexFile::kDexTypeEncodedArrayItem)) {
+ ErrorStringPrintf("Invalid offset in CallSideIdItem");
+ return false;
+ }
+
+ CallSiteArrayValueIterator it(*dex_file_, *item);
+
+ // Check Method Handle
+ if (!it.HasNext() || it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) {
+ ErrorStringPrintf("CallSiteArray missing method handle");
+ return false;
+ }
+
+ uint32_t handle_index = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (handle_index >= dex_file_->NumMethodHandles()) {
+ ErrorStringPrintf("CallSite has bad method handle id: %x", handle_index);
+ return false;
+ }
+
+ // Check target method name.
+ it.Next();
+ if (!it.HasNext() ||
+ it.GetValueType() != EncodedArrayValueIterator::ValueType::kString) {
+ ErrorStringPrintf("CallSiteArray missing target method name");
+ return false;
+ }
+
+ uint32_t name_index = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (name_index >= dex_file_->NumStringIds()) {
+ ErrorStringPrintf("CallSite has bad method name id: %x", name_index);
+ return false;
+ }
+
+ // Check method type.
+ it.Next();
+ if (!it.HasNext() ||
+ it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodType) {
+ ErrorStringPrintf("CallSiteArray missing method type");
+ return false;
+ }
+
+ uint32_t proto_index = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (proto_index >= dex_file_->NumProtoIds()) {
+ ErrorStringPrintf("CallSite has bad method type: %x", proto_index);
+ return false;
+ }
+
+ ptr_ += sizeof(DexFile::CallSiteIdItem);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterMethodHandleItem() {
+ const DexFile::MethodHandleItem* item = reinterpret_cast<const DexFile::MethodHandleItem*>(ptr_);
+
+ DexFile::MethodHandleType method_handle_type =
+ static_cast<DexFile::MethodHandleType>(item->method_handle_type_);
+ if (method_handle_type > DexFile::MethodHandleType::kLast) {
+ ErrorStringPrintf("Bad method handle type %x", item->method_handle_type_);
+ return false;
+ }
+
+ uint32_t index = item->field_or_method_idx_;
+ switch (method_handle_type) {
+ case DexFile::MethodHandleType::kStaticPut:
+ case DexFile::MethodHandleType::kStaticGet:
+ case DexFile::MethodHandleType::kInstancePut:
+ case DexFile::MethodHandleType::kInstanceGet: {
+ LOAD_FIELD(field, index, "method_handle_item field_idx", return false);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic:
+ case DexFile::MethodHandleType::kInvokeInstance:
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ LOAD_METHOD(method, index, "method_handle_item method_idx", return false);
+ break;
+ }
+ }
+
+ ptr_ += sizeof(DexFile::MethodHandleItem);
+ return true;
+}
+
bool DexFileVerifier::CheckInterAnnotationSetRefList() {
const DexFile::AnnotationSetRefList* list =
reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_);
@@ -2386,7 +2542,9 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
return true;
}
-bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type) {
+bool DexFileVerifier::CheckInterSectionIterate(size_t offset,
+ uint32_t count,
+ DexFile::MapItemType type) {
// Get the right alignment mask for the type of section.
size_t alignment_mask;
switch (type) {
@@ -2405,8 +2563,22 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui
ptr_ = begin_ + new_offset;
const uint8_t* prev_ptr = ptr_;
+ if (MapTypeToBitMask(type) == 0) {
+ ErrorStringPrintf("Unknown map item type %x", type);
+ return false;
+ }
+
// Check depending on the section type.
switch (type) {
+ case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeMapList:
+ case DexFile::kDexTypeTypeList:
+ case DexFile::kDexTypeCodeItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ break;
case DexFile::kDexTypeStringIdItem: {
if (!CheckInterStringIdItem()) {
return false;
@@ -2451,6 +2623,18 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui
}
break;
}
+ case DexFile::kDexTypeCallSiteIdItem: {
+ if (!CheckInterCallSiteIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeMethodHandleItem: {
+ if (!CheckInterMethodHandleItem()) {
+ return false;
+ }
+ break;
+ }
case DexFile::kDexTypeAnnotationSetRefList: {
if (!CheckInterAnnotationSetRefList()) {
return false;
@@ -2483,9 +2667,6 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui
}
break;
}
- default:
- ErrorStringPrintf("Unknown map item type %x", type);
- return false;
}
previous_item_ = prev_ptr;
@@ -2504,7 +2685,8 @@ bool DexFileVerifier::CheckInterSection() {
while (count--) {
uint32_t section_offset = item->offset_;
uint32_t section_count = item->size_;
- uint16_t type = item->type_;
+ DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
+ bool found = false;
switch (type) {
case DexFile::kDexTypeHeaderItem:
@@ -2515,6 +2697,7 @@ bool DexFileVerifier::CheckInterSection() {
case DexFile::kDexTypeDebugInfoItem:
case DexFile::kDexTypeAnnotationItem:
case DexFile::kDexTypeEncodedArrayItem:
+ found = true;
break;
case DexFile::kDexTypeStringIdItem:
case DexFile::kDexTypeTypeIdItem:
@@ -2522,6 +2705,8 @@ bool DexFileVerifier::CheckInterSection() {
case DexFile::kDexTypeFieldIdItem:
case DexFile::kDexTypeMethodIdItem:
case DexFile::kDexTypeClassDefItem:
+ case DexFile::kDexTypeCallSiteIdItem:
+ case DexFile::kDexTypeMethodHandleItem:
case DexFile::kDexTypeAnnotationSetRefList:
case DexFile::kDexTypeAnnotationSetItem:
case DexFile::kDexTypeClassDataItem:
@@ -2529,11 +2714,14 @@ bool DexFileVerifier::CheckInterSection() {
if (!CheckInterSectionIterate(section_offset, section_count, type)) {
return false;
}
+ found = true;
break;
}
- default:
- ErrorStringPrintf("Unknown map item type %x", type);
- return false;
+ }
+
+ if (!found) {
+ ErrorStringPrintf("Unknown map item type %x", item->type_);
+ return false;
}
item++;
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index ae206132dd..71b316c403 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -122,9 +122,9 @@ class DexFileVerifier {
bool CheckIntraAnnotationItem();
bool CheckIntraAnnotationsDirectoryItem();
- bool CheckIntraSectionIterate(size_t offset, uint32_t count, uint16_t type);
- bool CheckIntraIdSection(size_t offset, uint32_t count, uint16_t type);
- bool CheckIntraDataSection(size_t offset, uint32_t count, uint16_t type);
+ bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
+ bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type);
+ bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type);
bool CheckIntraSection();
bool CheckOffsetToTypeMap(size_t offset, uint16_t type);
@@ -140,12 +140,14 @@ class DexFileVerifier {
bool CheckInterFieldIdItem();
bool CheckInterMethodIdItem();
bool CheckInterClassDefItem();
+ bool CheckInterCallSiteIdItem();
+ bool CheckInterMethodHandleItem();
bool CheckInterAnnotationSetRefList();
bool CheckInterAnnotationSetItem();
bool CheckInterClassDataItem();
bool CheckInterAnnotationsDirectoryItem();
- bool CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type);
+ bool CheckInterSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
bool CheckInterSection();
// Load a string by (type) index. Checks whether the index is in bounds, printing the error if
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index c56b20057d..7736f3d615 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -1885,4 +1885,209 @@ TEST_F(DexFileVerifierTest, BadInitSignature) {
&error_msg));
}
+static const char* kInvokeCustomDexFiles[] = {
+ // TODO(oth): Revisit this test when we have smali / dx support.
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test001/Tests.java
+ "ZGV4CjAzOAAEj12s/acmmdGuDL92SWSBh6iLBjxgomWkCAAAcAAAAHhWNBIAAAAAAAAAALwHAAAx"
+ "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAAAMBgAAmAIAAMID"
+ "AADKAwAAzQMAANIDAADhAwAA5AMAAOoDAAAfBAAAUgQAAIMEAAC4BAAA1AQAAOsEAAD+BAAAEgUA"
+ "ACYFAAA6BQAAUQUAAG4FAACTBQAAtAUAAN0FAAD/BQAAHgYAADgGAABKBgAAVgYAAFkGAABdBgAA"
+ "YgYAAGYGAAB7BgAAgAYAAI8GAACdBgAAtAYAAMMGAADSBgAA3gYAAPIGAAD4BgAABgcAAA4HAAAU"
+ "BwAAGgcAAB8HAAAoBwAANAcAADoHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A"
+ "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA"
+ "AAAAAACMAwAABQAAAAwAAACUAwAABQAAAA4AAACgAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA"
+ "GwAAABQAAACsAwAAHAAAABQAAACMAwAAHQAAABQAAAC0AwAAHQAAABQAAAC8AwAAAwADAAMAAAAE"
+ "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA"
+ "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAJYHAACWBwAABAAA"
+ "AAEAAAAIAAAAAAAAABkAAABkAwAAnQcAAAAAAAAEAAAAAgAAAAEAAABjBwAAAQAAAIsHAAACAAAA"
+ "iwcAAJMHAAABAAEAAQAAAEEHAAAEAAAAcBAGAAAADgADAAIAAAAAAEYHAAADAAAAkAABAg8AAAAF"
+ "AAMABAAAAE0HAAAQAAAAcQAJAAAADAAcAQQAbkAIABBDDAAiAQ0AcCAHAAEAEQEEAAEAAgAAAFYH"
+ "AAAMAAAAYgACABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAABdBwAACwAAABIgEjH8IAEAEAAK"
+ "ABJRcSAKAAEADgAAAAAAAAAAAAAAAwAAAAAAAAABAAAAmAIAAAIAAACgAgAABAAAAKgCAAACAAAA"
+ "AAAAAAMAAAAPAAkAEQAAAAMAAAAHAAkAEQAAAAEAAAAAAAAAAQAAAA4AAAABAAAAFQAGPGluaXQ+"
+ "AAFJAANJSUkADUlOVk9LRV9TVEFUSUMAAUwABExMTEwAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90"
+ "YXRpb25zL0NhbGxlZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlv"
+ "bnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0"
+ "aG9kSGFuZGxlS2luZDsAM0xjb20vYW5kcm9pZC9qYWNrL2phdmE3L2ludm9rZWN1c3RvbS90ZXN0"
+ "MDAxL1Rlc3RzOwAaTGRhbHZpay9hbm5vdGF0aW9uL1Rocm93czsAFUxqYXZhL2lvL1ByaW50U3Ry"
+ "ZWFtOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9T"
+ "dHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEv"
+ "bGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRl"
+ "OwAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0"
+ "aG9kSGFuZGxlcyRMb29rdXA7ACBMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGph"
+ "dmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTsAGExqdW5pdC9mcmFtZXdvcmsvQXNzZXJ0OwAQTG9y"
+ "Zy9qdW5pdC9UZXN0OwAKVGVzdHMuamF2YQABVgACVkkAA1ZJSQACVkwAE1tMamF2YS9sYW5nL1N0"
+ "cmluZzsAA2FkZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQu"
+ "MC1lbmcADWVuY2xvc2luZ1R5cGUADWZpZWxkQ2FsbFNpdGUACmZpbmRTdGF0aWMAEmludm9rZU1l"
+ "dGhvZEhhbmRsZQAEa2luZAAMbGlua2VyTWV0aG9kAAZsb29rdXAABG1haW4ABG5hbWUAA291dAAH"
+ "cHJpbnRsbgAKcmV0dXJuVHlwZQAEdGVzdAAFdmFsdWUAIgAHDgAvAgAABw4ANQMAAAAHDqUAPwEA"
+ "Bw60ADsABw6lAAABBCAcAhgAGAAmHAEdAgQgHAMYDxgJGBEjGAQnGwArFygrFx8uGAACBQEwHAEY"
+ "CwETAAMWABcfFQABAAQBAQkAgYAEtAUBCswFAQrkBQEJlAYEAbwGAAAAEwAAAAAAAAABAAAAAAAA"
+ "AAEAAAAxAAAAcAAAAAIAAAAWAAAANAEAAAMAAAAJAAAAjAEAAAQAAAADAAAA+AEAAAUAAAALAAAA"
+ "EAIAAAcAAAACAAAAaAIAAAYAAAABAAAAcAIAAAgAAAABAAAAkAIAAAMQAAADAAAAmAIAAAEgAAAF"
+ "AAAAtAIAAAYgAAABAAAAZAMAAAEQAAAGAAAAjAMAAAIgAAAxAAAAwgMAAAMgAAAFAAAAQQcAAAQg"
+ "AAADAAAAYwcAAAUgAAABAAAAlgcAAAAgAAABAAAAnQcAAAAQAAABAAAAvAcAAA==",
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test002/Tests.java
+ "ZGV4CjAzOAAzq3aGAwKhT4QQj4lqNfZJAO8Tm24uTyNICQAAcAAAAHhWNBIAAAAAAAAAAGAIAAA2"
+ "AAAAcAAAABgAAABIAQAACQAAAKgBAAAEAAAAFAIAAA0AAAA0AgAAAQAAAKQCAAB8BgAAzAIAACYE"
+ "AAAwBAAAOAQAAEQEAABHBAAATAQAAE8EAABVBAAAigQAALwEAADtBAAAIgUAAD4FAABVBQAAaAUA"
+ "AH0FAACRBQAApQUAALkFAADQBQAA7QUAABIGAAAzBgAAXAYAAH4GAACdBgAAtwYAAMkGAADPBgAA"
+ "2wYAAN4GAADiBgAA5wYAAOsGAAD/BgAAFAcAABkHAAAoBwAANgcAAE0HAABcBwAAawcAAH4HAACK"
+ "BwAAkAcAAJgHAACeBwAAqgcAALAHAAC1BwAAxgcAAM8HAADbBwAA4QcAAAMAAAAHAAAACAAAAAkA"
+ "AAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAA"
+ "ABgAAAAZAAAAGgAAAB0AAAAhAAAAIgAAAAQAAAAAAAAA8AMAAAYAAAAPAAAA+AMAAAUAAAAQAAAA"
+ "AAAAAAYAAAASAAAABAQAAB0AAAAVAAAAAAAAAB4AAAAVAAAAEAQAAB8AAAAVAAAA8AMAACAAAAAV"
+ "AAAAGAQAACAAAAAVAAAAIAQAAAMAAwACAAAABAANACgAAAAIAAcAGwAAAAsABgAwAAAABAAEAAAA"
+ "AAAEAAQAAQAAAAQAAAAjAAAABAAIAC0AAAAEAAQANAAAAAYABQAyAAAACQAEAAEAAAAMAAQAMQAA"
+ "AA4ABwABAAAAEAABACoAAAARAAIALAAAABIAAwAuAAAAEwAGACUAAAA4CAAAOAgAAAQAAAABAAAA"
+ "CQAAAAAAAAAcAAAA0AMAAD8IAAAAAAAAAQAAAAEAAAABAAAADggAAAIAAAAtCAAANQgAAAgAAAAE"
+ "AAEA6AcAACoAAABxAAoAAAAMABwBBAAbAiMAAABiAwIAYgQCABIVI1UWAGIGAgASB00GBQdxMAsA"
+ "QwUMA25ACQAQMgwAIgEOAHAgCAABAGkBAQAOAA0AbhAHAAAAKPsAAAAAJAABAAEBDCUBAAEAAQAA"
+ "APUHAAAEAAAAcBAGAAAADgADAAIAAAAAAPoHAAADAAAAkAABAg8AAAAEAAEAAgAAAAEIAAAMAAAA"
+ "YgADABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAAAICAAACwAAABIgEjH8IAEAEAAKABJRcSAM"
+ "AAEADgAAAAAAAAAAAAAAAgAAAAAAAAACAAAAzAIAAAQAAADUAgAAAgAAAAAAAAADAAAABwAKABIA"
+ "AAADAAAABwAHABYAAAABAAAAAAAAAAEAAAAPAAAAAQAAABcACDxjbGluaXQ+AAY8aW5pdD4ACkdF"
+ "VF9TVEFUSUMAAUkAA0lJSQABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMv"
+ "Q2FsbGVkQnlJbnZva2VDdXN0b207ADBMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5r"
+ "ZXJGaWVsZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRs"
+ "ZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwMi9UZXN0"
+ "czsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEUxq"
+ "YXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVjdDsA"
+ "EkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93"
+ "YWJsZTsAG0xqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwAjTGphdmEvbGFuZy9pbnZva2UvQ29u"
+ "c3RhbnRDYWxsU2l0ZTsAH0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsAJ0xqYXZhL2xh"
+ "bmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwOwAgTGphdmEvbGFuZy9pbnZva2UvTWV0aG9k"
+ "SGFuZGxlczsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ABhManVuaXQvZnJhbWV3b3Jr"
+ "L0Fzc2VydDsAEExvcmcvanVuaXQvVGVzdDsABFRZUEUAClRlc3RzLmphdmEAAVYAAlZJAANWSUkA"
+ "AlZMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1l"
+ "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQuMC1lbmcADWVuY2xvc2luZ1R5"
+ "cGUADWZpZWxkQ2FsbFNpdGUAEWZpZWxkTWV0aG9kSGFuZGxlAApmaW5kU3RhdGljAARraW5kAAZs"
+ "b29rdXAABG1haW4ACm1ldGhvZFR5cGUABG5hbWUAA291dAAPcHJpbnRTdGFja1RyYWNlAAdwcmlu"
+ "dGxuAApyZXR1cm5UeXBlAAR0ZXN0AAV2YWx1ZQAoAAcOAR0PAnh3Jh4AIQAHDgA2AgAABw4APwEA"
+ "Bw60ADsABw6lAAABBCQcAhgAGAApHAEdAgMnGAQrGwAvFygvFyMzGAACBQE1HAEYDAEUAAMWABcj"
+ "FQABAAQBAQkAiIAE4AUBgYAE0AYBCugGAQmABwQBqAcAAAATAAAAAAAAAAEAAAAAAAAAAQAAADYA"
+ "AABwAAAAAgAAABgAAABIAQAAAwAAAAkAAACoAQAABAAAAAQAAAAUAgAABQAAAA0AAAA0AgAABwAA"
+ "AAIAAACcAgAABgAAAAEAAACkAgAACAAAAAEAAADEAgAAAxAAAAIAAADMAgAAASAAAAUAAADgAgAA"
+ "BiAAAAEAAADQAwAAARAAAAYAAADwAwAAAiAAADYAAAAmBAAAAyAAAAUAAADoBwAABCAAAAMAAAAO"
+ "CAAABSAAAAEAAAA4CAAAACAAAAEAAAA/CAAAABAAAAEAAABgCAAA",
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test003/Tests.java
+ "ZGV4CjAzOABjnhkFatj30/7cHTCJsfr7vAjz9/p+Y+TcCAAAcAAAAHhWNBIAAAAAAAAAAPQHAAAx"
+ "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAABEBgAAmAIAAOoD"
+ "AADyAwAA9QMAAP4DAAANBAAAEAQAABYEAABLBAAAfgQAAK8EAADkBAAAAAUAABcFAAAqBQAAPgUA"
+ "AFIFAABmBQAAfQUAAJoFAAC/BQAA4AUAAAkGAAArBgAASgYAAGQGAAB2BgAAggYAAIUGAACJBgAA"
+ "jgYAAJIGAACnBgAArAYAALsGAADJBgAA4AYAAO8GAAD+BgAACgcAAB4HAAAkBwAAMgcAADoHAABA"
+ "BwAARgcAAEsHAABUBwAAYAcAAGYHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A"
+ "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA"
+ "AAAAAACkAwAABQAAAAwAAAC0AwAABQAAAA4AAADAAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA"
+ "GwAAABQAAADMAwAAHAAAABQAAADUAwAAHQAAABQAAADcAwAAHQAAABQAAADkAwAAAwADAAMAAAAE"
+ "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA"
+ "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAM4HAADOBwAABAAA"
+ "AAEAAAAIAAAAAAAAABkAAAB8AwAA1QcAAAAAAAAEAAAAAgAAAAEAAACTBwAAAQAAAMMHAAACAAAA"
+ "wwcAAMsHAAABAAEAAQAAAG0HAAAEAAAAcBAGAAAADgAHAAYAAAAAAHIHAAAHAAAAkAABArAwsECw"
+ "ULBgDwAAAAUAAwAEAAAAfQcAABAAAABxAAkAAAAMABwBBABuQAgAEEMMACIBDQBwIAcAAQARAQgA"
+ "AQACAAAAhgcAABAAAABiBgIAEhASIRIyEkMSVBJl/QYAAAAACgBuIAUABgAOAAcAAQACAAAAjQcA"
+ "ABAAAAASEBIhEjISQxJUEmX9BgEAAAAKABMBFQBxIAoAAQAOAAAAAAAAAAAAAwAAAAAAAAABAAAA"
+ "mAIAAAIAAACgAgAABAAAAKgCAAAGAAAAAAAAAAAAAAAAAAAAAwAAAA8ACQARAAAAAwAAAAcACQAR"
+ "AAAAAQAAAAAAAAACAAAAAAAAAAEAAAAOAAAAAQAAABUABjxpbml0PgABSQAHSUlJSUlJSQANSU5W"
+ "T0tFX1NUQVRJQwABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvQ2FsbGVk"
+ "QnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5rZXJNZXRo"
+ "b2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9NZXRob2RIYW5kbGVLaW5k"
+ "OwAzTGNvbS9hbmRyb2lkL2phY2svamF2YTcvaW52b2tlY3VzdG9tL3Rlc3QwMDMvVGVzdHM7ABpM"
+ "ZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9s"
+ "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh"
+ "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D"
+ "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n"
+ "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv"
+ "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r"
+ "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7"
+ "AApUZXN0cy5qYXZhAAFWAAJWSQADVklJAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwADYWRkAA1h"
+ "cmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAFWVtaXR0ZXI6IGphY2stNC4wLWVuZwANZW5jbG9z"
+ "aW5nVHlwZQANZmllbGRDYWxsU2l0ZQAKZmluZFN0YXRpYwASaW52b2tlTWV0aG9kSGFuZGxlAARr"
+ "aW5kAAxsaW5rZXJNZXRob2QABmxvb2t1cAAEbWFpbgAEbmFtZQADb3V0AAdwcmludGxuAApyZXR1"
+ "cm5UeXBlAAR0ZXN0AAV2YWx1ZQAiAAcOAC8GAAAAAAAABw4ANQMAAAAHDqUAPwEABw7wADsABw7w"
+ "AAABBCAcBhgAGAAYABgAGAAYACYcAR0CBCAcAxgPGAkYESMYBCcbACsXKCsXHy4YAAIFATAcARgL"
+ "ARMAAxYAFx8VAAEABAEBCQCBgAS0BQEKzAUBCuwFAQmcBgQBzAYAAAATAAAAAAAAAAEAAAAAAAAA"
+ "AQAAADEAAABwAAAAAgAAABYAAAA0AQAAAwAAAAkAAACMAQAABAAAAAMAAAD4AQAABQAAAAsAAAAQ"
+ "AgAABwAAAAIAAABoAgAABgAAAAEAAABwAgAACAAAAAEAAACQAgAAAxAAAAMAAACYAgAAASAAAAUA"
+ "AAC0AgAABiAAAAEAAAB8AwAAARAAAAcAAACkAwAAAiAAADEAAADqAwAAAyAAAAUAAABtBwAABCAA"
+ "AAMAAACTBwAABSAAAAEAAADOBwAAACAAAAEAAADVBwAAABAAAAEAAAD0BwAA",
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test004/Tests.java
+ "ZGV4CjAzOABvUVfbV74qWbSOEsgKP+EzahlNQLW2/8TMDAAAcAAAAHhWNBIAAAAAAAAAAOQLAABS"
+ "AAAAcAAAAB8AAAC4AQAAEAAAADQCAAADAAAA9AIAABIAAAAMAwAAAQAAAKQDAAAACQAAzAMAANYF"
+ "AADZBQAA4QUAAOkFAADsBQAA7wUAAPIFAAD1BQAA/AUAAP8FAAAEBgAAEwYAABYGAAAZBgAAHwYA"
+ "AC8GAABkBgAAjQYAAMAGAADxBgAAJgcAAEUHAABhBwAAeAcAAIoHAACdBwAAsQcAAMUHAADZBwAA"
+ "8AcAAA0IAAAyCAAAUwgAAHwIAACeCAAAvQgAANcIAADpCAAA7AgAAPgIAAD7CAAAAAkAAAYJAAAM"
+ "CQAAEAkAABUJAAAaCQAAHgkAACMJAAAnCQAAKgkAADMJAABICQAATQkAAFwJAABqCQAAdgkAAIQJ"
+ "AACPCQAAmgkAAKYJAACzCQAAygkAANkJAADoCQAA9AkAAAAKAAAKCgAAHgoAACQKAAAyCgAAPQoA"
+ "AEUKAABLCgAAYgoAAGgKAABtCgAAdgoAAIIKAACOCgAAmwoAAKEKAAADAAAABAAAAAUAAAAGAAAA"
+ "CAAAAAsAAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABgAAAAZAAAAGgAAABsAAAAc"
+ "AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJwAAADEAAAAzAAAACQAAAAQA"
+ "AABMBQAADgAAABMAAABUBQAADQAAABUAAAB0BQAADAAAABYAAAAAAAAAJwAAABwAAAAAAAAAKAAA"
+ "ABwAAACABQAAKQAAABwAAACIBQAAKgAAABwAAACUBQAAKwAAABwAAACgBQAALAAAABwAAABMBQAA"
+ "LQAAABwAAACoBQAALwAAABwAAACwBQAALwAAABwAAAC4BQAALgAAABwAAADABQAAMAAAABwAAADI"
+ "BQAALgAAABwAAADQBQAACQAJAAoAAAAKABMAPwAAABEADQBLAAAACgAEAAIAAAAKAAAANAAAAAoA"
+ "AQBFAAAACgAPAEgAAAAKAAQAUAAAAA0ACABMAAAADwAEAAIAAAAUAA0AAgAAABYAAgBAAAAAFwAD"
+ "AEcAAAAZAAUANgAAABkABgA2AAAAGQAHADYAAAAZAAkANgAAABkACgA2AAAAGQALADYAAAAZAAwA"
+ "NgAAABkADgA3AAAAnQsAAJ0LAAAKAAAAAQAAAA8AAAAAAAAAJgAAACQFAADGCwAAAAAAAAQAAAAC"
+ "AAAAAQAAAN4KAAACAAAAegsAAJILAAACAAAAkgsAAJoLAAABAAEAAQAAAKgKAAAEAAAAcBAGAAAA"
+ "DgADAAIAAAAAAK0KAAADAAAAkAABAg8AAAAYAA8ABgAAALQKAABTAAAAcRARAAwAEhJxIA0A0gAT"
+ "AmEAcSAKAOIAEwIABHEgDQDyABISAgAQAHEgDQACABICFAOamTFBAgARAHEwDAADAhYGAAAYApqZ"
+ "mZmZmQFABQQSAHcGCwACABsCBwAAAAgAFABxIBAAAgAcAgoACAAVAHEgDwACABcCFc1bBwUAFgBx"
+ "QA4AMhBxAAkAAAAMAhwDCgBuQAgAMroMAiIDFABwIAcAIwARAwAABAABAAIAAADRCgAADAAAAGIA"
+ "AgASIRIy/CAAACEACgFuIAUAEAAOAAMAAQACAAAA2AoAAAsAAAASIBIx/CABABAACgASUXEgDQAB"
+ "AA4AAAAAAAAAAAAAAAMAAAAAAAAAAQAAAMwDAAACAAAA1AMAAAQAAADgAwAAAgAAAAQABAANAAAA"
+ "FgAQABgAHQAAAAEAGwAEAAMAAgAQAA4ABQAAAAMAAAAOABAAGAAAAAIAAAABAAEAAwAAAAIAAgAC"
+ "AAAAAwAAAAMAAwADAAAAAQAAAAQAAAACAAAABQAFAAIAAAAPAA8AAgAAABAAEAABAAAAFQAAAAEA"
+ "AAAdAAAAAQAAAB4AASgABjwqPjtKKQAGPGluaXQ+AAFCAAFDAAFEAAFGAAVIZWxsbwABSQADSUlJ"
+ "AA1JTlZPS0VfU1RBVElDAAFKAAFMAARMTExMAA5MTExMWkJDU0lGRExMSgAzTGNvbS9hbmRyb2lk"
+ "L2phY2svYW5ub3RhdGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ACdMY29tL2FuZHJvaWQvamFj"
+ "ay9hbm5vdGF0aW9ucy9Db25zdGFudDsAMUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xp"
+ "bmtlck1ldGhvZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhh"
+ "bmRsZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwNC9U"
+ "ZXN0czsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlrL2Fubm90YXRpb24v"
+ "VGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s"
+ "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh"
+ "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D"
+ "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n"
+ "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv"
+ "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r"
+ "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7"
+ "AAFTAApUZXN0cy5qYXZhAAFWAANWQ0MABFZEREQABFZGRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZM"
+ "TAACVloAAVoAB1pCQ1NJRkQAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1lbnRUeXBl"
+ "cwAMYXNzZXJ0RXF1YWxzAAphc3NlcnRUcnVlAAxib29sZWFuVmFsdWUACWJ5dGVWYWx1ZQAJY2hh"
+ "clZhbHVlAApjbGFzc1ZhbHVlAAtkb3VibGVWYWx1ZQAVZW1pdHRlcjogamFjay00LjAtZW5nAA1l"
+ "bmNsb3NpbmdUeXBlAA1maWVsZENhbGxTaXRlAApmaW5kU3RhdGljAApmbG9hdFZhbHVlAAhpbnRW"
+ "YWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAxsaW5rZXJNZXRob2QACWxvbmdWYWx1ZQAG"
+ "bG9va3VwAARtYWluABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MABG5hbWUAA291dAAHcHJpbnRsbgAK"
+ "cmV0dXJuVHlwZQAKc2hvcnRWYWx1ZQALc3RyaW5nVmFsdWUABHRlc3QABXZhbHVlACMABw4ANwIA"
+ "AAcOAD4NAAAAAAAAAAAAAAAAAAcOPEtaWmmWw4d4h6UAUgEABw60AE4ABw6lAAAGBTUcAhgEGARD"
+ "HAEdCAQ1HA0YFhgQGBgYHRgAGAEYGxgEGAMYAhgQGA4YBT4YCkQbAEoXRUkcCh0HATgcAT8dBwE5"
+ "HAEAAR0HATocAQNhHQcBThwBIgAEHQcBQhwBBAEdBwFBHAFwmpkxQR0HATwcAfGamZmZmZkBQB0H"
+ "AU8cARcHHQcBOxwBGAodBwFGHAFmFc1bB0oXNE0YBAILAVEcCRcAFyAXGhciFzIXGhcXFwEXHQIM"
+ "AVEcARgSARoADRYAFzQVAAQBBAEEYSQABAQBcJqZMUHxmpmZmZmZAUAXBxgKZhXNWwcBAAQBAQkA"
+ "gYAE7AcBCoQIAQqcCAEJ1AkEAfwJAAATAAAAAAAAAAEAAAAAAAAAAQAAAFIAAABwAAAAAgAAAB8A"
+ "AAC4AQAAAwAAABAAAAA0AgAABAAAAAMAAAD0AgAABQAAABIAAAAMAwAABwAAAAIAAACcAwAABgAA"
+ "AAEAAACkAwAACAAAAAEAAADEAwAAAxAAAAMAAADMAwAAASAAAAUAAADsAwAABiAAAAEAAAAkBQAA"
+ "ARAAAA0AAABMBQAAAiAAAFIAAADWBQAAAyAAAAUAAACoCgAABCAAAAQAAADeCgAABSAAAAEAAACd"
+ "CwAAACAAAAEAAADGCwAAABAAAAEAAADkCwAA"
+};
+
+TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) {
+ for (size_t i = 0; i < arraysize(kInvokeCustomDexFiles); ++i) {
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kInvokeCustomDexFiles[i], &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good checksum, verify",
+ /*verify_checksum*/ true,
+ &error_msg));
+ // TODO(oth): Test corruptions (b/35308502)
+ }
+}
+
} // namespace art
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 37f3ac92e9..091085a85c 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -407,6 +407,20 @@ std::string Instruction::DumpString(const DexFile* file) const {
break;
}
FALLTHROUGH_INTENDED;
+ case INVOKE_CUSTOM:
+ if (file != nullptr) {
+ os << opcode << " {";
+ uint32_t call_site_idx = VRegB_35c();
+ for (size_t i = 0; i < VRegA_35c(); ++i) {
+ if (i != 0) {
+ os << ", ";
+ }
+ os << "v" << arg[i];
+ }
+ os << "}, // call_site@" << call_site_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
default:
os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
<< ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -415,6 +429,8 @@ std::string Instruction::DumpString(const DexFile* file) const {
break;
}
case k3rc: {
+ uint16_t first_reg = VRegC_3rc();
+ uint16_t last_reg = VRegC_3rc() + VRegA_3rc() - 1;
switch (Opcode()) {
case INVOKE_VIRTUAL_RANGE:
case INVOKE_SUPER_RANGE:
@@ -423,7 +439,7 @@ std::string Instruction::DumpString(const DexFile* file) const {
case INVOKE_INTERFACE_RANGE:
if (file != nullptr) {
uint32_t method_idx = VRegB_3rc();
- os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
<< file->PrettyMethod(method_idx) << " // method@" << method_idx;
break;
}
@@ -431,14 +447,22 @@ std::string Instruction::DumpString(const DexFile* file) const {
case INVOKE_VIRTUAL_RANGE_QUICK:
if (file != nullptr) {
uint32_t method_idx = VRegB_3rc();
- os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
<< "// vtable@" << method_idx;
break;
}
FALLTHROUGH_INTENDED;
+ case INVOKE_CUSTOM_RANGE:
+ if (file != nullptr) {
+ uint32_t call_site_idx = VRegB_3rc();
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "// call_site@" << call_site_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
default:
- os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
- (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "thing@" << VRegB_3rc();
break;
}
break;
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 578550cae2..d269110570 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -126,14 +126,15 @@ class Instruction {
enum IndexType {
kIndexUnknown = 0,
- kIndexNone, // has no index
- kIndexTypeRef, // type reference index
- kIndexStringRef, // string reference index
- kIndexMethodRef, // method reference index
- kIndexFieldRef, // field reference index
- kIndexFieldOffset, // field offset (for static linked fields)
- kIndexVtableOffset, // vtable offset (for static linked methods)
- kIndexMethodAndProtoRef // method and a proto reference index (for invoke-polymorphic)
+ kIndexNone, // has no index
+ kIndexTypeRef, // type reference index
+ kIndexStringRef, // string reference index
+ kIndexMethodRef, // method reference index
+ kIndexFieldRef, // field reference index
+ kIndexFieldOffset, // field offset (for static linked fields)
+ kIndexVtableOffset, // vtable offset (for static linked methods)
+ kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic)
+ kIndexCallSiteRef, // call site reference index
};
enum Flags {
@@ -165,31 +166,32 @@ class Instruction {
};
enum VerifyFlag {
- kVerifyNone = 0x000000,
- kVerifyRegA = 0x000001,
- kVerifyRegAWide = 0x000002,
- kVerifyRegB = 0x000004,
- kVerifyRegBField = 0x000008,
- kVerifyRegBMethod = 0x000010,
- kVerifyRegBNewInstance = 0x000020,
- kVerifyRegBString = 0x000040,
- kVerifyRegBType = 0x000080,
- kVerifyRegBWide = 0x000100,
- kVerifyRegC = 0x000200,
- kVerifyRegCField = 0x000400,
- kVerifyRegCNewArray = 0x000800,
- kVerifyRegCType = 0x001000,
- kVerifyRegCWide = 0x002000,
- kVerifyArrayData = 0x004000,
- kVerifyBranchTarget = 0x008000,
- kVerifySwitchTargets = 0x010000,
- kVerifyVarArg = 0x020000,
- kVerifyVarArgNonZero = 0x040000,
- kVerifyVarArgRange = 0x080000,
- kVerifyVarArgRangeNonZero = 0x100000,
- kVerifyRuntimeOnly = 0x200000,
- kVerifyError = 0x400000,
- kVerifyRegHPrototype = 0x800000
+ kVerifyNone = 0x0000000,
+ kVerifyRegA = 0x0000001,
+ kVerifyRegAWide = 0x0000002,
+ kVerifyRegB = 0x0000004,
+ kVerifyRegBField = 0x0000008,
+ kVerifyRegBMethod = 0x0000010,
+ kVerifyRegBNewInstance = 0x0000020,
+ kVerifyRegBString = 0x0000040,
+ kVerifyRegBType = 0x0000080,
+ kVerifyRegBWide = 0x0000100,
+ kVerifyRegC = 0x0000200,
+ kVerifyRegCField = 0x0000400,
+ kVerifyRegCNewArray = 0x0000800,
+ kVerifyRegCType = 0x0001000,
+ kVerifyRegCWide = 0x0002000,
+ kVerifyArrayData = 0x0004000,
+ kVerifyBranchTarget = 0x0008000,
+ kVerifySwitchTargets = 0x0010000,
+ kVerifyVarArg = 0x0020000,
+ kVerifyVarArgNonZero = 0x0040000,
+ kVerifyVarArgRange = 0x0080000,
+ kVerifyVarArgRangeNonZero = 0x0100000,
+ kVerifyRuntimeOnly = 0x0200000,
+ kVerifyError = 0x0400000,
+ kVerifyRegHPrototype = 0x0800000,
+ kVerifyRegBCallSite = 0x1000000
};
static constexpr uint32_t kMaxVarArgRegs = 5;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index ca2ce1d990..a5ce3c2f8a 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -271,8 +271,8 @@
V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
- V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \
- V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \
+ V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
+ V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \
V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \
V(0xFF, UNUSED_FF, "unused-ff", k10x, kIndexUnknown, 0, kVerifyError)
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index fb8139b7c6..6301362e09 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -39,7 +39,7 @@
namespace art {
void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
- if (o.Get() == nullptr) {
+ if (o == nullptr) {
return;
}
// Make sure that the result is an instance of the type this method was expected to return.
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 5b1b2871c2..699cf91c70 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -53,13 +53,18 @@ static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Lock
}
}
+constexpr Runtime::CalleeSaveType kInitEntrypointSaveType =
+ // TODO: Change allocation entrypoints on MIPS and MIPS64 to kSaveEverything.
+ (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly
+ : Runtime::kSaveEverything;
+
extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Called to ensure static storage base is initialized for direct static field reads and writes.
// A class may be accessing another class' fields when it doesn't have access, as access has been
// given by inheritance.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false);
@@ -73,7 +78,7 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s
REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when method->dex_cache_resolved_types_[] misses.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false);
@@ -88,7 +93,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type
// Called when caller isn't guaranteed to have access to a type and the dex cache may be
// unpopulated.
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::Class* result =
ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true);
@@ -101,11 +106,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type
extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(
- self,
- // TODO: Change art_quick_resolve_string on MIPS and MIPS64 to kSaveEverything.
- (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly
- : Runtime::kSaveEverything);
+ auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType);
ArtMethod* caller = caller_and_outer.caller;
mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx));
if (LIKELY(result != nullptr)) {
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 4544aef148..822c5a8d9d 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -48,7 +48,7 @@ ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx,
StackHandleScope<1> hs(self);
HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj));
ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size);
- if (LIKELY(field != nullptr) && UNLIKELY(h.Get() == nullptr)) {
+ if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) {
ThrowNullPointerExceptionForFieldAccess(field, /*is_read*/FindFieldTypeIsRead(type));
return nullptr;
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 3ef47c427e..c2bca5305d 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2435,8 +2435,8 @@ extern "C" uintptr_t artInvokePolymorphic(
// Wrap raw_method_handle in a Handle for safety.
StackHandleScope<5> hs(self);
- Handle<mirror::MethodHandleImpl> method_handle(
- hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle))));
+ Handle<mirror::MethodHandle> method_handle(
+ hs.NewHandle(ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(raw_method_handle))));
raw_method_handle = nullptr;
self->EndAssertNoThreadSuspension(old_cause);
@@ -2497,15 +2497,14 @@ extern "C" uintptr_t artInvokePolymorphic(
// consecutive order.
uint32_t unused_args[Instruction::kMaxVarArgRegs] = {};
uint32_t first_callee_arg = first_arg + 1;
- const bool do_assignability_check = false;
- if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self,
- resolved_method,
- *shadow_frame,
- method_handle,
- method_type,
- unused_args,
- first_callee_arg,
- result)) {
+ if (!DoInvokePolymorphic<true /* is_range */>(self,
+ resolved_method,
+ *shadow_frame,
+ method_handle,
+ method_type,
+ unused_args,
+ first_callee_arg,
+ result)) {
DCHECK(self->IsExceptionPending());
}
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index e18a955251..122f7799df 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -292,7 +292,7 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self,
(kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(Locks::alloc_tracker_lock_);
new_record_condition_.WaitHoldingLocks(self);
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index f12ad8058d..f18ffb4aef 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -835,65 +835,9 @@ void ConcurrentCopying::ProcessFalseGrayStack() {
void ConcurrentCopying::IssueEmptyCheckpoint() {
Thread* self = Thread::Current();
ThreadList* thread_list = Runtime::Current()->GetThreadList();
- Barrier* barrier = thread_list->EmptyCheckpointBarrier();
- barrier->Init(self, 0);
- std::vector<uint32_t> runnable_thread_ids; // Used in debug build only
- size_t barrier_count = thread_list->RunEmptyCheckpoint(runnable_thread_ids);
- // If there are no threads to wait which implys that all the checkpoint functions are finished,
- // then no need to release the mutator lock.
- if (barrier_count == 0) {
- return;
- }
// Release locks then wait for all mutator threads to pass the barrier.
Locks::mutator_lock_->SharedUnlock(self);
- {
- ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
- if (kIsDebugBuild) {
- static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000; // 10 minutes.
- bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs);
- if (timed_out) {
- std::ostringstream ss;
- ss << "Empty checkpoint timeout\n";
- ss << "Barrier count " << barrier->GetCount(self) << "\n";
- ss << "Runnable thread IDs";
- for (uint32_t tid : runnable_thread_ids) {
- ss << " " << tid;
- }
- ss << "\n";
- Locks::mutator_lock_->Dump(ss);
- ss << "\n";
- LOG(FATAL_WITHOUT_ABORT) << ss.str();
- // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
- // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
- {
- ScopedObjectAccess soa(self);
- MutexLock mu1(self, *Locks::thread_list_lock_);
- for (Thread* thread : thread_list->GetList()) {
- uint32_t tid = thread->GetThreadId();
- bool is_in_runnable_thread_ids =
- std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) !=
- runnable_thread_ids.end();
- if (is_in_runnable_thread_ids &&
- thread->ReadFlag(kEmptyCheckpointRequest)) {
- // Found a runnable thread that hasn't responded to the empty checkpoint request.
- // Assume it's stuck and safe to dump its stack.
- thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT),
- /*dump_native_stack*/ true,
- /*backtrace_map*/ nullptr,
- /*force_dump_stack*/ true);
- }
- }
- }
- LOG(FATAL_WITHOUT_ABORT)
- << "Dumped runnable threads that haven't responded to empty checkpoint.";
- // Now use ThreadList::Dump() to dump more threads, noting it may get stuck.
- thread_list->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
- LOG(FATAL) << "Dumped all threads.";
- }
- } else {
- barrier->Increment(self, barrier_count);
- }
- }
+ thread_list->RunEmptyCheckpoint();
Locks::mutator_lock_->SharedLock(self);
}
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index c1548365c7..86b152211c 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -104,7 +104,7 @@ ObjPtr<mirror::Object> ReferenceProcessor::GetReferent(Thread* self,
}
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(Locks::reference_processor_lock_);
condition_.WaitHoldingLocks(self);
}
return reference->GetReferent();
@@ -292,7 +292,7 @@ void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) {
(kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(Locks::reference_processor_lock_);
condition_.WaitHoldingLocks(self);
}
}
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
index 3ca3353562..613b034f59 100644
--- a/runtime/gc/reference_queue_test.cc
+++ b/runtime/gc/reference_queue_test.cc
@@ -38,11 +38,11 @@ TEST_F(ReferenceQueueTest, EnqueueDequeue) {
auto ref_class = hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
ScopedNullHandle<mirror::ClassLoader>()));
- ASSERT_TRUE(ref_class.Get() != nullptr);
+ ASSERT_TRUE(ref_class != nullptr);
auto ref1(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
- ASSERT_TRUE(ref1.Get() != nullptr);
+ ASSERT_TRUE(ref1 != nullptr);
auto ref2(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
- ASSERT_TRUE(ref2.Get() != nullptr);
+ ASSERT_TRUE(ref2 != nullptr);
queue.EnqueueReference(ref1.Get());
ASSERT_TRUE(!queue.IsEmpty());
ASSERT_EQ(queue.GetLength(), 1U);
@@ -73,15 +73,15 @@ TEST_F(ReferenceQueueTest, Dump) {
auto weak_ref_class = hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
ScopedNullHandle<mirror::ClassLoader>()));
- ASSERT_TRUE(weak_ref_class.Get() != nullptr);
+ ASSERT_TRUE(weak_ref_class != nullptr);
auto finalizer_ref_class = hs.NewHandle(
Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/FinalizerReference;",
ScopedNullHandle<mirror::ClassLoader>()));
- ASSERT_TRUE(finalizer_ref_class.Get() != nullptr);
+ ASSERT_TRUE(finalizer_ref_class != nullptr);
auto ref1(hs.NewHandle(weak_ref_class->AllocObject(self)->AsReference()));
- ASSERT_TRUE(ref1.Get() != nullptr);
+ ASSERT_TRUE(ref1 != nullptr);
auto ref2(hs.NewHandle(finalizer_ref_class->AllocObject(self)->AsReference()));
- ASSERT_TRUE(ref2.Get() != nullptr);
+ ASSERT_TRUE(ref2 != nullptr);
queue.EnqueueReference(ref1.Get());
oss.str("");
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index ffbca525d9..2163a20e87 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -587,15 +587,18 @@ class ImageSpaceLoader {
}
std::unique_ptr<MemMap> map;
+
// GetImageBegin is the preferred address to map the image. If we manage to map the
// image at the image begin, the amount of fixup work required is minimized.
+ // If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to
+ // avoid reading proc maps for a mapping failure and slowing everything down.
map.reset(LoadImageFile(image_filename,
image_location,
*image_header,
image_header->GetImageBegin(),
file->Fd(),
logger,
- error_msg));
+ image_header->IsPic() ? nullptr : error_msg));
// If the header specifies PIC mode, we can also map at a random low_4gb address since we can
// relocate in-place.
if (map == nullptr && image_header->IsPic()) {
@@ -689,7 +692,7 @@ class ImageSpaceLoader {
if (validate_oat_file) {
TimingLogger::ScopedTiming timing("ValidateOatFile", &logger);
CHECK(space->oat_file_ != nullptr);
- if (!ValidateOatFile(*space, *space->oat_file_, error_msg)) {
+ if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) {
DCHECK(!error_msg->empty());
return nullptr;
}
@@ -765,8 +768,10 @@ class ImageSpaceLoader {
if (storage_mode != ImageHeader::kStorageModeLZ4 &&
storage_mode != ImageHeader::kStorageModeLZ4HC) {
- *error_msg = StringPrintf("Invalid storage mode in image header %d",
- static_cast<int>(storage_mode));
+ if (error_msg != nullptr) {
+ *error_msg = StringPrintf("Invalid storage mode in image header %d",
+ static_cast<int>(storage_mode));
+ }
return nullptr;
}
@@ -790,7 +795,7 @@ class ImageSpaceLoader {
image_filename,
error_msg));
if (temp_map == nullptr) {
- DCHECK(!error_msg->empty());
+ DCHECK(error_msg == nullptr || !error_msg->empty());
return nullptr;
}
memcpy(map->Begin(), &image_header, sizeof(ImageHeader));
@@ -802,12 +807,18 @@ class ImageSpaceLoader {
reinterpret_cast<char*>(map->Begin()) + decompress_offset,
stored_size,
map->Size() - decompress_offset);
- VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
+ const uint64_t time = NanoTime() - start;
+ // Add one 1 ns to prevent possible divide by 0.
+ VLOG(image) << "Decompressing image took " << PrettyDuration(time) << " ("
+ << PrettySize(static_cast<uint64_t>(map->Size()) * MsToNs(1000) / (time + 1))
+ << "/s)";
if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
- *error_msg = StringPrintf(
- "Decompressed size does not match expected image size %zu vs %zu",
- decompressed_size + sizeof(ImageHeader),
- image_header.GetImageSize());
+ if (error_msg != nullptr) {
+ *error_msg = StringPrintf(
+ "Decompressed size does not match expected image size %zu vs %zu",
+ decompressed_size + sizeof(ImageHeader),
+ image_header.GetImageSize());
+ }
return nullptr;
}
}
@@ -1272,6 +1283,14 @@ class ImageSpaceLoader {
}
dex_cache->FixupResolvedMethodTypes<kWithoutReadBarrier>(new_method_types, fixup_adapter);
}
+ GcRoot<mirror::CallSite>* call_sites = dex_cache->GetResolvedCallSites();
+ if (call_sites != nullptr) {
+ GcRoot<mirror::CallSite>* new_call_sites = fixup_adapter.ForwardObject(call_sites);
+ if (call_sites != new_call_sites) {
+ dex_cache->SetResolvedCallSites(new_call_sites);
+ }
+ dex_cache->FixupResolvedCallSites<kWithoutReadBarrier>(new_call_sites, fixup_adapter);
+ }
}
}
{
@@ -1368,33 +1387,6 @@ class ImageSpaceLoader {
return oat_file;
}
-
- static bool ValidateOatFile(const ImageSpace& space,
- const OatFile& oat_file,
- std::string* error_msg) {
- for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
- const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
- uint32_t dex_file_location_checksum;
- if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
- *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
- "%s",
- dex_file_location.c_str(),
- space.GetName(),
- error_msg->c_str());
- return false;
- }
- if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
- "dex file '%s' (0x%x != 0x%x)",
- oat_file.GetLocation().c_str(),
- dex_file_location.c_str(),
- oat_dex_file->GetDexFileLocationChecksum(),
- dex_file_location_checksum);
- return false;
- }
- }
- return true;
- }
};
static constexpr uint64_t kLowSpaceValue = 50 * MB;
@@ -1771,6 +1763,63 @@ std::string ImageSpace::GetMultiImageBootClassPath(
return bootcp_oss.str();
}
+bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
+ for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
+ const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
+
+ // Skip multidex locations - These will be checked when we visit their
+ // corresponding primary non-multidex location.
+ if (DexFile::IsMultiDexLocation(dex_file_location.c_str())) {
+ continue;
+ }
+
+ std::vector<uint32_t> checksums;
+ if (!DexFile::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) {
+ *error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' "
+ "referenced by oat file %s: %s",
+ dex_file_location.c_str(),
+ oat_file.GetLocation().c_str(),
+ error_msg->c_str());
+ return false;
+ }
+ CHECK(!checksums.empty());
+ if (checksums[0] != oat_dex_file->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file "
+ "'%s' and dex file '%s' (0x%x != 0x%x)",
+ oat_file.GetLocation().c_str(),
+ dex_file_location.c_str(),
+ oat_dex_file->GetDexFileLocationChecksum(),
+ checksums[0]);
+ return false;
+ }
+
+ // Verify checksums for any related multidex entries.
+ for (size_t i = 1; i < checksums.size(); i++) {
+ std::string multi_dex_location = DexFile::GetMultiDexLocation(i, dex_file_location.c_str());
+ const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(),
+ nullptr,
+ error_msg);
+ if (multi_dex == nullptr) {
+ *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'",
+ oat_file.GetLocation().c_str(),
+ multi_dex_location.c_str());
+ return false;
+ }
+
+ if (checksums[i] != multi_dex->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file "
+ "'%s' and dex file '%s' (0x%x != 0x%x)",
+ oat_file.GetLocation().c_str(),
+ multi_dex_location.c_str(),
+ multi_dex->GetDexFileLocationChecksum(),
+ checksums[i]);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
const std::string& boot_classpath,
std::vector<std::string>* image_file_names) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 489a2890fe..199bbdd00a 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -131,6 +131,17 @@ class ImageSpace : public MemMapSpace {
const std::vector<const char*>& oat_filenames,
const std::vector<const char*>& image_filenames);
+ // Returns true if the dex checksums in the given oat file match the
+ // checksums of the original dex files on disk. This is intended to be used
+ // to validate the boot image oat file, which may contain dex entries from
+ // multiple different (possibly multidex) dex files on disk. Prefer the
+ // OatFileAssistant for validating regular app oat files because the
+ // OatFileAssistant caches dex checksums that are reused to check both the
+ // oat and odex file.
+ //
+ // This function is exposed for testing purposes.
+ static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg);
+
// Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
uint8_t* GetImageEnd() const {
return Begin() + GetImageHeader().GetImageSize();
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
new file mode 100644
index 0000000000..7a380746a1
--- /dev/null
+++ b/runtime/gc/space/image_space_test.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 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 <gtest/gtest.h>
+
+#include "dexopt_test.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+TEST_F(DexoptTest, ValidateOatFile) {
+ std::string dex1 = GetScratchDir() + "/Dex1.jar";
+ std::string multidex1 = GetScratchDir() + "/MultiDex1.jar";
+ std::string dex2 = GetScratchDir() + "/Dex2.jar";
+ std::string oat_location = GetScratchDir() + "/Oat.oat";
+
+ Copy(GetDexSrc1(), dex1);
+ Copy(GetMultiDexSrc1(), multidex1);
+ Copy(GetDexSrc2(), dex2);
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex1);
+ args.push_back("--dex-file=" + multidex1);
+ args.push_back("--dex-file=" + dex2);
+ args.push_back("--oat-file=" + oat_location);
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(oat != nullptr) << error_msg;
+
+ // Originally all the dex checksums should be up to date.
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Invalidate the dex1 checksum.
+ Copy(GetDexSrc2(), dex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the dex1 checksum.
+ Copy(GetDexSrc1(), dex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Invalidate the non-main multidex checksum.
+ Copy(GetMultiDexSrc2(), multidex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the multidex checksum.
+ Copy(GetMultiDexSrc1(), multidex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Invalidate the dex2 checksum.
+ Copy(GetDexSrc1(), dex2);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // restore the dex2 checksum.
+ Copy(GetDexSrc2(), dex2);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Replace the multidex file with a non-multidex file.
+ Copy(GetDexSrc1(), multidex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the multidex file
+ Copy(GetMultiDexSrc1(), multidex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Replace dex1 with a multidex file.
+ Copy(GetMultiDexSrc1(), dex1);
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the dex1 file.
+ Copy(GetDexSrc1(), dex1);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Remove the dex2 file.
+ EXPECT_EQ(0, unlink(dex2.c_str()));
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+
+ // Restore the dex2 file.
+ Copy(GetDexSrc2(), dex2);
+ EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg;
+
+ // Remove the multidex file.
+ EXPECT_EQ(0, unlink(multidex1.c_str()));
+ EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
+}
+
+} // namespace space
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc
index 7bc4dc40e4..ca5f306264 100644
--- a/runtime/gc/space/space_create_test.cc
+++ b/runtime/gc/space/space_create_test.cc
@@ -108,7 +108,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
&ptr1_bytes_allocated,
&ptr1_usable_size,
&ptr1_bytes_tl_bulk_allocated)));
- EXPECT_TRUE(ptr1.Get() != nullptr);
+ EXPECT_TRUE(ptr1 != nullptr);
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
EXPECT_LE(1U * MB, ptr1_usable_size);
EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
@@ -126,7 +126,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
&ptr3_bytes_allocated,
&ptr3_usable_size,
&ptr3_bytes_tl_bulk_allocated)));
- EXPECT_TRUE(ptr3.Get() != nullptr);
+ EXPECT_TRUE(ptr3 != nullptr);
EXPECT_LE(8U * MB, ptr3_bytes_allocated);
EXPECT_LE(8U * MB, ptr3_usable_size);
EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
@@ -154,7 +154,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
&ptr6_bytes_allocated,
&ptr6_usable_size,
&ptr6_bytes_tl_bulk_allocated)));
- EXPECT_TRUE(ptr6.Get() != nullptr);
+ EXPECT_TRUE(ptr6 != nullptr);
EXPECT_LE(9U * MB, ptr6_bytes_allocated);
EXPECT_LE(9U * MB, ptr6_usable_size);
EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
@@ -193,7 +193,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
&ptr1_bytes_allocated,
&ptr1_usable_size,
&ptr1_bytes_tl_bulk_allocated));
- EXPECT_TRUE(ptr1.Get() != nullptr);
+ EXPECT_TRUE(ptr1 != nullptr);
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
EXPECT_LE(1U * MB, ptr1_usable_size);
EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
@@ -210,7 +210,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) {
&ptr3_bytes_allocated,
&ptr3_usable_size,
&ptr3_bytes_tl_bulk_allocated));
- EXPECT_TRUE(ptr3.Get() != nullptr);
+ EXPECT_TRUE(ptr3 != nullptr);
EXPECT_LE(2U * MB, ptr3_bytes_allocated);
EXPECT_LE(2U * MB, ptr3_usable_size);
EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
@@ -242,7 +242,7 @@ TEST_P(SpaceCreateTest, AllocAndFreeTestBody) {
&ptr1_bytes_allocated,
&ptr1_usable_size,
&ptr1_bytes_tl_bulk_allocated)));
- EXPECT_TRUE(ptr1.Get() != nullptr);
+ EXPECT_TRUE(ptr1 != nullptr);
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
EXPECT_LE(1U * MB, ptr1_usable_size);
EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated);
@@ -260,7 +260,7 @@ TEST_P(SpaceCreateTest, AllocAndFreeTestBody) {
&ptr3_bytes_allocated,
&ptr3_usable_size,
&ptr3_bytes_tl_bulk_allocated)));
- EXPECT_TRUE(ptr3.Get() != nullptr);
+ EXPECT_TRUE(ptr3 != nullptr);
EXPECT_LE(8U * MB, ptr3_bytes_allocated);
EXPECT_LE(8U * MB, ptr3_usable_size);
EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated);
@@ -288,7 +288,7 @@ TEST_P(SpaceCreateTest, AllocAndFreeTestBody) {
&ptr6_bytes_allocated,
&ptr6_usable_size,
&ptr6_bytes_tl_bulk_allocated)));
- EXPECT_TRUE(ptr6.Get() != nullptr);
+ EXPECT_TRUE(ptr6 != nullptr);
EXPECT_LE(9U * MB, ptr6_bytes_allocated);
EXPECT_LE(9U * MB, ptr6_usable_size);
EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated);
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index cbb3d73497..1fe3fb2e86 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -200,7 +200,7 @@ void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space,
}
footprint = space->GetFootprint();
EXPECT_GE(space->Size(), footprint); // invariant
- if (object.Get() != nullptr) { // allocation succeeded
+ if (object != nullptr) { // allocation succeeded
lots_of_objects[i] = object.Get();
size_t allocation_size = space->AllocationSize(object.Get(), nullptr);
EXPECT_EQ(bytes_allocated, allocation_size);
@@ -296,7 +296,7 @@ void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space,
large_object.Assign(AllocWithGrowth(space, self, three_quarters_space, &bytes_allocated,
nullptr, &bytes_tl_bulk_allocated));
}
- EXPECT_TRUE(large_object.Get() != nullptr);
+ EXPECT_TRUE(large_object != nullptr);
// Sanity check footprint
footprint = space->GetFootprint();
diff --git a/runtime/gc/system_weak.h b/runtime/gc/system_weak.h
index e5cddfc6f9..60105f4e4f 100644
--- a/runtime/gc/system_weak.h
+++ b/runtime/gc/system_weak.h
@@ -82,7 +82,7 @@ class SystemWeakHolder : public AbstractSystemWeakHolder {
(kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(&allow_disallow_lock_);
new_weak_condition_.WaitHoldingLocks(self);
}
}
diff --git a/runtime/handle.h b/runtime/handle.h
index e4b6d29a55..ccff575495 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -81,6 +81,14 @@ class Handle : public ValueObject {
return reference_;
}
+ ALWAYS_INLINE bool operator!=(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return !IsNull();
+ }
+
+ ALWAYS_INLINE bool operator==(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return IsNull();
+ }
+
protected:
template<typename S>
explicit Handle(StackReference<S>* reference)
diff --git a/runtime/imtable_test.cc b/runtime/imtable_test.cc
index 8cbe2916ec..17149dfe44 100644
--- a/runtime/imtable_test.cc
+++ b/runtime/imtable_test.cc
@@ -53,7 +53,7 @@ class ImTableTest : public CommonRuntimeTest {
ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(jclass_loader_a)));
Handle<mirror::Class> h_class_a(
hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), h_class_loader)));
- if (h_class_a.Get() == nullptr) {
+ if (h_class_a == nullptr) {
LOG(ERROR) << self->GetException()->Dump();
CHECK(false) << "h_class_a == nullptr";
}
@@ -63,7 +63,7 @@ class ImTableTest : public CommonRuntimeTest {
ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(jclass_loader_b)));
Handle<mirror::Class> h_class_b(
hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), h_class_loader)));
- if (h_class_b.Get() == nullptr) {
+ if (h_class_b == nullptr) {
LOG(ERROR) << self->GetException()->Dump();
CHECK(false) << "h_class_b == nullptr";
}
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index bf4cab24cc..6aefe239a9 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -64,13 +64,13 @@ TEST_F(IndirectReferenceTableTest, BasicTest) {
StackHandleScope<4> hs(soa.Self());
ASSERT_TRUE(c != nullptr);
Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0.Get() != nullptr);
+ ASSERT_TRUE(obj0 != nullptr);
Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj1.Get() != nullptr);
+ ASSERT_TRUE(obj1 != nullptr);
Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj2.Get() != nullptr);
+ ASSERT_TRUE(obj2 != nullptr);
Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj3.Get() != nullptr);
+ ASSERT_TRUE(obj3 != nullptr);
const IRTSegmentState cookie = kIRTFirstSegment;
@@ -282,15 +282,15 @@ TEST_F(IndirectReferenceTableTest, Holes) {
StackHandleScope<5> hs(soa.Self());
ASSERT_TRUE(c != nullptr);
Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0.Get() != nullptr);
+ ASSERT_TRUE(obj0 != nullptr);
Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj1.Get() != nullptr);
+ ASSERT_TRUE(obj1 != nullptr);
Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj2.Get() != nullptr);
+ ASSERT_TRUE(obj2 != nullptr);
Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj3.Get() != nullptr);
+ ASSERT_TRUE(obj3 != nullptr);
Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj4.Get() != nullptr);
+ ASSERT_TRUE(obj4 != nullptr);
std::string error_msg;
@@ -491,7 +491,7 @@ TEST_F(IndirectReferenceTableTest, Resize) {
StackHandleScope<1> hs(soa.Self());
ASSERT_TRUE(c != nullptr);
Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0.Get() != nullptr);
+ ASSERT_TRUE(obj0 != nullptr);
std::string error_msg;
IndirectReferenceTable irt(kTableMax,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index f11e2cba10..d862ff2708 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -1010,15 +1010,18 @@ void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_o
void Instrumentation::ExceptionCaughtEvent(Thread* thread,
mirror::Throwable* exception_object) const {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Throwable> h_exception(hs.NewHandle(exception_object));
if (HasExceptionCaughtListeners()) {
- DCHECK_EQ(thread->GetException(), exception_object);
+ DCHECK_EQ(thread->GetException(), h_exception.Get());
thread->ClearException();
for (InstrumentationListener* listener : exception_caught_listeners_) {
if (listener != nullptr) {
- listener->ExceptionCaught(thread, exception_object);
+ listener->ExceptionCaught(thread, h_exception.Get());
}
}
- thread->SetException(exception_object);
+ thread->SetException(h_exception.Get());
}
}
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 3991d6550d..f0d0260482 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -36,10 +36,10 @@ TEST_F(InternTableTest, Intern) {
Handle<mirror::String> foo_3(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar")));
- ASSERT_TRUE(foo_1.Get() != nullptr);
- ASSERT_TRUE(foo_2.Get() != nullptr);
- ASSERT_TRUE(foo_3.Get() != nullptr);
- ASSERT_TRUE(bar.Get() != nullptr);
+ ASSERT_TRUE(foo_1 != nullptr);
+ ASSERT_TRUE(foo_2 != nullptr);
+ ASSERT_TRUE(foo_3 != nullptr);
+ ASSERT_TRUE(bar != nullptr);
EXPECT_EQ(foo_1.Get(), foo_2.Get());
EXPECT_TRUE(foo_1->Equals("foo"));
EXPECT_TRUE(foo_2->Equals("foo"));
@@ -204,9 +204,9 @@ TEST_F(InternTableTest, LookupStrong) {
Handle<mirror::String> foo(hs.NewHandle(intern_table.InternStrong(3, "foo")));
Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar")));
Handle<mirror::String> foobar(hs.NewHandle(intern_table.InternStrong(6, "foobar")));
- ASSERT_TRUE(foo.Get() != nullptr);
- ASSERT_TRUE(bar.Get() != nullptr);
- ASSERT_TRUE(foobar.Get() != nullptr);
+ ASSERT_TRUE(foo != nullptr);
+ ASSERT_TRUE(bar != nullptr);
+ ASSERT_TRUE(foobar != nullptr);
ASSERT_TRUE(foo->Equals("foo"));
ASSERT_TRUE(bar->Equals("bar"));
ASSERT_TRUE(foobar->Equals("foobar"));
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 28bcb97105..8978bfd5af 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -519,7 +519,7 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame,
}
}
-template<bool is_range, bool do_access_check>
+template<bool is_range>
bool DoInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
@@ -539,10 +539,10 @@ bool DoInvokePolymorphic(Thread* self,
// was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact)
// and not the method that we'll dispatch to in the end.
StackHandleScope<5> hs(self);
- Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle(
- ObjPtr<mirror::MethodHandleImpl>::DownCast(
+ Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+ ObjPtr<mirror::MethodHandle>::DownCast(
MakeObjPtr(shadow_frame.GetVRegReference(vRegC)))));
- if (UNLIKELY(method_handle.Get() == nullptr)) {
+ if (UNLIKELY(method_handle == nullptr)) {
// Note that the invoke type is kVirtual here because a call to a signature
// polymorphic method is shaped like a virtual call at the bytecode level.
ThrowNullPointerExceptionForMethodAccess(invoke_method_idx, InvokeType::kVirtual);
@@ -564,7 +564,7 @@ bool DoInvokePolymorphic(Thread* self,
hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader()))));
// This implies we couldn't resolve one or more types in this method handle.
- if (UNLIKELY(callsite_type.Get() == nullptr)) {
+ if (UNLIKELY(callsite_type == nullptr)) {
CHECK(self->IsExceptionPending());
return false;
}
@@ -584,31 +584,300 @@ bool DoInvokePolymorphic(Thread* self,
// VRegC is the register holding the method handle. Arguments passed
// to the method handle's target do not include the method handle.
uint32_t first_arg = inst->VRegC_4rcc() + 1;
- return DoInvokePolymorphic<is_range, do_access_check>(self,
- invoke_method,
- shadow_frame,
- method_handle,
- callsite_type,
- args /* unused */,
- first_arg,
- result);
+ return DoInvokePolymorphic<is_range>(self,
+ invoke_method,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args /* unused */,
+ first_arg,
+ result);
} else {
// Get the register arguments for the invoke.
inst->GetVarArgs(args, inst_data);
// Drop the first register which is the method handle performing the invoke.
memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
args[Instruction::kMaxVarArgRegs - 1] = 0;
- return DoInvokePolymorphic<is_range, do_access_check>(self,
- invoke_method,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- args[0],
- result);
+ return DoInvokePolymorphic<is_range>(self,
+ invoke_method,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ args[0],
+ result);
}
}
+static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t call_site_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* referrer = shadow_frame.GetMethod();
+ const DexFile* dex_file = referrer->GetDexFile();
+ const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
+
+ StackHandleScope<9> hs(self);
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+
+ CallSiteArrayValueIterator it(*dex_file, csi);
+ uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::MethodHandle>
+ bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(method_handle_idx, referrer)));
+ if (bootstrap.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ Handle<mirror::MethodType> bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType());
+ it.Next();
+
+ DCHECK_EQ(static_cast<size_t>(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size());
+ const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs();
+
+ // Set-up a shadow frame for invoking the bootstrap method handle.
+ ShadowFrameAllocaUniquePtr bootstrap_frame =
+ CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC());
+ ScopedStackedShadowFramePusher pusher(
+ self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
+ size_t vreg = 0;
+
+ // The first parameter is a MethodHandles lookup instance.
+ {
+ Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass()));
+ ObjPtr<mirror::MethodHandlesLookup> lookup =
+ mirror::MethodHandlesLookup::Create(self, lookup_class);
+ if (lookup.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr());
+ }
+
+ // The second parameter is the name to lookup.
+ {
+ dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i));
+ ObjPtr<mirror::String> name = class_linker->ResolveString(*dex_file, name_idx, dex_cache);
+ if (name.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg++, name.Ptr());
+ }
+ it.Next();
+
+ // The third parameter is the method type associated with the name.
+ uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ Handle<mirror::MethodType>
+ method_type(hs.NewHandle(class_linker->ResolveMethodType(*dex_file,
+ method_type_idx,
+ dex_cache,
+ class_loader)));
+ if (method_type.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg++, method_type.Get());
+ it.Next();
+
+ // Append remaining arguments (if any).
+ while (it.HasNext()) {
+ const jvalue& jvalue = it.GetJavaValue();
+ switch (it.GetValueType()) {
+ case EncodedArrayValueIterator::ValueType::kBoolean:
+ case EncodedArrayValueIterator::ValueType::kByte:
+ case EncodedArrayValueIterator::ValueType::kChar:
+ case EncodedArrayValueIterator::ValueType::kShort:
+ case EncodedArrayValueIterator::ValueType::kInt:
+ bootstrap_frame->SetVReg(vreg, jvalue.i);
+ vreg += 1;
+ break;
+ case EncodedArrayValueIterator::ValueType::kLong:
+ bootstrap_frame->SetVRegLong(vreg, jvalue.j);
+ vreg += 2;
+ break;
+ case EncodedArrayValueIterator::ValueType::kFloat:
+ bootstrap_frame->SetVRegFloat(vreg, jvalue.f);
+ vreg += 1;
+ break;
+ case EncodedArrayValueIterator::ValueType::kDouble:
+ bootstrap_frame->SetVRegDouble(vreg, jvalue.d);
+ vreg += 2;
+ break;
+ case EncodedArrayValueIterator::ValueType::kMethodType: {
+ uint32_t idx = static_cast<uint32_t>(jvalue.i);
+ ObjPtr<mirror::MethodType> ref =
+ class_linker->ResolveMethodType(*dex_file, idx, dex_cache, class_loader);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kMethodHandle: {
+ uint32_t idx = static_cast<uint32_t>(jvalue.i);
+ ObjPtr<mirror::MethodHandle> ref =
+ class_linker->ResolveMethodHandle(idx, referrer);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kString: {
+ dex::StringIndex idx(static_cast<uint32_t>(jvalue.i));
+ ObjPtr<mirror::String> ref = class_linker->ResolveString(*dex_file, idx, dex_cache);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kType: {
+ dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i));
+ ObjPtr<mirror::Class> ref =
+ class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader);
+ if (ref.IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ bootstrap_frame->SetVRegReference(vreg, ref.Ptr());
+ vreg += 1;
+ break;
+ }
+ case EncodedArrayValueIterator::ValueType::kNull:
+ bootstrap_frame->SetVRegReference(vreg, nullptr);
+ vreg += 1;
+ break;
+ case EncodedArrayValueIterator::ValueType::kField:
+ case EncodedArrayValueIterator::ValueType::kMethod:
+ case EncodedArrayValueIterator::ValueType::kEnum:
+ case EncodedArrayValueIterator::ValueType::kArray:
+ case EncodedArrayValueIterator::ValueType::kAnnotation:
+ // Unreachable based on current EncodedArrayValueIterator::Next().
+ UNREACHABLE();
+ }
+
+ it.Next();
+ }
+
+ // Invoke the bootstrap method handle.
+ JValue result;
+
+ // This array of arguments is unused. DoInvokePolymorphic() operates on either a
+ // an argument array or a range, but always takes an array argument.
+ uint32_t args_unused[Instruction::kMaxVarArgRegs];
+ ArtMethod* invoke_exact =
+ jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ bool invoke_success = DoInvokePolymorphic<true /* is_range */>(self,
+ invoke_exact,
+ *bootstrap_frame,
+ bootstrap,
+ bootstrap_method_type,
+ args_unused,
+ 0,
+ &result);
+ if (!invoke_success) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ Handle<mirror::Object> object(hs.NewHandle(result.GetL()));
+
+ // Check the result is not null.
+ if (UNLIKELY(object.IsNull())) {
+ ThrowNullPointerException("CallSite == null");
+ return nullptr;
+ }
+
+ // Check the result type is a subclass of CallSite.
+ if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) {
+ ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass());
+ return nullptr;
+ }
+
+ Handle<mirror::CallSite> call_site =
+ hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
+
+ // Check the call site target is not null as we're going to invoke it.
+ Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+ if (UNLIKELY(target.IsNull())) {
+ ThrowNullPointerException("CallSite target == null");
+ return nullptr;
+ }
+
+ // Check the target method type matches the method type requested.
+ if (UNLIKELY(!target->GetMethodType()->IsExactMatch(method_type.Get()))) {
+ ThrowWrongMethodTypeException(target->GetMethodType(), method_type.Get());
+ return nullptr;
+ }
+
+ return call_site.Get();
+}
+
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // invoke-custom is not supported in transactions. In transactions
+ // there is a limited set of types supported. invoke-custom allows
+ // running arbitrary code and instantiating arbitrary types.
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ StackHandleScope<4> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache()));
+ const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ MutableHandle<mirror::CallSite>
+ call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx)));
+ if (call_site.IsNull()) {
+ call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx));
+ if (UNLIKELY(call_site.IsNull())) {
+ CHECK(self->IsExceptionPending());
+ ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method",
+ call_site_idx);
+ result->SetJ(0);
+ return false;
+ }
+ mirror::CallSite* winning_call_site =
+ dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get());
+ call_site.Assign(winning_call_site);
+ }
+
+ // CallSite.java checks the re-assignment of the call site target
+ // when mutating call site targets. We only check the target is
+ // non-null and has the right type during bootstrap method execution.
+ Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
+ Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
+ DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs());
+
+ uint32_t args[Instruction::kMaxVarArgRegs];
+ if (is_range) {
+ args[0] = inst->VRegC_3rc();
+ } else {
+ inst->GetVarArgs(args, inst_data);
+ }
+
+ ArtMethod* invoke_exact =
+ jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ return DoInvokePolymorphic<is_range>(self,
+ invoke_exact,
+ shadow_frame,
+ target,
+ target_method_type,
+ args,
+ args[0],
+ result);
+}
+
template <bool is_range>
inline void CopyRegisters(ShadowFrame& caller_frame,
ShadowFrame* callee_frame,
@@ -975,17 +1244,24 @@ EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false);
EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
-// Explicit DoInvokePolymorphic template function declarations.
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \
- Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
+// Explicit DoInvokeCustom template function declarations.
+#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokeCustom<_is_range>( \
+ Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true);
+#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokePolymorphic<_is_range>( \
+ Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data, JValue* result)
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
// Explicit DoFilledNewArray template function declarations.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 7ef3508164..6b22af9829 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -40,9 +40,11 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "handle_scope-inl.h"
#include "jit/jit.h"
+#include "mirror/call_site.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/method.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
@@ -154,13 +156,21 @@ static inline bool DoInvoke(Thread* self,
}
// Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range).
-template<bool is_range, bool do_access_check>
+template<bool is_range>
bool DoInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
JValue* result);
+// Performs a custom invoke (invoke-custom/invoke-custom-range).
+template<bool is_range>
+bool DoInvokeCustom(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result);
+
// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
// Returns true on success, otherwise throws an exception and returns false.
template<bool is_range>
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index a77a3fc2b3..b191dd79a1 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -1524,7 +1524,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
case Instruction::INVOKE_POLYMORPHIC: {
PREAMBLE();
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokePolymorphic<false, do_access_check>(
+ bool success = DoInvokePolymorphic<false /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
break;
@@ -1532,11 +1532,27 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
case Instruction::INVOKE_POLYMORPHIC_RANGE: {
PREAMBLE();
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokePolymorphic<true, do_access_check>(
+ bool success = DoInvokePolymorphic<true /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
break;
}
+ case Instruction::INVOKE_CUSTOM: {
+ PREAMBLE();
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokeCustom<false /* is_range */>(
+ self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_CUSTOM_RANGE: {
+ PREAMBLE();
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokeCustom<true /* is_range */>(
+ self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
case Instruction::NEG_INT:
PREAMBLE();
shadow_frame.SetVReg(
@@ -2315,7 +2331,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
- case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
UnexpectedOpcode(inst, shadow_frame);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 545cc1ad42..c7e84420d3 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -124,7 +124,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> class
const std::string& method_name, bool initialize_class,
bool abort_if_not_found)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK(className.Get() != nullptr);
+ CHECK(className != nullptr);
std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str()));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -239,7 +239,7 @@ void UnstartedRuntime::UnstartedClassNewInstance(
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
// Check that it's not null.
- if (h_klass.Get() == nullptr) {
+ if (h_klass == nullptr) {
AbortTransactionOrFail(self, "Class reference is null for newInstance");
return;
}
@@ -263,7 +263,7 @@ void UnstartedRuntime::UnstartedClassNewInstance(
auto* cons = h_klass->FindDeclaredDirectMethod("<init>", "()V", cl->GetImagePointerSize());
if (cons != nullptr) {
Handle<mirror::Object> h_obj(hs.NewHandle(klass->AllocObject(self)));
- CHECK(h_obj.Get() != nullptr); // We don't expect OOM at compile-time.
+ CHECK(h_obj != nullptr); // We don't expect OOM at compile-time.
EnterInterpreterFromInvoke(self, cons, h_obj.Get(), nullptr, nullptr);
if (!self->IsExceptionPending()) {
result->SetL(h_obj.Get());
@@ -542,7 +542,7 @@ static void GetResourceAsStream(Thread* self,
// Create byte array for content.
Handle<mirror::ByteArray> h_array(hs.NewHandle(mirror::ByteArray::Alloc(self, map_size)));
- if (h_array.Get() == nullptr) {
+ if (h_array == nullptr) {
AbortTransactionOrFail(self, "Could not find/create byte array class");
return;
}
@@ -556,7 +556,7 @@ static void GetResourceAsStream(Thread* self,
runtime->GetClassLinker()->FindClass(self,
"Ljava/io/ByteArrayInputStream;",
ScopedNullHandle<mirror::ClassLoader>())));
- if (h_class.Get() == nullptr) {
+ if (h_class == nullptr) {
AbortTransactionOrFail(self, "Could not find ByteArrayInputStream class");
return;
}
@@ -566,7 +566,7 @@ static void GetResourceAsStream(Thread* self,
}
Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
- if (h_obj.Get() == nullptr) {
+ if (h_obj == nullptr) {
AbortTransactionOrFail(self, "Could not allocate ByteArrayInputStream object");
return;
}
@@ -800,7 +800,7 @@ static void GetSystemProperty(Thread* self,
StackHandleScope<4> hs(self);
Handle<mirror::String> h_key(
hs.NewHandle(reinterpret_cast<mirror::String*>(shadow_frame->GetVRegReference(arg_offset))));
- if (h_key.Get() == nullptr) {
+ if (h_key == nullptr) {
AbortTransactionOrFail(self, "getProperty key was null");
return;
}
@@ -815,7 +815,7 @@ static void GetSystemProperty(Thread* self,
class_linker->FindClass(self,
"Ljava/lang/AndroidHardcodedSystemProperties;",
ScopedNullHandle<mirror::ClassLoader>())));
- if (h_props_class.Get() == nullptr) {
+ if (h_props_class == nullptr) {
AbortTransactionOrFail(self, "Could not find AndroidHardcodedSystemProperties");
return;
}
@@ -837,7 +837,7 @@ static void GetSystemProperty(Thread* self,
ObjPtr<mirror::Object> props = static_properties->GetObject(h_props_class.Get());
Handle<mirror::ObjectArray<mirror::ObjectArray<mirror::String>>> h_2string_array(hs.NewHandle(
props->AsObjectArray<mirror::ObjectArray<mirror::String>>()));
- if (h_2string_array.Get() == nullptr) {
+ if (h_2string_array == nullptr) {
AbortTransactionOrFail(self, "Field %s is null", kAndroidHardcodedSystemPropertiesFieldName);
return;
}
@@ -849,7 +849,7 @@ static void GetSystemProperty(Thread* self,
hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr));
for (int32_t i = 0; i < prop_count; ++i) {
h_string_array.Assign(h_2string_array->Get(i));
- if (h_string_array.Get() == nullptr ||
+ if (h_string_array == nullptr ||
h_string_array->GetLength() != 2 ||
h_string_array->Get(0) == nullptr) {
AbortTransactionOrFail(self,
@@ -924,7 +924,7 @@ static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, const char* class_d
StackHandleScope<2> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(klass));
Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
- if (h_obj.Get() != nullptr) {
+ if (h_obj != nullptr) {
ArtMethod* init_method = h_class->FindDirectMethod(
"<init>", "()V", class_linker->GetImagePointerSize());
if (init_method == nullptr) {
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 31be587e9c..16b7b55e00 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -960,7 +960,7 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) {
class_linker->FindClass(self,
"Lsun/misc/FloatingDecimal;",
ScopedNullHandle<mirror::ClassLoader>()));
- ASSERT_TRUE(floating_decimal.Get() != nullptr);
+ ASSERT_TRUE(floating_decimal != nullptr);
ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true));
ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod(
@@ -1014,7 +1014,7 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) {
class_linker->FindClass(self,
"Ljava/lang/Double;",
ScopedNullHandle<mirror::ClassLoader>()));
- ASSERT_TRUE(double_class.Get() != nullptr);
+ ASSERT_TRUE(double_class != nullptr);
ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true));
ArtMethod* method = double_class->FindDeclaredDirectMethod("toString",
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index e0f28adc4f..a341cdb89f 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -572,7 +572,7 @@ jweak JavaVMExt::AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) {
while (!kUseReadBarrier && UNLIKELY(!MayAccessWeakGlobals(self))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_);
weak_globals_add_condition_.WaitHoldingLocks(self);
}
IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj);
@@ -706,7 +706,7 @@ ObjPtr<mirror::Object> JavaVMExt::DecodeWeakGlobalLocked(Thread* self, IndirectR
while (UNLIKELY(!MayAccessWeakGlobals(self))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_);
weak_globals_add_condition_.WaitHoldingLocks(self);
}
return weak_globals_.Get(ref);
@@ -731,7 +731,7 @@ bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) {
while (UNLIKELY(!MayAccessWeakGlobals(self))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_);
weak_globals_add_condition_.WaitHoldingLocks(self);
}
// When just checking a weak ref has been cleared, avoid triggering the read barrier in decode
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index e5d34e1a2d..86af6d44db 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -22,6 +22,7 @@
#include "jdwp/jdwp_bits.h"
#include "jdwp/jdwp_constants.h"
#include "jdwp/jdwp_expand_buf.h"
+#include "obj_ptr.h"
#include <pthread.h>
#include <stddef.h>
@@ -286,6 +287,10 @@ struct JdwpState {
REQUIRES(!event_list_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void UnregisterLocationEventsOnClass(ObjPtr<mirror::Class> klass)
+ REQUIRES(!event_list_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
/*
* Unregister all events.
*/
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 172f52a974..96249f9b58 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -251,6 +251,43 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) {
return ERR_NONE;
}
+void JdwpState::UnregisterLocationEventsOnClass(ObjPtr<mirror::Class> klass) {
+ VLOG(jdwp) << "Removing events within " << klass->PrettyClass();
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::Class> h_klass(hs.NewHandle(klass));
+ std::vector<JdwpEvent*> to_remove;
+ MutexLock mu(Thread::Current(), event_list_lock_);
+ for (JdwpEvent* cur_event = event_list_; cur_event != nullptr; cur_event = cur_event->next) {
+ // Fill in the to_remove list
+ bool found_event = false;
+ for (int i = 0; i < cur_event->modCount && !found_event; i++) {
+ JdwpEventMod& mod = cur_event->mods[i];
+ switch (mod.modKind) {
+ case MK_LOCATION_ONLY: {
+ JdwpLocation& loc = mod.locationOnly.loc;
+ JdwpError error;
+ ObjPtr<mirror::Class> breakpoint_class(
+ Dbg::GetObjectRegistry()->Get<art::mirror::Class*>(loc.class_id, &error));
+ DCHECK_EQ(error, ERR_NONE);
+ if (breakpoint_class == h_klass.Get()) {
+ to_remove.push_back(cur_event);
+ found_event = true;
+ }
+ break;
+ }
+ default:
+ // TODO Investigate how we should handle non-locationOnly events.
+ break;
+ }
+ }
+ }
+
+ for (JdwpEvent* event : to_remove) {
+ UnregisterEvent(event);
+ EventFree(event);
+ }
+}
+
/*
* Remove an event from the list. This will also remove the event from
* any optimization tables, e.g. breakpoints.
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 4615574947..bd7251baeb 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -57,7 +57,7 @@ JDWP::ObjectId ObjectRegistry::Add(ObjPtr<mirror::Object> o) {
// Template instantiations must be declared below.
template<class T>
JDWP::ObjectId ObjectRegistry::Add(Handle<T> obj_h) {
- if (obj_h.Get() == nullptr) {
+ if (obj_h == nullptr) {
return 0;
}
return InternalAdd(obj_h);
@@ -76,7 +76,7 @@ JDWP::ObjectId ObjectRegistry::Add(Handle<mirror::Throwable> obj_h);
template<class T>
JDWP::ObjectId ObjectRegistry::InternalAdd(Handle<T> obj_h) {
- CHECK(obj_h.Get() != nullptr);
+ CHECK(obj_h != nullptr);
Thread* const self = Thread::Current();
self->AssertNoPendingException();
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index f5151b588a..60ab275641 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -556,12 +556,13 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
// Flush data cache, as compiled code references literals in it.
FlushDataCache(reinterpret_cast<char*>(roots_data),
reinterpret_cast<char*>(roots_data + data_size));
- // Flush caches before we remove write permission because on some ARMv8 hardware,
- // flushing caches require write permissions.
+ // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may
+ // trigger a segfault if a page fault occurs when requesting a cache maintenance operation.
+ // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and
+ // 6P) stop being supported or their kernels are fixed.
//
- // For reference, here are kernel patches discussing about this issue:
- // https://android.googlesource.com/kernel/msm/%2B/0e7f7bcc3fc87489cda5aa6aff8ce40eed912279
- // https://patchwork.kernel.org/patch/9047921/
+ // For reference, this behavior is caused by this commit:
+ // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
reinterpret_cast<char*>(code_ptr + code_size));
DCHECK(!Runtime::Current()->IsAotCompiler());
@@ -685,6 +686,10 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) {
// shouldn't be used since it is no longer logically in the jit code cache.
// TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
+ // Native methods have no profiling info and need no special handling from the JIT code cache.
+ if (old_method->IsNative()) {
+ return;
+ }
MutexLock mu(Thread::Current(), lock_);
// Update ProfilingInfo to the new one and remove it from the old_method.
if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) {
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 9ba2d1a355..54fc0386e1 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -664,6 +664,38 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>*
return os.str();
}
+void ProfileCompilationInfo::GetClassNames(
+ const std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* class_names) const {
+ std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files(
+ MakeNonOwningVector(dex_files));
+ GetClassNames(non_owning_dex_files.get(), class_names);
+}
+
+void ProfileCompilationInfo::GetClassNames(const std::vector<const DexFile*>* dex_files,
+ std::set<std::string>* class_names) const {
+ if (info_.empty()) {
+ return;
+ }
+ for (const auto& it : info_) {
+ const std::string& location = it.first;
+ const DexFileData& dex_data = it.second;
+ const DexFile* dex_file = nullptr;
+ if (dex_files != nullptr) {
+ for (size_t i = 0; i < dex_files->size(); i++) {
+ if (location == (*dex_files)[i]->GetLocation()) {
+ dex_file = (*dex_files)[i];
+ }
+ }
+ }
+ for (const auto class_it : dex_data.class_set) {
+ if (dex_file != nullptr) {
+ class_names->insert(std::string(dex_file->PrettyType(class_it)));
+ }
+ }
+ }
+}
+
bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
return info_.Equals(other.info_);
}
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index b1587c0070..758b46d74a 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -78,6 +78,11 @@ class ProfileCompilationInfo {
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
+ void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* class_names) const;
+ void GetClassNames(const std::vector<const DexFile*>* dex_files,
+ std::set<std::string>* class_names) const;
+
bool Equals(const ProfileCompilationInfo& other);
static std::string GetProfileDexFileKey(const std::string& dex_location);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 3c641b0b75..547b5b8a2d 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -196,7 +196,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> c(
hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class))));
- if (c.Get() == nullptr) {
+ if (c == nullptr) {
return nullptr;
}
ArtField* field = nullptr;
diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc
index 443f095d05..4c45e3839b 100644
--- a/runtime/jobject_comparator.cc
+++ b/runtime/jobject_comparator.cc
@@ -34,9 +34,9 @@ bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object>(jobj1)));
Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object>(jobj2)));
- if (obj1.Get() == nullptr) {
+ if (obj1 == nullptr) {
return true;
- } else if (obj2.Get() == nullptr) {
+ } else if (obj2 == nullptr) {
return false;
}
// Sort by class...
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index dce56b3c58..93c212bafb 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -400,7 +400,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr,
// reuse means it is okay that it overlaps an existing page mapping.
// Only use this if you actually made the page reservation yourself.
CHECK(expected_ptr != nullptr);
-
+ DCHECK(error_msg != nullptr);
DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg))
<< ((error_msg != nullptr) ? *error_msg : std::string());
flags |= MAP_FIXED;
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 99886e5c2f..6ecfd8c714 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -220,7 +220,7 @@ bool ConvertJValueCommon(
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_to(hs.NewHandle(to));
Handle<mirror::Object> h_obj(hs.NewHandle(src_value.GetL()));
- if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
+ if (h_obj != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
ThrowClassCastException(h_to.Get(), h_obj->GetClass());
return false;
}
@@ -555,7 +555,7 @@ static inline bool DoCallTransform(ArtMethod* called_method,
Handle<mirror::MethodType> callee_type,
Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> receiver,
+ Handle<mirror::MethodHandle> receiver,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
JValue* result)
@@ -599,7 +599,7 @@ static inline bool DoCallTransform(ArtMethod* called_method,
// Something went wrong while creating the emulated stack frame, we should
// throw the pending exception.
- if (sf.Get() == nullptr) {
+ if (sf == nullptr) {
DCHECK(self->IsExceptionPending());
return false;
}
@@ -645,7 +645,7 @@ inline static ObjPtr<mirror::Class> GetAndInitializeDeclaringClass(Thread* self,
template <bool is_range>
bool DoInvokePolymorphicUnchecked(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -780,7 +780,6 @@ inline static void DoFieldGetForInvokePolymorphic(Thread* self,
}
// Helper for setters in invoke-polymorphic.
-template <bool do_assignability_check>
inline bool DoFieldPutForInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
ObjPtr<mirror::Object>& obj,
@@ -788,30 +787,33 @@ inline bool DoFieldPutForInvokePolymorphic(Thread* self,
Primitive::Type field_type,
const JValue& value)
REQUIRES_SHARED(Locks::mutator_lock_) {
- static const bool kTransaction = false;
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ static const bool kTransaction = false; // Not in a transaction.
+ static const bool kAssignabilityCheck = false; // No access check.
switch (field_type) {
case Primitive::kPrimBoolean:
- return DoFieldPutCommon<Primitive::kPrimBoolean, do_assignability_check, kTransaction>(
- self, shadow_frame, obj, field, value);
+ return
+ DoFieldPutCommon<Primitive::kPrimBoolean, kAssignabilityCheck, kTransaction>(
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimByte:
- return DoFieldPutCommon<Primitive::kPrimByte, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimByte, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimChar:
- return DoFieldPutCommon<Primitive::kPrimChar, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimChar, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimShort:
- return DoFieldPutCommon<Primitive::kPrimShort, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimShort, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimInt:
case Primitive::kPrimFloat:
- return DoFieldPutCommon<Primitive::kPrimInt, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimInt, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimLong:
case Primitive::kPrimDouble:
- return DoFieldPutCommon<Primitive::kPrimLong, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimLong, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimNot:
- return DoFieldPutCommon<Primitive::kPrimNot, do_assignability_check, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimNot, kAssignabilityCheck, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -855,10 +857,10 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame,
return field_value;
}
-template <bool is_range, bool do_conversions, bool do_assignability_check>
+template <bool is_range, bool do_conversions>
bool DoInvokePolymorphicFieldAccess(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -903,12 +905,7 @@ bool DoInvokePolymorphicFieldAccess(Thread* self,
return false;
}
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
- return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
- shadow_frame,
- obj,
- field,
- field_type,
- value);
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
}
case mirror::MethodHandle::kStaticPut: {
ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
@@ -922,12 +919,7 @@ bool DoInvokePolymorphicFieldAccess(Thread* self,
DCHECK(self->IsExceptionPending());
return false;
}
- return DoFieldPutForInvokePolymorphic<do_assignability_check>(self,
- shadow_frame,
- obj,
- field,
- field_type,
- value);
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
}
default:
LOG(FATAL) << "Unreachable: " << handle_kind;
@@ -935,10 +927,10 @@ bool DoInvokePolymorphicFieldAccess(Thread* self,
}
}
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
static inline bool DoInvokePolymorphicNonExact(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -959,7 +951,7 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self,
if (IsFieldAccess(handle_kind)) {
if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) {
const bool do_convert = false;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+ return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -969,7 +961,7 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self,
result);
} else {
const bool do_convert = true;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+ return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -999,10 +991,10 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self,
}
}
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
bool DoInvokePolymorphicExact(Thread* self,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
@@ -1018,13 +1010,13 @@ bool DoInvokePolymorphicExact(Thread* self,
ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
return false;
}
- return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return DoInvokePolymorphicNonExact<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
}
ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
@@ -1036,7 +1028,7 @@ bool DoInvokePolymorphicExact(Thread* self,
const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
if (IsFieldAccess(handle_kind)) {
const bool do_convert = false;
- return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>(
+ return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
self,
shadow_frame,
method_handle,
@@ -1057,51 +1049,49 @@ bool DoInvokePolymorphicExact(Thread* self,
} // namespace
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
bool DoInvokePolymorphic(Thread* self,
ArtMethod* invoke_method,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (IsMethodHandleInvokeExact(invoke_method)) {
- return DoInvokePolymorphicExact<is_range, do_assignability_check>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return DoInvokePolymorphicExact<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
} else {
- return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self,
- shadow_frame,
- method_handle,
- callsite_type,
- args,
- first_arg,
- result);
+ return DoInvokePolymorphicNonExact<is_range>(self,
+ shadow_frame,
+ method_handle,
+ callsite_type,
+ args,
+ first_arg,
+ result);
}
}
-#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
-template REQUIRES_SHARED(Locks::mutator_lock_) \
-bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \
- Thread* self, \
- ArtMethod* invoke_method, \
- ShadowFrame& shadow_frame, \
- Handle<mirror::MethodHandleImpl> method_handle, \
- Handle<mirror::MethodType> callsite_type, \
- const uint32_t (&args)[Instruction::kMaxVarArgRegs], \
- uint32_t first_arg, \
- JValue* result)
-
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokePolymorphic<_is_range>( \
+ Thread* self, \
+ ArtMethod* invoke_method, \
+ ShadowFrame& shadow_frame, \
+ Handle<mirror::MethodHandle> method_handle, \
+ Handle<mirror::MethodType> callsite_type, \
+ const uint32_t (&args)[Instruction::kMaxVarArgRegs], \
+ uint32_t first_arg, \
+ JValue* result)
+
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false);
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
} // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 734d7c7bf4..5bea0ab5cd 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -27,7 +27,7 @@
namespace art {
namespace mirror {
- class MethodHandleImpl;
+ class MethodHandle;
class MethodType;
} // mirror
@@ -202,11 +202,11 @@ class ShadowFrameSetter {
size_t arg_index_;
};
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
bool DoInvokePolymorphic(Thread* self,
ArtMethod* invoke_method,
ShadowFrame& shadow_frame,
- Handle<mirror::MethodHandleImpl> method_handle,
+ Handle<mirror::MethodHandle> method_handle,
Handle<mirror::MethodType> callsite_type,
const uint32_t (&args)[Instruction::kMaxVarArgRegs],
uint32_t first_arg,
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index cc548b9cc8..f283ec3e9d 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -52,7 +52,7 @@ static Array* RecursiveCreateMultiArray(Thread* self,
Array::Alloc<true>(self, array_class.Get(), array_length,
array_class->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
- if (UNLIKELY(new_array.Get() == nullptr)) {
+ if (UNLIKELY(new_array == nullptr)) {
CHECK(self->IsExceptionPending());
return nullptr;
}
@@ -98,14 +98,14 @@ Array* Array::CreateMultiArray(Thread* self, Handle<Class> element_class,
StackHandleScope<1> hs(self);
MutableHandle<mirror::Class> array_class(
hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr)));
- if (UNLIKELY(array_class.Get() == nullptr)) {
+ if (UNLIKELY(array_class == nullptr)) {
CHECK(self->IsExceptionPending());
return nullptr;
}
for (int32_t i = 1; i < dimensions->GetLength(); ++i) {
ObjPtr<mirror::Class> array_class_ptr = array_class.Get();
array_class.Assign(class_linker->FindArrayClass(self, &array_class_ptr));
- if (UNLIKELY(array_class.Get() == nullptr)) {
+ if (UNLIKELY(array_class == nullptr)) {
CHECK(self->IsExceptionPending());
return nullptr;
}
diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc
new file mode 100644
index 0000000000..eb613df4c6
--- /dev/null
+++ b/runtime/mirror/call_site.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 "call_site.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> CallSite::static_class_;
+
+mirror::CallSite* CallSite::Create(Thread* const self, Handle<MethodHandle> target) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::CallSite> cs(
+ hs.NewHandle(ObjPtr<CallSite>::DownCast(StaticClass()->AllocObject(self))));
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ cs->SetFieldObject<false>(TargetOffset(), target.Get());
+ return cs.Get();
+}
+
+void CallSite::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void CallSite::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void CallSite::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h
new file mode 100644
index 0000000000..db244a5442
--- /dev/null
+++ b/runtime/mirror/call_site.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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_MIRROR_CALL_SITE_H_
+#define ART_RUNTIME_MIRROR_CALL_SITE_H_
+
+#include "mirror/method_handle_impl.h"
+#include "utils.h"
+
+namespace art {
+
+struct CallSiteOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.CallSite
+class MANAGED CallSite : public Object {
+ public:
+ static mirror::CallSite* Create(Thread* const self,
+ Handle<MethodHandle> method_handle)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<MethodHandle>(TargetOffset());
+ }
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static inline MemberOffset TargetOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(CallSite, target_));
+ }
+
+ HeapReference<mirror::MethodHandle> target_;
+
+ static GcRoot<mirror::Class> static_class_; // java.lang.invoke.CallSite.class
+
+ friend struct art::CallSiteOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_CALL_SITE_H_
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 1b8f3f83e7..9a9a5d8398 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -81,7 +81,7 @@ ClassExt* Class::EnsureExtDataPresent(Thread* self) {
self->ClearException();
// Allocate the ClassExt
Handle<ClassExt> new_ext(hs.NewHandle(ClassExt::Alloc(self)));
- if (new_ext.Get() == nullptr) {
+ if (new_ext == nullptr) {
// OOM allocating the classExt.
// TODO Should we restore the suppressed exception?
self->AssertPendingOOMException();
@@ -103,7 +103,7 @@ ClassExt* Class::EnsureExtDataPresent(Thread* self) {
DCHECK(!set || h_this->GetExtData() == new_ext.Get());
CHECK(!ret.IsNull());
// Restore the exception if there was one.
- if (throwable.Get() != nullptr) {
+ if (throwable != nullptr) {
self->SetException(throwable.Get());
}
return ret.Ptr();
@@ -269,10 +269,10 @@ void Class::DumpClass(std::ostream& os, int flags) {
os << "----- " << (IsInterface() ? "interface" : "class") << " "
<< "'" << GetDescriptor(&temp) << "' cl=" << GetClassLoader() << " -----\n",
os << " objectSize=" << SizeOf() << " "
- << "(" << (h_super.Get() != nullptr ? h_super->SizeOf() : -1) << " from super)\n",
+ << "(" << (h_super != nullptr ? h_super->SizeOf() : -1) << " from super)\n",
os << StringPrintf(" access=0x%04x.%04x\n",
GetAccessFlags() >> 16, GetAccessFlags() & kAccJavaFlagsMask);
- if (h_super.Get() != nullptr) {
+ if (h_super != nullptr) {
os << " super='" << h_super->PrettyClass() << "' (cl=" << h_super->GetClassLoader()
<< ")\n";
}
@@ -297,7 +297,7 @@ void Class::DumpClass(std::ostream& os, int flags) {
} else {
// After this point, this may have moved due to GetDirectInterface.
os << " vtable (" << h_this->NumVirtualMethods() << " entries, "
- << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n";
+ << (h_super != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n";
for (size_t i = 0; i < NumVirtualMethods(); ++i) {
os << StringPrintf(" %2zd: %s\n", i, ArtMethod::PrettyMethod(
h_this->GetVirtualMethodDuringLinking(i, image_pointer_size)).c_str());
@@ -971,7 +971,7 @@ ObjPtr<Class> Class::ResolveDirectInterface(Thread* self, Handle<Class> klass, u
}
ObjPtr<Class> Class::GetCommonSuperClass(Handle<Class> klass) {
- DCHECK(klass.Get() != nullptr);
+ DCHECK(klass != nullptr);
DCHECK(!klass->IsInterface());
DCHECK(!IsInterface());
ObjPtr<Class> common_super_class = this;
@@ -1165,7 +1165,7 @@ ObjPtr<Method> Class::GetDeclaredMethodInternal(
constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic;
StackHandleScope<3> hs(self);
auto h_method_name = hs.NewHandle(name);
- if (UNLIKELY(h_method_name.Get() == nullptr)) {
+ if (UNLIKELY(h_method_name == nullptr)) {
ThrowNullPointerException("name == null");
return nullptr;
}
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index efd949e031..7270079a8f 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -58,8 +58,8 @@ bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) {
Handle<ObjectArray<DexCache>> old_dex_caches(hs.NewHandle(h_this->GetObsoleteDexCaches()));
ClassLinker* cl = Runtime::Current()->GetClassLinker();
size_t new_len;
- if (old_methods.Get() == nullptr) {
- CHECK(old_dex_caches.Get() == nullptr);
+ if (old_methods == nullptr) {
+ CHECK(old_dex_caches == nullptr);
new_len = increase;
} else {
CHECK_EQ(old_methods->GetLength(), old_dex_caches->GetLength());
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index a59bb7b880..973c8ed07d 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -26,6 +26,7 @@
#include "base/logging.h"
#include "gc_root.h"
#include "mirror/class.h"
+#include "mirror/call_site.h"
#include "mirror/method_type.h"
#include "runtime.h"
#include "obj_ptr.h"
@@ -106,6 +107,35 @@ inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* reso
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
}
+inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+ GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+ Atomic<GcRoot<mirror::CallSite>>& ref =
+ reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+ return ref.LoadSequentiallyConsistent().Read();
+}
+
+inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
+
+ GcRoot<mirror::CallSite> null_call_site(nullptr);
+ GcRoot<mirror::CallSite> candidate(call_site);
+ GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+
+ // The first assignment for a given call site wins.
+ Atomic<GcRoot<mirror::CallSite>>& ref =
+ reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
+ if (ref.CompareExchangeStrongSequentiallyConsistent(null_call_site, candidate)) {
+ // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+ return call_site;
+ } else {
+ return target.Read();
+ }
+}
+
inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB.
@@ -208,6 +238,11 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito
VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>(
GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
+
+ GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites();
+ for (size_t i = 0, num_call_sites = NumResolvedCallSites(); i != num_call_sites; ++i) {
+ visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
+ }
}
}
@@ -246,6 +281,17 @@ inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* d
}
}
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
+inline void DexCache::FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest,
+ const Visitor& visitor) {
+ GcRoot<mirror::CallSite>* src = GetResolvedCallSites();
+ for (size_t i = 0, count = NumResolvedCallSites(); i < count; ++i) {
+ mirror::CallSite* source = src[i].Read<kReadBarrierOption>();
+ mirror::CallSite* new_source = visitor(source);
+ dest[i] = GcRoot<mirror::CallSite>(new_source);
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 741cf3bb47..0f6acab7e1 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -90,6 +90,10 @@ void DexCache::InitializeDexCache(Thread* self,
raw_arrays + layout.MethodTypesOffset());
}
+ GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0)
+ ? nullptr
+ : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
+
DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
"Expected raw_arrays to align to StringDexCacheType.";
DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) <<
@@ -117,6 +121,9 @@ void DexCache::InitializeDexCache(Thread* self,
CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
}
+ for (size_t i = 0; i < dex_file->NumCallSiteIds(); ++i) {
+ CHECK(call_sites[i].IsNull());
+ }
}
if (strings != nullptr) {
mirror::StringDexCachePair::Initialize(strings);
@@ -136,6 +143,8 @@ void DexCache::InitializeDexCache(Thread* self,
dex_file->NumFieldIds(),
method_types,
num_method_types,
+ call_sites,
+ dex_file->NumCallSiteIds(),
image_pointer_size);
}
@@ -151,6 +160,8 @@ void DexCache::Init(const DexFile* dex_file,
uint32_t num_resolved_fields,
MethodTypeDexCacheType* resolved_method_types,
uint32_t num_resolved_method_types,
+ GcRoot<CallSite>* resolved_call_sites,
+ uint32_t num_resolved_call_sites,
PointerSize pointer_size) {
CHECK(dex_file != nullptr);
CHECK(location != nullptr);
@@ -159,6 +170,7 @@ void DexCache::Init(const DexFile* dex_file,
CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
+ CHECK_EQ(num_resolved_call_sites != 0u, resolved_call_sites != nullptr);
SetDexFile(dex_file);
SetLocation(location);
@@ -167,11 +179,13 @@ void DexCache::Init(const DexFile* dex_file,
SetResolvedMethods(resolved_methods);
SetResolvedFields(resolved_fields);
SetResolvedMethodTypes(resolved_method_types);
+ SetResolvedCallSites(resolved_call_sites);
SetField32<false>(NumStringsOffset(), num_strings);
SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
+ SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites);
Runtime* const runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 6f88cc5df4..10bb5aa01c 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -36,6 +36,7 @@ class Thread;
namespace mirror {
+class CallSite;
class MethodType;
class String;
@@ -163,6 +164,10 @@ class MANAGED DexCache FINAL : public Object {
void FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
+ void FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, const Visitor& visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
String* GetLocation() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_));
}
@@ -191,6 +196,10 @@ class MANAGED DexCache FINAL : public Object {
return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
}
+ static MemberOffset ResolvedCallSitesOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
+ }
+
static MemberOffset NumStringsOffset() {
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
}
@@ -211,6 +220,10 @@ class MANAGED DexCache FINAL : public Object {
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
}
+ static MemberOffset NumResolvedCallSitesOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
+ }
+
mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -244,7 +257,18 @@ class MANAGED DexCache FINAL : public Object {
MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Attempts to bind |call_site_idx| to the call site |resolved|. The
+ // caller must use the return value in place of |resolved|. This is
+ // because multiple threads can invoke the bootstrap method each
+ // producing a call site, but the method handle invocation on the
+ // call site must be on a common agreed value.
+ CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED
+ REQUIRES_SHARED(Locks::mutator_lock_);
StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr64<StringDexCacheType*>(StringsOffset());
@@ -295,6 +319,18 @@ class MANAGED DexCache FINAL : public Object {
SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
}
+ GcRoot<CallSite>* GetResolvedCallSites()
+ ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldPtr<GcRoot<CallSite>*>(ResolvedCallSitesOffset());
+ }
+
+ void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
+ ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
+ }
+
size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetField32(NumStringsOffset());
}
@@ -315,6 +351,10 @@ class MANAGED DexCache FINAL : public Object {
return GetField32(NumResolvedMethodTypesOffset());
}
+ size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetField32(NumResolvedCallSitesOffset());
+ }
+
const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
}
@@ -346,8 +386,10 @@ class MANAGED DexCache FINAL : public Object {
uint32_t num_resolved_methods,
ArtField** resolved_fields,
uint32_t num_resolved_fields,
- MethodTypeDexCacheType* resolved_methodtypes,
- uint32_t num_resolved_methodtypes,
+ MethodTypeDexCacheType* resolved_method_types,
+ uint32_t num_resolved_method_types,
+ GcRoot<CallSite>* resolved_call_sites,
+ uint32_t num_resolved_call_sites,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -362,6 +404,8 @@ class MANAGED DexCache FINAL : public Object {
HeapReference<Object> dex_;
HeapReference<String> location_;
uint64_t dex_file_; // const DexFile*
+ uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
+ // elements.
uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements.
uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
// num_resolved_method_types_ elements.
@@ -370,6 +414,7 @@ class MANAGED DexCache FINAL : public Object {
uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
// elements.
+ uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array.
uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array.
uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 8f978e122c..5a2ab7151c 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -47,7 +47,7 @@ TEST_F(DexCacheTest, Open) {
soa.Self(),
*java_lang_dex_file_,
Runtime::Current()->GetLinearAlloc())));
- ASSERT_TRUE(dex_cache.Get() != nullptr);
+ ASSERT_TRUE(dex_cache != nullptr);
EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings()
|| java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings());
@@ -95,10 +95,10 @@ TEST_F(DexCacheTest, TestResolvedFieldAccess) {
soa.Decode<mirror::ClassLoader>(jclass_loader)));
Handle<mirror::Class> klass1 =
hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
- ASSERT_TRUE(klass1.Get() != nullptr);
+ ASSERT_TRUE(klass1 != nullptr);
Handle<mirror::Class> klass2 =
hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
- ASSERT_TRUE(klass2.Get() != nullptr);
+ ASSERT_TRUE(klass2 != nullptr);
EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache());
EXPECT_NE(klass1->NumStaticFields(), 0u);
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index 978cc32320..be0eac05c9 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -173,13 +173,13 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs(
Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(self, array_class, refs_size)));
- if (references.Get() == nullptr) {
+ if (references == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
Handle<ByteArray> stack_frame(hs.NewHandle(ByteArray::Alloc(self, frame_size)));
- if (stack_frame.Get() == nullptr) {
+ if (stack_frame == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index c03f20a991..2496989337 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -33,7 +33,7 @@ inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field, b
// Try to resolve type before allocating since this is a thread suspension point.
Handle<mirror::Class> type = hs.NewHandle(field->GetType<true>());
- if (type.Get() == nullptr) {
+ if (type == nullptr) {
if (force_resolve) {
if (kIsDebugBuild) {
self->AssertPendingException();
@@ -49,7 +49,7 @@ inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field, b
}
}
auto ret = hs.NewHandle(ObjPtr<Field>::DownCast(StaticClass()->AllocObject(self)));
- if (UNLIKELY(ret.Get() == nullptr)) {
+ if (UNLIKELY(ret == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
}
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 4f1c448b56..fa4d25a031 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -28,6 +28,18 @@ mirror::Class* MethodHandle::StaticClass() {
return klass;
}
+void MethodHandle::Initialize(uintptr_t art_field_or_method,
+ Kind kind,
+ Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(!Runtime::Current()->IsActiveTransaction());
+ SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr);
+ SetFieldObject<false>(NominalTypeOffset(), nullptr);
+ SetFieldObject<false>(MethodTypeOffset(), method_type.Get());
+ SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind));
+ SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method);
+}
+
GcRoot<mirror::Class> MethodHandleImpl::static_class_;
void MethodHandleImpl::SetClass(Class* klass) {
@@ -45,5 +57,17 @@ void MethodHandleImpl::VisitRoots(RootVisitor* visitor) {
static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
+mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self,
+ uintptr_t art_field_or_method,
+ MethodHandle::Kind kind,
+ Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::MethodHandleImpl> mh(
+ hs.NewHandle(ObjPtr<MethodHandleImpl>::DownCast(StaticClass()->AllocObject(self))));
+ mh->Initialize(art_field_or_method, kind, method_type);
+ return mh.Get();
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 53d267b52c..9938af8abf 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -17,10 +17,11 @@
#ifndef ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
#define ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_
+#include "art_field.h"
+#include "art_method.h"
#include "class.h"
#include "gc_root.h"
#include "object-inl.h"
-#include "method_handles.h"
#include "method_type.h"
namespace art {
@@ -82,10 +83,19 @@ class MANAGED MethodHandle : public Object {
GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
}
+ ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ Kind kind = GetHandleKind();
+ return (kind <= kLastValidKind) ?
+ GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass();
+ }
+
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ protected:
+ void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
- // NOTE: cached_spread_invoker_ isn't used by the runtime.
HeapReference<mirror::MethodHandle> cached_spread_invoker_;
HeapReference<mirror::MethodType> nominal_type_;
HeapReference<mirror::MethodType> method_type_;
@@ -93,6 +103,9 @@ class MANAGED MethodHandle : public Object {
uint64_t art_field_or_method_;
private:
+ static MemberOffset CachedSpreadInvokerOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_));
+ }
static MemberOffset NominalTypeOffset() {
return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_));
}
@@ -113,6 +126,12 @@ class MANAGED MethodHandle : public Object {
// C++ mirror of java.lang.invoke.MethodHandleImpl
class MANAGED MethodHandleImpl : public MethodHandle {
public:
+ static mirror::MethodHandleImpl* Create(Thread* const self,
+ uintptr_t art_field_or_method,
+ MethodHandle::Kind kind,
+ Handle<MethodType> method_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
return static_class_.Read();
}
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
new file mode 100644
index 0000000000..c758e54dd4
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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 "method_handles_lookup.h"
+
+#include "class.h"
+#include "gc_root-inl.h"
+#include "object-inl.h"
+#include "handle_scope.h"
+#include "modifiers.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> MethodHandlesLookup::static_class_;
+
+void MethodHandlesLookup::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void MethodHandlesLookup::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle<Class> lookup_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
+ static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic;
+
+ StackHandleScope<1> hs(self);
+ Handle<MethodHandlesLookup> mhl(
+ hs.NewHandle(ObjPtr<MethodHandlesLookup>::DownCast(StaticClass()->AllocObject(self))));
+ mhl->SetFieldObject<false>(LookupClassOffset(), lookup_class.Get());
+ mhl->SetField32<false>(AllowedModesOffset(), kAllModes);
+ return mhl.Get();
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h
new file mode 100644
index 0000000000..63eb428f94
--- /dev/null
+++ b/runtime/mirror/method_handles_lookup.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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_MIRROR_METHOD_HANDLES_LOOKUP_H_
+#define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
+
+#include "obj_ptr.h"
+#include "gc_root.h"
+#include "object.h"
+#include "handle.h"
+#include "utils.h"
+
+namespace art {
+
+struct MethodHandlesLookupOffsets;
+class RootVisitor;
+
+namespace mirror {
+
+// C++ mirror of java.lang.invoke.MethodHandles.Lookup
+class MANAGED MethodHandlesLookup : public Object {
+ public:
+ static mirror::MethodHandlesLookup* Create(Thread* const self,
+ Handle<Class> lookup_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static MemberOffset AllowedModesOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, allowed_modes_));
+ }
+
+ static MemberOffset LookupClassOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, lookup_class_));
+ }
+
+ HeapReference<mirror::Class> lookup_class_;
+
+ int32_t allowed_modes_;
+
+ static GcRoot<mirror::Class> static_class_; // java.lang.invoke.MethodHandles.Lookup.class
+
+ friend struct art::MethodHandlesLookupOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 5d77a16e7d..4b8dfacec6 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -44,6 +44,22 @@ mirror::MethodType* MethodType::Create(Thread* const self,
return mt.Get();
}
+size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::ObjectArray<Class>* const p_types = GetPTypes();
+ const int32_t p_types_length = p_types->GetLength();
+
+ // Initialize |num_vregs| with number of parameters and only increment it for
+ // types requiring a second vreg.
+ size_t num_vregs = static_cast<size_t>(p_types_length);
+ for (int32_t i = 0; i < p_types_length; ++i) {
+ mirror::Class* klass = p_types->GetWithoutChecks(i);
+ if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
+ ++num_vregs;
+ }
+ }
+ return num_vregs;
+}
+
bool MethodType::IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::ObjectArray<Class>* const p_types = GetPTypes();
const int32_t params_length = p_types->GetLength();
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 9a98143144..374bbe5df3 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -44,6 +44,10 @@ class MANAGED MethodType : public Object {
return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_));
}
+ // Number of virtual registers required to hold the parameters for
+ // this method type.
+ size_t NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_);
+
Class* GetRType() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_));
}
diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc
index 637bafd75e..41231ef617 100644
--- a/runtime/mirror/method_type_test.cc
+++ b/runtime/mirror/method_type_test.cc
@@ -51,7 +51,7 @@ static mirror::MethodType* CreateMethodType(const std::string& return_type,
Handle<mirror::Class> return_clazz = hs.NewHandle(class_linker->FindClass(
soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader));
- CHECK(return_clazz.Get() != nullptr);
+ CHECK(return_clazz != nullptr);
ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 6a4ec9dca7..e761e4db7a 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -530,8 +530,8 @@ TEST_F(ObjectTest, InstanceOf) {
Handle<Object> x(hs.NewHandle(X->AllocObject(soa.Self())));
Handle<Object> y(hs.NewHandle(Y->AllocObject(soa.Self())));
- ASSERT_TRUE(x.Get() != nullptr);
- ASSERT_TRUE(y.Get() != nullptr);
+ ASSERT_TRUE(x != nullptr);
+ ASSERT_TRUE(y != nullptr);
EXPECT_TRUE(x->InstanceOf(X));
EXPECT_FALSE(x->InstanceOf(Y));
@@ -650,7 +650,7 @@ TEST_F(ObjectTest, FindInstanceField) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
Handle<String> s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC")));
- ASSERT_TRUE(s.Get() != nullptr);
+ ASSERT_TRUE(s != nullptr);
Class* c = s->GetClass();
ASSERT_TRUE(c != nullptr);
@@ -684,9 +684,9 @@ TEST_F(ObjectTest, FindStaticField) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<4> hs(soa.Self());
Handle<String> s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC")));
- ASSERT_TRUE(s.Get() != nullptr);
+ ASSERT_TRUE(s != nullptr);
Handle<Class> c(hs.NewHandle(s->GetClass()));
- ASSERT_TRUE(c.Get() != nullptr);
+ ASSERT_TRUE(c != nullptr);
// Wrong type.
EXPECT_TRUE(c->FindDeclaredStaticField("CASE_INSENSITIVE_ORDER", "I") == nullptr);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 9b8445dc9e..c2407d7772 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -308,7 +308,7 @@ inline int32_t String::GetHashCode() {
}
template<typename MemoryType>
-bool String::AllASCII(const MemoryType* const chars, const int length) {
+inline bool String::AllASCII(const MemoryType* chars, const int length) {
static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType");
for (int i = 0; i < length; ++i) {
// Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII
@@ -320,6 +320,13 @@ bool String::AllASCII(const MemoryType* const chars, const int length) {
return true;
}
+inline bool String::DexFileStringAllASCII(const char* chars, const int length) {
+ // For strings from the dex file we just need to check that
+ // the terminating character is at the right position.
+ DCHECK_EQ(AllASCII(reinterpret_cast<const uint8_t*>(chars), length), chars[length] == 0);
+ return chars[length] == 0;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 409c6c2896..38f6dd4b6f 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -184,7 +184,9 @@ class MANAGED String FINAL : public Object {
bool IsValueNull() REQUIRES_SHARED(Locks::mutator_lock_);
template<typename MemoryType>
- static bool AllASCII(const MemoryType* const chars, const int length);
+ static bool AllASCII(const MemoryType* chars, const int length);
+
+ static bool DexFileStringAllASCII(const char* chars, const int length);
ALWAYS_INLINE static bool IsCompressed(int32_t count) {
return GetCompressionFlagFromCount(count) == StringCompressionFlag::kCompressed;
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index a32003e81b..f3cb0df80e 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1380,7 +1380,7 @@ void MonitorList::Add(Monitor* m) {
while (!kUseReadBarrier && UNLIKELY(!allow_new_monitors_)) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
- self->CheckEmptyCheckpoint();
+ self->CheckEmptyCheckpointFromWeakRefAccess(&monitor_list_lock_);
monitor_add_condition_.WaitHoldingLocks(self);
}
list_.push_front(m);
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 4fbfe4781c..27ce149342 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -77,7 +77,7 @@ static void FillHeap(Thread* self, ClassLinker* class_linker,
while (length > 10) {
MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4)));
- if (self->IsExceptionPending() || h.Get() == nullptr) {
+ if (self->IsExceptionPending() || h == nullptr) {
self->ClearException();
// Try a smaller length
@@ -95,7 +95,7 @@ static void FillHeap(Thread* self, ClassLinker* class_linker,
// Allocate simple objects till it fails.
while (!self->IsExceptionPending()) {
MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
- if (!self->IsExceptionPending() && h.Get() != nullptr) {
+ if (!self->IsExceptionPending() && h != nullptr) {
handles->push_back(h);
}
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 24308d9e81..6bfccdc8fb 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -350,7 +350,7 @@ static void PreloadDexCachesResolveField(Handle<mirror::DexCache> dex_cache, uin
Thread* const self = Thread::Current();
StackHandleScope<1> hs(self);
Handle<mirror::Class> klass(hs.NewHandle(dex_cache->GetResolvedType(field_id.class_idx_)));
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
return;
}
if (is_static) {
@@ -512,7 +512,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
CHECK(dex_file != nullptr);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file, nullptr)));
- CHECK(dex_cache.Get() != nullptr); // Boot class path dex caches are never unloaded.
+ CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded.
if (kPreloadDexCachesStrings) {
for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
PreloadDexCachesResolveString(dex_cache, dex::StringIndex(j), strings);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 5438a6ddb4..256787b2a1 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -81,7 +81,7 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> c(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
- if (c.Get() == nullptr) {
+ if (c == nullptr) {
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
env->ExceptionClear();
jthrowable cnfe = reinterpret_cast<jthrowable>(
@@ -137,7 +137,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
size_t array_idx = 0;
auto object_array = hs.NewHandle(mirror::ObjectArray<mirror::Field>::Alloc(
self, mirror::Field::ArrayClass(), array_size));
- if (object_array.Get() == nullptr) {
+ if (object_array == nullptr) {
return nullptr;
}
for (ArtField& field : ifields) {
@@ -267,7 +267,7 @@ static mirror::Field* GetPublicFieldRecursive(
Handle<mirror::String> h_name(hs.NewHandle(name));
// We search the current class, its direct interfaces then its superclass.
- while (h_clazz.Get() != nullptr) {
+ while (h_clazz != nullptr) {
mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), h_name.Get());
if ((result != nullptr) && (result->GetAccessFlags() & kAccPublic)) {
return result;
@@ -319,14 +319,14 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<3> hs(soa.Self());
Handle<mirror::String> h_string = hs.NewHandle(soa.Decode<mirror::String>(name));
- if (h_string.Get() == nullptr) {
+ if (h_string == nullptr) {
ThrowNullPointerException("name == null");
return nullptr;
}
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
Handle<mirror::Field> result =
hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
- if (result.Get() == nullptr) {
+ if (result == nullptr) {
std::string name_str = h_string->ToModifiedUtf8();
if (name_str == "value" && h_klass->IsStringClass()) {
// We log the error for this specific case, as the user might just swallow the exception.
@@ -377,7 +377,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal(
}
auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
soa.Self(), mirror::Constructor::ArrayClass(), constructor_count));
- if (UNLIKELY(h_constructors.Get() == nullptr)) {
+ if (UNLIKELY(h_constructors == nullptr)) {
soa.Self()->AssertPendingException();
return nullptr;
}
@@ -428,7 +428,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
}
auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc(
soa.Self(), mirror::Method::ArrayClass(), num_methods));
- if (ret.Get() == nullptr) {
+ if (ret == nullptr) {
soa.Self()->AssertPendingOOMException();
return nullptr;
}
@@ -645,7 +645,7 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
// Verify that we can access the class.
if (!klass->IsPublic()) {
caller.Assign(GetCallingClass(soa.Self(), 1));
- if (caller.Get() != nullptr && !caller->CanAccess(klass.Get())) {
+ if (caller != nullptr && !caller->CanAccess(klass.Get())) {
soa.Self()->ThrowNewExceptionF(
"Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
klass->PrettyClass().c_str(), caller->PrettyClass().c_str());
@@ -673,17 +673,17 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
}
}
auto receiver = hs.NewHandle(klass->AllocObject(soa.Self()));
- if (UNLIKELY(receiver.Get() == nullptr)) {
+ if (UNLIKELY(receiver == nullptr)) {
soa.Self()->AssertPendingOOMException();
return nullptr;
}
// Verify that we can access the constructor.
auto* declaring_class = constructor->GetDeclaringClass();
if (!constructor->IsPublic()) {
- if (caller.Get() == nullptr) {
+ if (caller == nullptr) {
caller.Assign(GetCallingClass(soa.Self(), 1));
}
- if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(receiver.Get(),
+ if (UNLIKELY(caller != nullptr && !VerifyAccess(receiver.Get(),
declaring_class,
constructor->GetAccessFlags(),
caller.Get()))) {
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
index 72a37f875d..9113841909 100644
--- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
@@ -57,7 +57,7 @@ static jobject MethodHandleImpl_getMemberInternal(JNIEnv* env, jobject thiz) {
}
}
- if (UNLIKELY(h_object.Get() == nullptr)) {
+ if (UNLIKELY(h_object == nullptr)) {
soa.Self()->AssertPendingOOMException();
return nullptr;
}
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index ee59c4a9e2..2a3942829f 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -103,7 +103,7 @@ static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
}
// Validate the MethodParameters system annotation data.
- if (UNLIKELY(names.Get() == nullptr || access_flags.Get() == nullptr)) {
+ if (UNLIKELY(names == nullptr || access_flags == nullptr)) {
ThrowIllegalArgumentException(
StringPrintf("Missing parameter metadata for names or access flags for %s",
art_method->PrettyMethod().c_str()).c_str());
@@ -132,7 +132,7 @@ static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
mirror::ObjectArray<mirror::Object>::Alloc(self,
parameter_array_class.Get(),
names_count));
- if (UNLIKELY(parameter_array.Get() == nullptr)) {
+ if (UNLIKELY(parameter_array == nullptr)) {
self->AssertPendingException();
return nullptr;
}
@@ -154,7 +154,7 @@ static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
// Allocate / initialize the Parameter to add to parameter_array.
parameter.Assign(parameter_class->AllocObject(self));
- if (UNLIKELY(parameter.Get() == nullptr)) {
+ if (UNLIKELY(parameter == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
}
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index 2590452678..e51b6d2a89 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -155,7 +155,7 @@ static jbyteArray charsToBytes(JNIEnv* env, jstring java_string, jint offset, ji
ScopedObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string)));
- if (string.Get() == nullptr) {
+ if (string == nullptr) {
return nullptr;
}
@@ -192,7 +192,7 @@ static jbyteArray CharsetUtils_toUtf8Bytes(JNIEnv* env, jclass, jstring java_str
ScopedObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string)));
- if (string.Get() == nullptr) {
+ if (string == nullptr) {
return nullptr;
}
diff --git a/runtime/oat.h b/runtime/oat.h
index e7e8328796..0f6657b7ed 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '1', '1', '0', '\0' }; // Clean up code info change.
+ static constexpr uint8_t kOatVersion[] = { '1', '1', '2', '\0' }; // Manual bump (Revert^3 hash-based DexCache types; stack maps).
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 31eb1ccdc8..493da271d1 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -273,6 +273,36 @@ inline static bool ReadOatDexFileData(const OatFile& oat_file,
return true;
}
+static bool FindDexFileMapItem(const uint8_t* dex_begin,
+ const uint8_t* dex_end,
+ DexFile::MapItemType map_item_type,
+ const DexFile::MapItem** result_item) {
+ *result_item = nullptr;
+
+ const DexFile::Header* header =
+ BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
+ if (nullptr == header) return false;
+
+ if (!DexFile::IsMagicValid(header->magic_)) return true; // Not a dex file, not an error.
+
+ const DexFile::MapList* map_list =
+ BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
+ if (nullptr == map_list) return false;
+
+ const DexFile::MapItem* map_item = map_list->list_;
+ size_t count = map_list->size_;
+ while (count--) {
+ if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
+ *result_item = map_item;
+ break;
+ }
+ map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
+ if (nullptr == map_item) return false;
+ }
+
+ return true;
+}
+
bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -501,7 +531,19 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
uint8_t* current_dex_cache_arrays = nullptr;
if (dex_cache_arrays != nullptr) {
- DexCacheArraysLayout layout(pointer_size, *header);
+ // All DexCache types except for CallSite have their instance counts in the
+ // DexFile header. For CallSites, we need to read the info from the MapList.
+ const DexFile::MapItem* call_sites_item = nullptr;
+ if (!FindDexFileMapItem(DexBegin(),
+ DexEnd(),
+ DexFile::MapItemType::kDexTypeCallSiteIdItem,
+ &call_sites_item)) {
+ *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
+ GetLocation().c_str());
+ return false;
+ }
+ size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
+ DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
if (layout.Size() != 0u) {
if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
@@ -1468,77 +1510,6 @@ std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>
return out.str();
}
-bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) {
- if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
- // No dependencies.
- return true;
- }
-
- // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
- // Split() instead of manual parsing of the combined char*.
- std::vector<std::string> split;
- Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
- if (split.size() % 2 != 0) {
- // Expected pairs of location and checksum.
- *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies);
- return false;
- }
-
- for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
- std::string& location = *it;
- std::string& checksum = *(it + 1);
- int64_t converted = strtoll(checksum.c_str(), nullptr, 10);
- if (converted == 0) {
- // Conversion error.
- *msg = StringPrintf("Conversion error for %s", checksum.c_str());
- return false;
- }
-
- uint32_t dex_checksum;
- std::string error_msg;
- if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(),
- &dex_checksum,
- &error_msg)) {
- if (converted != dex_checksum) {
- *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u",
- location.c_str(), converted, dex_checksum);
- return false;
- }
- } else {
- // Problem retrieving checksum.
- // TODO: odex files?
- *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(),
- error_msg.c_str());
- return false;
- }
- }
-
- return true;
-}
-
-bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies,
- std::vector<std::string>* locations) {
- DCHECK(locations != nullptr);
- if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
- return true;
- }
-
- // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
- // Split() instead of manual parsing of the combined char*.
- std::vector<std::string> split;
- Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
- if (split.size() % 2 != 0) {
- // Expected pairs of location and checksum.
- return false;
- }
-
- for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
- locations->push_back(*it);
- }
-
- return true;
-}
-
OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file,
uint16_t class_def_idx,
bool* found) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 111755e7a1..d24283afee 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -290,15 +290,6 @@ class OatFile {
// Create a dependency list (dex locations and checksums) for the given dex files.
static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files);
- // Check the given dependency list against their dex files - thus the name "Static," this does
- // not check the class-loader environment, only whether there have been file updates.
- static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg);
-
- // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic
- // locations of multidex files.
- static bool GetDexLocationsFromDependencies(const char* dex_dependencies,
- std::vector<std::string>* locations);
-
// Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
// error and sets found to false.
static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found);
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 77cdd28d3a..5ae2fc51b7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -38,6 +38,8 @@
namespace art {
+using android::base::StringPrintf;
+
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
switch (status) {
case OatFileAssistant::kOatCannotOpen:
@@ -264,7 +266,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
const OatFile& oat_file, const char* dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
- // Load the primary dex file.
+ // Load the main dex file.
std::string error_msg;
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
dex_location, nullptr, &error_msg);
@@ -280,12 +282,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
}
dex_files.push_back(std::move(dex_file));
- // Load secondary multidex files
+ // Load the rest of the multidex entries
for (size_t i = 1; ; i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
- oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
+ std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
+ oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
- // There are no more secondary dex files to load.
+ // There are no more multidex entries to load.
break;
}
@@ -300,10 +302,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
}
bool OatFileAssistant::HasOriginalDexFiles() {
- // Ensure GetRequiredDexChecksum has been run so that
+ // Ensure GetRequiredDexChecksums has been run so that
// has_original_dex_files_ is initialized. We don't care about the result of
- // GetRequiredDexChecksum.
- GetRequiredDexChecksum();
+ // GetRequiredDexChecksums.
+ GetRequiredDexChecksums();
return has_original_dex_files_;
}
@@ -316,88 +318,66 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
}
bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) {
- if (file.GetHeader().GetNumberOfDexFiles() <= 0) {
- VLOG(oat) << "Vdex does not contain any dex files";
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+ if (required_dex_checksums == nullptr) {
+ LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+ return true;
+ }
+
+ uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles();
+ if (required_dex_checksums->size() != number_of_dex_files) {
+ *error_msg = StringPrintf("expected %zu dex files but found %u",
+ required_dex_checksums->size(),
+ number_of_dex_files);
return false;
}
- // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not
- // just the primary. Because otherwise we may fail to see a secondary
- // checksum failure in the case when the original (multidex) files are
- // stripped but we have a newer odex file.
- const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
- if (dex_checksum_pointer != nullptr) {
- uint32_t actual_checksum = file.GetLocationChecksum(0);
- if (*dex_checksum_pointer != actual_checksum) {
- VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_
- << ". Expected: " << *dex_checksum_pointer
- << ", Actual: " << actual_checksum;
+ for (uint32_t i = 0; i < number_of_dex_files; i++) {
+ uint32_t expected_checksum = (*required_dex_checksums)[i];
+ uint32_t actual_checksum = file.GetLocationChecksum(i);
+ if (expected_checksum != actual_checksum) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ *error_msg = StringPrintf("Dex checksum does not match for dex: %s."
+ "Expected: %u, actual: %u",
+ dex.c_str(),
+ expected_checksum,
+ actual_checksum);
return false;
}
}
- // Verify the dex checksums for any secondary multidex files
- for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
- uint32_t expected_secondary_checksum = 0;
- if (DexFile::GetChecksum(secondary_dex_location.c_str(),
- &expected_secondary_checksum,
- error_msg)) {
- uint32_t actual_secondary_checksum = file.GetLocationChecksum(i);
- if (expected_secondary_checksum != actual_secondary_checksum) {
- VLOG(oat) << "Dex checksum does not match for secondary dex: "
- << secondary_dex_location
- << ". Expected: " << expected_secondary_checksum
- << ", Actual: " << actual_secondary_checksum;
- return false;
- }
- } else {
- // If we can't get the checksum for the secondary location, we assume
- // the dex checksum is up to date for this and all other secondary dex
- // files.
- break;
- }
- }
return true;
}
bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
- // Note: GetOatDexFile will return null if the dex checksum doesn't match
- // what we provide, which verifies the primary dex checksum for us.
- const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
- const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(
- dex_location_.c_str(), dex_checksum_pointer, error_msg);
- if (oat_dex_file == nullptr) {
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+ if (required_dex_checksums == nullptr) {
+ LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+ return true;
+ }
+
+ uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount();
+ if (required_dex_checksums->size() != number_of_dex_files) {
+ *error_msg = StringPrintf("expected %zu dex files but found %u",
+ required_dex_checksums->size(),
+ number_of_dex_files);
return false;
}
- // Verify the dex checksums for any secondary multidex files
- for (size_t i = 1; ; i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
- const OatFile::OatDexFile* secondary_oat_dex_file
- = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
- if (secondary_oat_dex_file == nullptr) {
- // There are no more secondary dex files to check.
- break;
+ for (uint32_t i = 0; i < number_of_dex_files; i++) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ uint32_t expected_checksum = (*required_dex_checksums)[i];
+ const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str());
+ return false;
}
-
- uint32_t expected_secondary_checksum = 0;
- if (DexFile::GetChecksum(secondary_dex_location.c_str(),
- &expected_secondary_checksum, error_msg)) {
- uint32_t actual_secondary_checksum
- = secondary_oat_dex_file->GetDexFileLocationChecksum();
- if (expected_secondary_checksum != actual_secondary_checksum) {
- VLOG(oat) << "Dex checksum does not match for secondary dex: "
- << secondary_dex_location
- << ". Expected: " << expected_secondary_checksum
- << ", Actual: " << actual_secondary_checksum;
- return false;
- }
- } else {
- // If we can't get the checksum for the secondary location, we assume
- // the dex checksum is up to date for this and all other secondary dex
- // files.
- break;
+ uint32_t actual_checksum = oat_dex_file->GetDexFileLocationChecksum();
+ if (expected_checksum != actual_checksum) {
+ VLOG(oat) << "Dex checksum does not match for dex: " << dex
+ << ". Expected: " << expected_checksum
+ << ", Actual: " << actual_checksum;
+ return false;
}
}
return true;
@@ -710,13 +690,16 @@ std::string OatFileAssistant::ImageLocation() {
return image_spaces[0]->GetImageLocation();
}
-const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
- if (!required_dex_checksum_attempted_) {
- required_dex_checksum_attempted_ = true;
- required_dex_checksum_found_ = false;
+const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
+ if (!required_dex_checksums_attempted_) {
+ required_dex_checksums_attempted_ = true;
+ required_dex_checksums_found_ = false;
+ cached_required_dex_checksums_.clear();
std::string error_msg;
- if (DexFile::GetChecksum(dex_location_.c_str(), &cached_required_dex_checksum_, &error_msg)) {
- required_dex_checksum_found_ = true;
+ if (DexFile::GetMultiDexChecksums(dex_location_.c_str(),
+ &cached_required_dex_checksums_,
+ &error_msg)) {
+ required_dex_checksums_found_ = true;
has_original_dex_files_ = true;
} else {
// This can happen if the original dex file has been stripped from the
@@ -724,19 +707,23 @@ const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
VLOG(oat) << "OatFileAssistant: " << error_msg;
has_original_dex_files_ = false;
- // Get the checksum from the odex if we can.
+ // Get the checksums from the odex if we can.
const OatFile* odex_file = odex_.GetFile();
if (odex_file != nullptr) {
- const OatFile::OatDexFile* odex_dex_file
- = odex_file->GetOatDexFile(dex_location_.c_str(), nullptr);
- if (odex_dex_file != nullptr) {
- cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
- required_dex_checksum_found_ = true;
+ required_dex_checksums_found_ = true;
+ for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr);
+ if (odex_dex_file == nullptr) {
+ required_dex_checksums_found_ = false;
+ break;
+ }
+ cached_required_dex_checksums_.push_back(odex_dex_file->GetDexFileLocationChecksum());
}
}
}
}
- return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
+ return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
}
const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6d47ad2228..3ede29f5e0 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -400,13 +400,13 @@ class OatFileAssistant {
// the oat file assistant.
static std::string ImageLocation();
- // Gets the dex checksum required for an up-to-date oat file.
- // Returns dex_checksum if a required checksum was located. Returns
- // null if the required checksum was not found.
- // The caller shouldn't clean up or free the returned pointer.
- // This sets the has_original_dex_files_ field to true if a checksum was
- // found for the dex_location_ dex file.
- const uint32_t* GetRequiredDexChecksum();
+ // Gets the dex checksums required for an up-to-date oat file.
+ // Returns cached_required_dex_checksums if the required checksums were
+ // located. Returns null if the required checksums were not found. The
+ // caller shouldn't clean up or free the returned pointer. This sets the
+ // has_original_dex_files_ field to true if the checksums were found for the
+ // dex_location_ dex file.
+ const std::vector<uint32_t>* GetRequiredDexChecksums();
// Returns the loaded image info.
// Loads the image info if needed. Returns null if the image info failed
@@ -430,11 +430,11 @@ class OatFileAssistant {
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
- // Cached value of the required dex checksum.
- // This should be accessed only by the GetRequiredDexChecksum() method.
- uint32_t cached_required_dex_checksum_;
- bool required_dex_checksum_attempted_ = false;
- bool required_dex_checksum_found_;
+ // Cached value of the required dex checksums.
+ // This should be accessed only by the GetRequiredDexChecksums() method.
+ std::vector<uint32_t> cached_required_dex_checksums_;
+ bool required_dex_checksums_attempted_ = false;
+ bool required_dex_checksums_found_;
bool has_original_dex_files_;
OatFileInfo odex_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index f777340cfd..9b35489330 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -237,16 +237,16 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
EXPECT_EQ(2u, dex_files.size());
}
-// Case: We have a MultiDEX file where the secondary dex file is out of date.
+// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
- std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar";
// Compile code for GetMultiDexSrc1.
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
- // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+ // Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
@@ -256,6 +256,37 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
+// Case: We have a stripped MultiDEX file where the non-main multidex entry is
+// out of date with respect to the odex file.
+TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/StrippedMultiDexNonMainOutOfDate.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedMultiDexNonMainOutOfDate.odex";
+
+ // Compile the oat from GetMultiDexSrc1.
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Compile the odex from GetMultiDexSrc2, which has a different non-main
+ // dex checksum.
+ Copy(GetMultiDexSrc2(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kInterpretOnly);
+
+ // Strip the dex file.
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, /*load_executable*/false);
+
+ // Because the dex file is stripped, the odex file is considered the source
+ // of truth for the dex checksums. The oat file should be considered
+ // unusable.
+ std::unique_ptr<OatFile> best_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(best_file.get() != nullptr);
+ EXPECT_EQ(best_file->GetLocation(), odex_location);
+ EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+}
+
// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
// encoded dex locations.
// Expect: The oat file status is kNoDexOptNeeded.
@@ -336,16 +367,16 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) {
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
}
-// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is
-// out of date and there is no corresponding ODEX file.
-TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) {
+// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry
+// is out of date and there is no corresponding ODEX file.
+TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
// This test case is only meaningful if vdex is enabled.
if (!kIsVdexEnabled) {
return;
}
- std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar";
- std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat";
+ std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
+ std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);
@@ -1028,7 +1059,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) {
ClassLinker* linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> dexfile(
hs.NewHandle(linker->FindSystemClass(soa.Self(), "Ldalvik/system/DexFile;")));
- ASSERT_FALSE(dexfile.Get() == nullptr);
+ ASSERT_FALSE(dexfile == nullptr);
linker->EnsureInitialized(soa.Self(), dexfile, true, true);
for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index a46b47075c..70796148a4 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -342,7 +342,7 @@ static void GetDexFilesFromDexElementsArray(
ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
std::priority_queue<DexFileAndClassPair>* queue) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (dex_elements.Get() == nullptr) {
+ if (dex_elements == nullptr) {
// Nothing to do.
return;
}
@@ -463,14 +463,14 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file,
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements =
hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements));
- if (h_class_loader.Get() != nullptr &&
+ if (h_class_loader != nullptr &&
GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) {
class_loader_ok = true;
// In this case, also take into account the dex_elements array, if given. We don't need to
// read it otherwise, as we'll compare against all open oat files anyways.
GetDexFilesFromDexElementsArray(soa, h_dex_elements, &queue);
- } else if (h_class_loader.Get() != nullptr) {
+ } else if (h_class_loader != nullptr) {
VLOG(class_linker) << "Something unsupported with "
<< mirror::Class::PrettyClass(h_class_loader->GetClass());
}
@@ -658,7 +658,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
Handle<mirror::ClassLoader> h_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
// Can not load app image without class loader.
- if (h_loader.Get() != nullptr) {
+ if (h_loader != nullptr) {
std::string temp_error_msg;
// Add image space has a race condition since other threads could be reading from the
// spaces array.
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index b416b9dbad..d5fe1f382a 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -62,54 +62,4 @@ TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) {
"/data/app/foo/base.apk", "o/base.apk"));
}
-static std::vector<const DexFile*> ToConstDexFiles(
- const std::vector<std::unique_ptr<const DexFile>>& in) {
- std::vector<const DexFile*> ret;
- for (auto& d : in) {
- ret.push_back(d.get());
- }
- return ret;
-}
-
-TEST_F(OatFileTest, DexFileDependencies) {
- std::string error_msg;
-
- // No dependencies.
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg;
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg;
-
- // Ill-formed dependencies.
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg));
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg));
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg));
-
- // Unsatisfiable dependency.
- EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg));
-
- // Load some dex files to be able to do a real test.
- ScopedObjectAccess soa(Thread::Current());
-
- std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main");
- std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1);
- std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1);
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg))
- << error_msg << " " << encoding1;
- std::vector<std::string> split1;
- EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1));
- ASSERT_EQ(split1.size(), 1U);
- EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation());
-
- std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex");
- EXPECT_GT(dex_files2.size(), 1U);
- std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2);
- std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2);
- EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg))
- << error_msg << " " << encoding2;
- std::vector<std::string> split2;
- EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2));
- ASSERT_EQ(split2.size(), 2U);
- EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation());
- EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation());
-}
-
} // namespace art
diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc
index 39ab52fb2d..f6db544276 100644
--- a/runtime/object_lock.cc
+++ b/runtime/object_lock.cc
@@ -24,7 +24,7 @@ namespace art {
template <typename T>
ObjectLock<T>::ObjectLock(Thread* self, Handle<T> object) : self_(self), obj_(object) {
- CHECK(object.Get() != nullptr);
+ CHECK(object != nullptr);
obj_->MonitorEnter(self_);
}
@@ -50,7 +50,7 @@ void ObjectLock<T>::NotifyAll() {
template <typename T>
ObjectTryLock<T>::ObjectTryLock(Thread* self, Handle<T> object) : self_(self), obj_(object) {
- CHECK(object.Get() != nullptr);
+ CHECK(object != nullptr);
acquired_ = obj_->MonitorTryEnter(self_) != nullptr;
}
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index c14fd84264..7ca233fb10 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -42,12 +42,17 @@
#include "class_linker.h"
#include "common_throws.h"
#include "events-inl.h"
+#include "gc/heap.h"
+#include "gc_root.h"
#include "handle.h"
#include "jni_env_ext-inl.h"
#include "jni_internal.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
+#include "mirror/object_reference.h"
+#include "mirror/object-inl.h"
+#include "mirror/reference.h"
#include "runtime.h"
#include "runtime_callbacks.h"
#include "ScopedLocalRef.h"
@@ -261,15 +266,22 @@ struct ClassCallback : public art::ClassLoadCallback {
thread_jni.get(),
jklass.get());
}
- AddTempClass(thread, jklass.get());
+ if (klass->IsTemp()) {
+ AddTempClass(thread, jklass.get());
+ }
}
}
- void ClassPrepare(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+ void ClassPrepare(art::Handle<art::mirror::Class> temp_klass,
art::Handle<art::mirror::Class> klass)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassPrepare)) {
art::Thread* thread = art::Thread::Current();
+ if (temp_klass.Get() != klass.Get()) {
+ DCHECK(temp_klass->IsTemp());
+ DCHECK(temp_klass->IsRetired());
+ HandleTempClass(thread, temp_klass, klass);
+ }
ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
ScopedLocalRef<jthread> thread_jni(
@@ -283,32 +295,209 @@ struct ClassCallback : public art::ClassLoadCallback {
}
}
+ // To support parallel class-loading, we need to perform some locking dances here. Namely,
+ // the fixup stage must not be holding the temp_classes lock when it fixes up the system
+ // (as that requires suspending all mutators).
+
void AddTempClass(art::Thread* self, jclass klass) {
std::unique_lock<std::mutex> mu(temp_classes_lock);
- temp_classes.push_back(reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass)));
+ jclass global_klass = reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass));
+ temp_classes.push_back(global_klass);
}
- void HandleTempClass(art::Handle<art::mirror::Class> temp_klass,
+ void HandleTempClass(art::Thread* self,
+ art::Handle<art::mirror::Class> temp_klass,
art::Handle<art::mirror::Class> klass)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- std::unique_lock<std::mutex> mu(temp_classes_lock);
- if (temp_classes.empty()) {
- return;
- }
+ bool requires_fixup = false;
+ {
+ std::unique_lock<std::mutex> mu(temp_classes_lock);
+ if (temp_classes.empty()) {
+ return;
+ }
- art::Thread* self = art::Thread::Current();
- for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) {
- if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) {
- temp_classes.erase(it);
- FixupTempClass(temp_klass, klass);
+ for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) {
+ if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) {
+ self->GetJniEnv()->DeleteGlobalRef(*it);
+ temp_classes.erase(it);
+ requires_fixup = true;
+ break;
+ }
}
}
+ if (requires_fixup) {
+ FixupTempClass(self, temp_klass, klass);
+ }
}
- void FixupTempClass(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
- art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED)
+ void FixupTempClass(art::Thread* self,
+ art::Handle<art::mirror::Class> temp_klass,
+ art::Handle<art::mirror::Class> klass)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- // TODO: Implement.
+ // Suspend everything.
+ art::gc::Heap* heap = art::Runtime::Current()->GetHeap();
+ if (heap->IsGcConcurrentAndMoving()) {
+ // Need to take a heap dump while GC isn't running. See the
+ // comment in Heap::VisitObjects().
+ heap->IncrementDisableMovingGC(self);
+ }
+ {
+ art::ScopedThreadSuspension sts(self, art::kWaitingForVisitObjects);
+ art::ScopedSuspendAll ssa("FixupTempClass");
+
+ art::mirror::Class* input = temp_klass.Get();
+ art::mirror::Class* output = klass.Get();
+
+ FixupGlobalReferenceTables(input, output);
+ FixupLocalReferenceTables(self, input, output);
+ FixupHeap(input, output);
+ }
+ if (heap->IsGcConcurrentAndMoving()) {
+ heap->DecrementDisableMovingGC(self);
+ }
+ }
+
+ class RootUpdater : public art::RootVisitor {
+ public:
+ RootUpdater(const art::mirror::Class* input, art::mirror::Class* output)
+ : input_(input), output_(output) {}
+
+ void VisitRoots(art::mirror::Object*** roots,
+ size_t count,
+ const art::RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE {
+ for (size_t i = 0; i != count; ++i) {
+ if (*roots[i] == input_) {
+ *roots[i] = output_;
+ }
+ }
+ }
+
+ void VisitRoots(art::mirror::CompressedReference<art::mirror::Object>** roots,
+ size_t count,
+ const art::RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ for (size_t i = 0; i != count; ++i) {
+ if (roots[i]->AsMirrorPtr() == input_) {
+ roots[i]->Assign(output_);
+ }
+ }
+ }
+
+ private:
+ const art::mirror::Class* input_;
+ art::mirror::Class* output_;
+ };
+
+ void FixupGlobalReferenceTables(art::mirror::Class* input, art::mirror::Class* output)
+ REQUIRES(art::Locks::mutator_lock_) {
+ art::JavaVMExt* java_vm = art::Runtime::Current()->GetJavaVM();
+
+ // Fix up the global table with a root visitor.
+ RootUpdater global_update(input, output);
+ java_vm->VisitRoots(&global_update);
+
+ class WeakGlobalUpdate : public art::IsMarkedVisitor {
+ public:
+ WeakGlobalUpdate(art::mirror::Class* root_input, art::mirror::Class* root_output)
+ : input_(root_input), output_(root_output) {}
+
+ art::mirror::Object* IsMarked(art::mirror::Object* obj) OVERRIDE {
+ if (obj == input_) {
+ return output_;
+ }
+ return obj;
+ }
+
+ private:
+ const art::mirror::Class* input_;
+ art::mirror::Class* output_;
+ };
+ WeakGlobalUpdate weak_global_update(input, output);
+ java_vm->SweepJniWeakGlobals(&weak_global_update);
+ }
+
+ void FixupLocalReferenceTables(art::Thread* self,
+ art::mirror::Class* input,
+ art::mirror::Class* output)
+ REQUIRES(art::Locks::mutator_lock_) {
+ class LocalUpdate {
+ public:
+ LocalUpdate(const art::mirror::Class* root_input, art::mirror::Class* root_output)
+ : input_(root_input), output_(root_output) {}
+
+ static void Callback(art::Thread* t, void* arg) REQUIRES(art::Locks::mutator_lock_) {
+ LocalUpdate* local = reinterpret_cast<LocalUpdate*>(arg);
+
+ // Fix up the local table with a root visitor.
+ RootUpdater local_update(local->input_, local->output_);
+ t->GetJniEnv()->locals.VisitRoots(
+ &local_update, art::RootInfo(art::kRootJNILocal, t->GetThreadId()));
+ }
+
+ private:
+ const art::mirror::Class* input_;
+ art::mirror::Class* output_;
+ };
+ LocalUpdate local_upd(input, output);
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Runtime::Current()->GetThreadList()->ForEach(LocalUpdate::Callback, &local_upd);
+ }
+
+ void FixupHeap(art::mirror::Class* input, art::mirror::Class* output)
+ REQUIRES(art::Locks::mutator_lock_) {
+ class HeapFixupVisitor {
+ public:
+ HeapFixupVisitor(const art::mirror::Class* root_input, art::mirror::Class* root_output)
+ : input_(root_input), output_(root_output) {}
+
+ void operator()(art::mirror::Object* src,
+ art::MemberOffset field_offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::mirror::HeapReference<art::mirror::Object>* trg =
+ src->GetFieldObjectReferenceAddr(field_offset);
+ if (trg->AsMirrorPtr() == input_) {
+ DCHECK_NE(field_offset.Uint32Value(), 0u); // This shouldn't be the class field of
+ // an object.
+ trg->Assign(output_);
+ }
+ }
+
+ void operator()(art::ObjPtr<art::mirror::Class> klass ATTRIBUTE_UNUSED,
+ art::ObjPtr<art::mirror::Reference> reference) const
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::mirror::Object* val = reference->GetReferent();
+ if (val == input_) {
+ reference->SetReferent<false>(output_);
+ }
+ }
+
+ void VisitRoot(art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {
+ LOG(FATAL) << "Unreachable";
+ }
+
+ void VisitRootIfNonNull(
+ art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED) const {
+ LOG(FATAL) << "Unreachable";
+ }
+
+ static void AllObjectsCallback(art::mirror::Object* obj, void* arg)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ HeapFixupVisitor* hfv = reinterpret_cast<HeapFixupVisitor*>(arg);
+
+ // Visit references, not native roots.
+ obj->VisitReferences<false>(*hfv, *hfv);
+ }
+
+ private:
+ const art::mirror::Class* input_;
+ art::mirror::Class* output_;
+ };
+ HeapFixupVisitor hfv(input, output);
+ art::Runtime::Current()->GetHeap()->VisitObjectsPaused(HeapFixupVisitor::AllObjectsCallback,
+ &hfv);
}
// A set of all the temp classes we have handed out. We have to fix up references to these.
diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc
index afec0bfac0..d05f579407 100644
--- a/runtime/openjdkjvmti/ti_class_loader.cc
+++ b/runtime/openjdkjvmti/ti_class_loader.cc
@@ -119,11 +119,11 @@ art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie(
art::Handle<art::mirror::LongArray> cookie,
const art::DexFile* dex_file) {
art::StackHandleScope<1> hs(self);
- CHECK(cookie.Get() != nullptr);
+ CHECK(cookie != nullptr);
CHECK_GE(cookie->GetLength(), 1);
art::Handle<art::mirror::LongArray> new_cookie(
hs.NewHandle(art::mirror::LongArray::Alloc(self, cookie->GetLength() + 1)));
- if (new_cookie.Get() == nullptr) {
+ if (new_cookie == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
@@ -183,13 +183,13 @@ art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
// Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative)
art::Handle<art::mirror::Object> path_list(
hs.NewHandle(path_list_field->GetObject(loader.Get())));
- CHECK(path_list.Get() != nullptr);
+ CHECK(path_list != nullptr);
CHECK(!self->IsExceptionPending());
art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
dex_path_list_element_field->GetObject(path_list.Get())->
AsObjectArray<art::mirror::Object>()));
CHECK(!self->IsExceptionPending());
- CHECK(dex_elements_list.Get() != nullptr);
+ CHECK(dex_elements_list != nullptr);
size_t num_elements = dex_elements_list->GetLength();
// Iterate over the DexPathList$Element to find the right one
for (size_t i = 0; i < num_elements; i++) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index f0c0dbcbfc..f01acc11aa 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -38,12 +38,17 @@
#include "art_jvmti.h"
#include "base/array_slice.h"
#include "base/logging.h"
+#include "debugger.h"
#include "dex_file.h"
#include "dex_file_types.h"
#include "events-inl.h"
#include "gc/allocation_listener.h"
#include "gc/heap.h"
#include "instrumentation.h"
+#include "jdwp/jdwp.h"
+#include "jdwp/jdwp_constants.h"
+#include "jdwp/jdwp_event.h"
+#include "jdwp/object_registry.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jni_env_ext-inl.h"
@@ -63,6 +68,66 @@ namespace openjdkjvmti {
using android::base::StringPrintf;
+// A helper that fills in a classes obsolete_methods_ and obsolete_dex_caches_ classExt fields as
+// they are created. This ensures that we can always call any method of an obsolete ArtMethod object
+// almost as soon as they are created since the GetObsoleteDexCache method will succeed.
+class ObsoleteMap {
+ public:
+ art::ArtMethod* FindObsoleteVersion(art::ArtMethod* original)
+ REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
+ auto method_pair = id_map_.find(original);
+ if (method_pair != id_map_.end()) {
+ art::ArtMethod* res = obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
+ method_pair->second, art::kRuntimePointerSize);
+ DCHECK(res != nullptr);
+ DCHECK_EQ(original, res->GetNonObsoleteMethod());
+ return res;
+ } else {
+ return nullptr;
+ }
+ }
+
+ void RecordObsolete(art::ArtMethod* original, art::ArtMethod* obsolete)
+ REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
+ DCHECK(original != nullptr);
+ DCHECK(obsolete != nullptr);
+ int32_t slot = next_free_slot_++;
+ DCHECK_LT(slot, obsolete_methods_->GetLength());
+ DCHECK(nullptr ==
+ obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(slot, art::kRuntimePointerSize));
+ DCHECK(nullptr == obsolete_dex_caches_->Get(slot));
+ obsolete_methods_->SetElementPtrSize(slot, obsolete, art::kRuntimePointerSize);
+ obsolete_dex_caches_->Set(slot, original_dex_cache_);
+ id_map_.insert({original, slot});
+ }
+
+ ObsoleteMap(art::ObjPtr<art::mirror::PointerArray> obsolete_methods,
+ art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches,
+ art::ObjPtr<art::mirror::DexCache> original_dex_cache)
+ : next_free_slot_(0),
+ obsolete_methods_(obsolete_methods),
+ obsolete_dex_caches_(obsolete_dex_caches),
+ original_dex_cache_(original_dex_cache) {
+ // Figure out where the first unused slot in the obsolete_methods_ array is.
+ while (obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(
+ next_free_slot_, art::kRuntimePointerSize) != nullptr) {
+ DCHECK(obsolete_dex_caches_->Get(next_free_slot_) != nullptr);
+ next_free_slot_++;
+ }
+ // Sanity check that the same slot in obsolete_dex_caches_ is free.
+ DCHECK(obsolete_dex_caches_->Get(next_free_slot_) == nullptr);
+ }
+
+ private:
+ int32_t next_free_slot_;
+ std::unordered_map<art::ArtMethod*, int32_t> id_map_;
+ // Pointers to the fields in mirror::ClassExt. These can be held as ObjPtr since this is only used
+ // when we have an exclusive mutator_lock_ (i.e. all threads are suspended).
+ art::ObjPtr<art::mirror::PointerArray> obsolete_methods_;
+ art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches_;
+ art::ObjPtr<art::mirror::DexCache> original_dex_cache_;
+};
+
// This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does
// some basic sanity checks that the obsolete method is sane.
class ObsoleteMethodStackVisitor : public art::StackVisitor {
@@ -71,7 +136,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+ ObsoleteMap* obsolete_maps)
: StackVisitor(thread,
/*context*/nullptr,
StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -89,7 +154,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps)
+ ObsoleteMap* obsolete_maps)
REQUIRES(art::Locks::mutator_lock_) {
ObsoleteMethodStackVisitor visitor(thread,
allocator,
@@ -99,6 +164,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
}
bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
+ art::ScopedAssertNoThreadSuspension snts("Fixing up the stack for obsolete methods.");
art::ArtMethod* old_method = GetMethod();
if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
// We cannot ensure that the right dex file is used in inlined frames so we don't support
@@ -108,9 +174,8 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// TODO We should really support redefining intrinsics.
// We don't support intrinsics so check for them here.
DCHECK(!old_method->IsIntrinsic());
- art::ArtMethod* new_obsolete_method = nullptr;
- auto obsolete_method_pair = obsolete_maps_->find(old_method);
- if (obsolete_method_pair == obsolete_maps_->end()) {
+ art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
+ if (new_obsolete_method == nullptr) {
// Create a new Obsolete Method and put it in the list.
art::Runtime* runtime = art::Runtime::Current();
art::ClassLinker* cl = runtime->GetClassLinker();
@@ -124,7 +189,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
new_obsolete_method->SetIsObsolete();
new_obsolete_method->SetDontCompile();
- obsolete_maps_->insert({old_method, new_obsolete_method});
+ obsolete_maps_->RecordObsolete(old_method, new_obsolete_method);
// Update JIT Data structures to point to the new method.
art::jit::Jit* jit = art::Runtime::Current()->GetJit();
if (jit != nullptr) {
@@ -132,8 +197,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// structures to keep track of the new obsolete method.
jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method);
}
- } else {
- new_obsolete_method = obsolete_method_pair->second;
}
DCHECK(new_obsolete_method != nullptr);
SetMethod(new_obsolete_method);
@@ -147,9 +210,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// The set of all methods which could be obsoleted.
const std::unordered_set<art::ArtMethod*>& obsoleted_methods_;
// A map from the original to the newly allocated obsolete method for frames on this thread. The
- // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
- // the redefined classes ClassExt by the caller.
- std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
+ // values in this map are added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
+ // the redefined classes ClassExt as it is filled.
+ ObsoleteMap* obsolete_maps_;
};
jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
@@ -426,11 +489,12 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi
}
struct CallbackCtx {
+ ObsoleteMap* obsolete_map;
art::LinearAlloc* allocator;
- std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map;
std::unordered_set<art::ArtMethod*> obsolete_methods;
- explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {}
+ explicit CallbackCtx(ObsoleteMap* map, art::LinearAlloc* alloc)
+ : obsolete_map(map), allocator(alloc) {}
};
void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS {
@@ -438,7 +502,7 @@ void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SA
ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t,
data->allocator,
data->obsolete_methods,
- &data->obsolete_map);
+ data->obsolete_map);
}
// This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is
@@ -449,9 +513,18 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C
art::mirror::ClassExt* ext = art_klass->GetExtData();
CHECK(ext->GetObsoleteMethods() != nullptr);
art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
- CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
+ // This holds pointers to the obsolete methods map fields which are updated as needed.
+ ObsoleteMap map(ext->GetObsoleteMethods(), ext->GetObsoleteDexCaches(), art_klass->GetDexCache());
+ CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
// Add all the declared methods to the map
for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
+ // It is possible to simply filter out some methods where they cannot really become obsolete,
+ // such as native methods and keep their original (possibly optimized) implementations. We don't
+ // do this, however, since we would need to mark these functions (still in the classes
+ // declared_methods array) as obsolete so we will find the correct dex file to get meta-data
+ // from (for example about stack-frame size). Furthermore we would be unable to get some useful
+ // error checking from the interpreter which ensure we don't try to start executing obsolete
+ // methods.
ctx.obsolete_methods.insert(&m);
// TODO Allow this or check in IsModifiableClass.
DCHECK(!m.IsIntrinsic());
@@ -461,36 +534,6 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C
art::ThreadList* list = art::Runtime::Current()->GetThreadList();
list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx));
}
- FillObsoleteMethodMap(art_klass, ctx.obsolete_map);
-}
-
-// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to
-// figure out their DexCaches.
-void Redefiner::ClassRedefinition::FillObsoleteMethodMap(
- art::mirror::Class* art_klass,
- const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) {
- int32_t index = 0;
- art::mirror::ClassExt* ext_data = art_klass->GetExtData();
- art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods();
- art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches =
- ext_data->GetObsoleteDexCaches();
- int32_t num_method_slots = obsolete_methods->GetLength();
- // Find the first empty index.
- for (; index < num_method_slots; index++) {
- if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>(
- index, art::kRuntimePointerSize) == nullptr) {
- break;
- }
- }
- // Make sure we have enough space.
- CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index));
- CHECK(obsolete_dex_caches->Get(index) == nullptr);
- // Fill in the map.
- for (auto& obs : obsoletes) {
- obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize);
- obsolete_dex_caches->Set(index, art_klass->GetDexCache());
- index++;
- }
}
// Try and get the declared method. First try to get a virtual method then a direct method if that's
@@ -934,7 +977,7 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(
ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader)));
holder->SetJavaDexFile(klass_index, dex_file_obj.Get());
- if (dex_file_obj.Get() == nullptr) {
+ if (dex_file_obj == nullptr) {
// TODO Better error msg.
RecordFailure(ERR(INTERNAL), "Unable to find dex file!");
return false;
@@ -966,6 +1009,23 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
return true;
}
+void Redefiner::ClassRedefinition::UnregisterBreakpoints() {
+ DCHECK(art::Dbg::IsDebuggerActive());
+ art::JDWP::JdwpState* state = art::Dbg::GetJdwpState();
+ if (state != nullptr) {
+ state->UnregisterLocationEventsOnClass(GetMirrorClass());
+ }
+}
+
+void Redefiner::UnregisterAllBreakpoints() {
+ if (LIKELY(!art::Dbg::IsDebuggerActive())) {
+ return;
+ }
+ for (Redefiner::ClassRedefinition& redef : redefinitions_) {
+ redef.UnregisterBreakpoints();
+ }
+}
+
bool Redefiner::CheckAllRedefinitionAreValid() {
for (Redefiner::ClassRedefinition& redef : redefinitions_) {
if (!redef.CheckRedefinitionIsValid()) {
@@ -1044,6 +1104,7 @@ jvmtiError Redefiner::Run() {
// cleaned up by the GC eventually.
return result_;
}
+ // At this point we can no longer fail without corrupting the runtime state.
int32_t counter = 0;
for (Redefiner::ClassRedefinition& redef : redefinitions_) {
if (holder.GetSourceClassLoader(counter) == nullptr) {
@@ -1051,6 +1112,7 @@ jvmtiError Redefiner::Run() {
}
counter++;
}
+ UnregisterAllBreakpoints();
// Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done
// allocating so no deadlocks.
art::gc::Heap* heap = runtime_->GetHeap();
@@ -1083,9 +1145,7 @@ jvmtiError Redefiner::Run() {
holder.GetOriginalDexFileBytes(counter));
counter++;
}
- // TODO Verify the new Class.
// TODO Shrink the obsolete method maps if possible?
- // TODO find appropriate class loader.
// TODO Put this into a scoped thing.
runtime_->GetThreadList()->ResumeAll();
// Get back shared mutator lock as expected for return.
@@ -1190,13 +1250,13 @@ bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() {
art::StackHandleScope<2> hs(driver_->self_);
art::Handle<art::mirror::Class> klass(hs.NewHandle(
driver_->self_->DecodeJObject(klass_)->AsClass()));
- if (klass.Get() == nullptr) {
+ if (klass == nullptr) {
RecordFailure(ERR(INVALID_CLASS), "Unable to decode class argument!");
return false;
}
// Allocate the classExt
art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(driver_->self_)));
- if (ext.Get() == nullptr) {
+ if (ext == nullptr) {
// No memory. Clear exception (it's not useful) and return error.
// TODO This doesn't need to be fatal. We could just not support obsolete methods after hitting
// this case.
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 421d22ef4c..65ee2912e2 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -155,12 +155,6 @@ class Redefiner {
void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
REQUIRES(art::Locks::mutator_lock_);
- void FillObsoleteMethodMap(
- art::mirror::Class* art_klass,
- const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
- REQUIRES(art::Locks::mutator_lock_);
-
-
// Checks that the dex file contains only the single expected class and that the top-level class
// data has not been modified in an incompatible manner.
bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
@@ -207,6 +201,8 @@ class Redefiner {
void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
private:
Redefiner* driver_;
jclass klass_;
@@ -250,6 +246,7 @@ class Redefiner {
bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void UnregisterAllBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
void RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg);
void RecordFailure(jvmtiError result, const std::string& error_msg) {
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
index e63ce6576a..142387433e 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.cc
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -155,7 +155,7 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env,
static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thread_group,
art::ObjPtr<art::mirror::Object> peer)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- CHECK(desired_thread_group.Get() != nullptr);
+ CHECK(desired_thread_group != nullptr);
art::ArtField* thread_group_field =
art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
@@ -167,7 +167,7 @@ static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thre
static void GetThreads(art::Handle<art::mirror::Object> thread_group,
std::vector<art::ObjPtr<art::mirror::Object>>* thread_peers)
REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) {
- CHECK(thread_group.Get() != nullptr);
+ CHECK(thread_group != nullptr);
art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_);
for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) {
@@ -187,7 +187,7 @@ static void GetThreads(art::Handle<art::mirror::Object> thread_group,
static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group,
std::vector<art::ObjPtr<art::mirror::Object>>* thread_groups)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
- CHECK(thread_group.Get() != nullptr);
+ CHECK(thread_group != nullptr);
// Get the ThreadGroup[] "groups" out of this thread group...
art::ArtField* groups_field =
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 1292a819a3..5748475163 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -114,8 +114,8 @@ TEST_F(ProxyTest, ProxyClassHelper) {
class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
Handle<mirror::Class> J(hs.NewHandle(
class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader)));
- ASSERT_TRUE(I.Get() != nullptr);
- ASSERT_TRUE(J.Get() != nullptr);
+ ASSERT_TRUE(I != nullptr);
+ ASSERT_TRUE(J != nullptr);
std::vector<mirror::Class*> interfaces;
interfaces.push_back(I.Get());
@@ -123,7 +123,7 @@ TEST_F(ProxyTest, ProxyClassHelper) {
Handle<mirror::Class> proxy_class(hs.NewHandle(
GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)));
interfaces.clear(); // Don't least possibly stale objects in the array as good practice.
- ASSERT_TRUE(proxy_class.Get() != nullptr);
+ ASSERT_TRUE(proxy_class != nullptr);
ASSERT_TRUE(proxy_class->IsProxyClass());
ASSERT_TRUE(proxy_class->IsInitialized());
@@ -148,8 +148,8 @@ TEST_F(ProxyTest, ProxyFieldHelper) {
class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader)));
Handle<mirror::Class> J(hs.NewHandle(
class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader)));
- ASSERT_TRUE(I.Get() != nullptr);
- ASSERT_TRUE(J.Get() != nullptr);
+ ASSERT_TRUE(I != nullptr);
+ ASSERT_TRUE(J != nullptr);
Handle<mirror::Class> proxyClass;
{
@@ -159,7 +159,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) {
proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces));
}
- ASSERT_TRUE(proxyClass.Get() != nullptr);
+ ASSERT_TRUE(proxyClass != nullptr);
ASSERT_TRUE(proxyClass->IsProxyClass());
ASSERT_TRUE(proxyClass->IsInitialized());
@@ -171,10 +171,10 @@ TEST_F(ProxyTest, ProxyFieldHelper) {
Handle<mirror::Class> interfacesFieldClass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Class;")));
- ASSERT_TRUE(interfacesFieldClass.Get() != nullptr);
+ ASSERT_TRUE(interfacesFieldClass != nullptr);
Handle<mirror::Class> throwsFieldClass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Class;")));
- ASSERT_TRUE(throwsFieldClass.Get() != nullptr);
+ ASSERT_TRUE(throwsFieldClass != nullptr);
// Test "Class[] interfaces" field.
ArtField* field = &static_fields->At(0);
@@ -208,10 +208,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) {
proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces));
}
- ASSERT_TRUE(proxyClass0.Get() != nullptr);
+ ASSERT_TRUE(proxyClass0 != nullptr);
ASSERT_TRUE(proxyClass0->IsProxyClass());
ASSERT_TRUE(proxyClass0->IsInitialized());
- ASSERT_TRUE(proxyClass1.Get() != nullptr);
+ ASSERT_TRUE(proxyClass1 != nullptr);
ASSERT_TRUE(proxyClass1->IsProxyClass());
ASSERT_TRUE(proxyClass1->IsInitialized());
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index 9523e92d7c..4ccfb6d83b 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -48,12 +48,12 @@ static mirror::Object* CreateWeakReference(mirror::Object* referent)
class_linker->FindClass(self,
"Ljava/lang/ref/WeakReference;",
ScopedNullHandle<mirror::ClassLoader>())));
- CHECK(h_ref_class.Get() != nullptr);
+ CHECK(h_ref_class != nullptr);
CHECK(class_linker->EnsureInitialized(self, h_ref_class, true, true));
Handle<mirror::Object> h_ref_instance(scope.NewHandle<mirror::Object>(
h_ref_class->AllocObject(self)));
- CHECK(h_ref_instance.Get() != nullptr);
+ CHECK(h_ref_instance != nullptr);
ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod(
"<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index a2b4cb37a9..3c64d40720 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -231,8 +231,8 @@ class ArgArray {
hs.NewHandle<mirror::ObjectArray<mirror::Object>>(raw_args));
for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
arg.Assign(args->Get(args_offset));
- if (((shorty_[i] == 'L') && (arg.Get() != nullptr)) ||
- ((arg.Get() == nullptr && shorty_[i] != 'L'))) {
+ if (((shorty_[i] == 'L') && (arg != nullptr)) ||
+ ((arg == nullptr && shorty_[i] != 'L'))) {
// TODO: The method's parameter's type must have been previously resolved, yet
// we've seen cases where it's not b/34440020.
ObjPtr<mirror::Class> dst_class(
@@ -242,7 +242,7 @@ class ArgArray {
CHECK(self->IsExceptionPending());
return false;
}
- if (UNLIKELY(arg.Get() == nullptr || !arg->InstanceOf(dst_class))) {
+ if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
ThrowIllegalArgumentException(
StringPrintf("method %s argument %zd has type %s, got %s",
m->PrettyMethod(false).c_str(),
@@ -254,13 +254,13 @@ class ArgArray {
}
#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
- if (LIKELY(arg.Get() != nullptr && \
+ if (LIKELY(arg != nullptr && \
arg->GetClass()->DescriptorEquals(match_descriptor))) { \
ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
append(primitive_field-> get_fn(arg.Get()));
#define DO_ARG(match_descriptor, get_fn, append) \
- } else if (LIKELY(arg.Get() != nullptr && \
+ } else if (LIKELY(arg != nullptr && \
arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
append(primitive_field-> get_fn(arg.Get()));
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9609bee022..f8f3d766c0 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -95,6 +95,7 @@
#include "mirror/field.h"
#include "mirror/method.h"
#include "mirror/method_handle_impl.h"
+#include "mirror/method_handles_lookup.h"
#include "mirror/method_type.h"
#include "mirror/stack_trace_element.h"
#include "mirror/throwable.h"
@@ -1715,6 +1716,7 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) {
mirror::Field::VisitRoots(visitor);
mirror::MethodType::VisitRoots(visitor);
mirror::MethodHandleImpl::VisitRoots(visitor);
+ mirror::MethodHandlesLookup::VisitRoots(visitor);
mirror::EmulatedStackFrame::VisitRoots(visitor);
mirror::ClassExt::VisitRoots(visitor);
// Visit all the primitive array types classes.
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index f1e78b40e0..abe99e0d50 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -294,7 +294,7 @@ TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) {
const char* descriptor_y = "LY;";
Handle<mirror::Class> h_Y(
hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
- ASSERT_TRUE(h_Y.Get() != nullptr);
+ ASSERT_TRUE(h_Y != nullptr);
bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>",
"PreDefine:LX; <art-gtest-XandY.jar>",
diff --git a/runtime/stack.cc b/runtime/stack.cc
index d7ba1d75d8..51a24e4e01 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -874,9 +874,13 @@ void StackVisitor::WalkStack(bool include_transitions) {
CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee)
<< " Found: " << ArtMethod::PrettyMethod(GetMethod());
} else {
- CHECK_EQ(instrumentation_frame.method_, GetMethod())
- << "Expected: " << ArtMethod::PrettyMethod(instrumentation_frame.method_)
- << " Found: " << ArtMethod::PrettyMethod(GetMethod());
+ // Instrumentation generally doesn't distinguish between a method's obsolete and
+ // non-obsolete version.
+ CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(),
+ GetMethod()->GetNonObsoleteMethod())
+ << "Expected: "
+ << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod())
+ << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod());
}
if (num_frames_ != 0) {
// Check agreement of frame Ids only if num_frames_ is computed to avoid infinite
@@ -903,7 +907,7 @@ void StackVisitor::WalkStack(bool include_transitions) {
<< " native=" << method->IsNative()
<< std::noboolalpha
<< " entrypoints=" << method->GetEntryPointFromQuickCompiledCode()
- << "," << method->GetEntryPointFromJni()
+ << "," << (method->IsNative() ? method->GetEntryPointFromJni() : nullptr)
<< " next=" << *cur_quick_frame_;
}
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 61d6a5847c..f7a64026b7 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -747,6 +747,7 @@ class StackMapEncoding {
return total_bit_size_;
}
+ // Encode the encoding into the vector.
template<typename Vector>
void Encode(Vector* dest) const {
static_assert(alignof(StackMapEncoding) == 1, "Should not require alignment");
@@ -754,6 +755,7 @@ class StackMapEncoding {
dest->insert(dest->end(), ptr, ptr + sizeof(*this));
}
+ // Decode the encoding from a pointer, updates the pointer.
void Decode(const uint8_t** ptr) {
*this = *reinterpret_cast<const StackMapEncoding*>(*ptr);
*ptr += sizeof(*this);
@@ -924,6 +926,7 @@ class InlineInfoEncoding {
void Dump(VariableIndentationOutputStream* vios) const;
+ // Encode the encoding into the vector.
template<typename Vector>
void Encode(Vector* dest) const {
static_assert(alignof(InlineInfoEncoding) == 1, "Should not require alignment");
@@ -931,6 +934,7 @@ class InlineInfoEncoding {
dest->insert(dest->end(), ptr, ptr + sizeof(*this));
}
+ // Decode the encoding from a pointer, updates the pointer.
void Decode(const uint8_t** ptr) {
*this = *reinterpret_cast<const InlineInfoEncoding*>(*ptr);
*ptr += sizeof(*this);
@@ -1171,6 +1175,7 @@ struct CodeInfoEncoding {
ComputeTableOffsets();
}
+ // Compress is not const since it calculates cache_header_size. This is used by PrepareForFillIn.
template<typename Vector>
void Compress(Vector* dest) {
dex_register_map.Encode(dest);
@@ -1210,9 +1215,9 @@ struct CodeInfoEncoding {
private:
// Computed fields (not serialized).
- // Header size in bytes.
+ // Header size in bytes, cached to avoid needing to re-decoding the encoding in HeaderSize.
uint32_t cache_header_size = kInvalidSize;
- // Non header size in bytes.
+ // Non header size in bytes, cached to avoid needing to re-decoding the encoding in NonHeaderSize.
uint32_t cache_non_header_size = kInvalidSize;
};
@@ -1221,7 +1226,13 @@ struct CodeInfoEncoding {
* The information is of the form:
*
* [CodeInfoEncoding, DexRegisterMap+, DexLocationCatalog+, StackMap+, RegisterMask+, StackMask+,
- * DexRegisterMap+, InlineInfo*]
+ * InlineInfo*]
+ *
+ * where CodeInfoEncoding is of the form:
+ *
+ * [ByteSizedTable(dex_register_map), ByteSizedTable(location_catalog),
+ * BitEncodingTable<StackMapEncoding>, BitEncodingTable<BitRegionEncoding>,
+ * BitEncodingTable<BitRegionEncoding>, BitEncodingTable<InlineInfoEncoding>]
*/
class CodeInfo {
public:
@@ -1331,7 +1342,9 @@ class CodeInfo {
}
InlineInfo GetInlineInfo(size_t index, const CodeInfoEncoding& encoding) const {
- // Since we do not know the depth, we just return the whole remaining map.
+ // Since we do not know the depth, we just return the whole remaining map. The caller may
+ // access the inline info for arbitrary depths. To return the precise inline info we would need
+ // to count the depth before returning.
// TODO: Clean this up.
const size_t bit_offset = encoding.inline_info.bit_offset +
index * encoding.inline_info.encoding.BitSize();
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index c92305f373..8d946262e8 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -80,7 +80,34 @@ inline void Thread::CheckSuspend() {
}
}
-inline void Thread::CheckEmptyCheckpoint() {
+inline void Thread::CheckEmptyCheckpointFromWeakRefAccess(BaseMutex* cond_var_mutex) {
+ Thread* self = Thread::Current();
+ DCHECK_EQ(self, this);
+ for (;;) {
+ if (ReadFlag(kEmptyCheckpointRequest)) {
+ RunEmptyCheckpoint();
+ // Check we hold only an expected mutex when accessing weak ref.
+ if (kIsDebugBuild) {
+ for (int i = kLockLevelCount - 1; i >= 0; --i) {
+ BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
+ if (held_mutex != nullptr &&
+ held_mutex != Locks::mutator_lock_ &&
+ held_mutex != cond_var_mutex) {
+ std::vector<BaseMutex*>& expected_mutexes = Locks::expected_mutexes_on_weak_ref_access_;
+ CHECK(std::find(expected_mutexes.begin(), expected_mutexes.end(), held_mutex) !=
+ expected_mutexes.end())
+ << "Holding unexpected mutex " << held_mutex->GetName()
+ << " when accessing weak ref";
+ }
+ }
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+inline void Thread::CheckEmptyCheckpointFromMutex() {
DCHECK_EQ(Thread::Current(), this);
for (;;) {
if (ReadFlag(kEmptyCheckpointRequest)) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index eba8975754..7ee0cd1d3f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -874,7 +874,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group)
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName()));
- if (peer_thread_name.Get() == nullptr) {
+ if (peer_thread_name == nullptr) {
// The Thread constructor should have set the Thread.name to a
// non-null value. However, because we can run without code
// available (in the compiler, in tests), we manually assign the
@@ -887,7 +887,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group)
peer_thread_name.Assign(GetThreadName());
}
// 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null.
- if (peer_thread_name.Get() != nullptr) {
+ if (peer_thread_name != nullptr) {
SetThreadName(peer_thread_name->ToModifiedUtf8().c_str());
}
}
@@ -2284,7 +2284,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor {
Handle<mirror::ObjectArray<mirror::Object>> trace(
hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(hs.Self(), array_class, depth + 1)));
- if (trace.Get() == nullptr) {
+ if (trace == nullptr) {
// Acquire uninterruptible_ in all paths.
self_->StartAssertNoThreadSuspension("Building internal stack trace");
self_->AssertPendingOOMException();
@@ -2479,14 +2479,14 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(
std::string class_name(PrettyDescriptor(descriptor));
class_name_object.Assign(
mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str()));
- if (class_name_object.Get() == nullptr) {
+ if (class_name_object == nullptr) {
soa.Self()->AssertPendingOOMException();
return nullptr;
}
const char* source_file = method->GetDeclaringClassSourceFile();
if (source_file != nullptr) {
source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
- if (source_name_object.Get() == nullptr) {
+ if (source_name_object == nullptr) {
soa.Self()->AssertPendingOOMException();
return nullptr;
}
@@ -2496,7 +2496,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(
CHECK(method_name != nullptr);
Handle<mirror::String> method_name_object(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name)));
- if (method_name_object.Get() == nullptr) {
+ if (method_name_object == nullptr) {
return nullptr;
}
ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(),
@@ -2554,7 +2554,7 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor,
auto* cl = runtime->GetClassLinker();
Handle<mirror::Class> exception_class(
hs.NewHandle(cl->FindClass(this, exception_class_descriptor, class_loader)));
- if (UNLIKELY(exception_class.Get() == nullptr)) {
+ if (UNLIKELY(exception_class == nullptr)) {
CHECK(IsExceptionPending());
LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor);
return;
@@ -2570,7 +2570,7 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor,
hs.NewHandle(ObjPtr<mirror::Throwable>::DownCast(exception_class->AllocObject(this))));
// If we couldn't allocate the exception, throw the pre-allocated out of memory exception.
- if (exception.Get() == nullptr) {
+ if (exception == nullptr) {
SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
return;
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 5dd6ae1dc2..e500e0b9e4 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -176,7 +176,8 @@ class Thread {
void CheckSuspend() REQUIRES_SHARED(Locks::mutator_lock_);
// Process a pending empty checkpoint if pending.
- void CheckEmptyCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_);
+ void CheckEmptyCheckpointFromWeakRefAccess(BaseMutex* cond_var_mutex);
+ void CheckEmptyCheckpointFromMutex();
static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts,
ObjPtr<mirror::Object> thread_peer)
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index df8acc37a2..caed36936a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -379,13 +379,15 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback
return count;
}
-size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids) {
+void ThreadList::RunEmptyCheckpoint() {
Thread* self = Thread::Current();
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
Locks::thread_suspend_count_lock_->AssertNotHeld(self);
-
+ std::vector<uint32_t> runnable_thread_ids;
size_t count = 0;
+ Barrier* barrier = empty_checkpoint_barrier_.get();
+ barrier->Init(self, 0);
{
MutexLock mu(self, *Locks::thread_list_lock_);
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
@@ -415,8 +417,72 @@ size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids
// checkpoint request. Otherwise we will hang as they are blocking in the kRunnable state.
Runtime::Current()->GetHeap()->GetReferenceProcessor()->BroadcastForSlowPath(self);
Runtime::Current()->BroadcastForNewSystemWeaks(/*broadcast_for_checkpoint*/true);
-
- return count;
+ {
+ ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+ uint64_t total_wait_time = 0;
+ bool first_iter = true;
+ while (true) {
+ // Wake up the runnable threads blocked on the mutexes that another thread, which is blocked
+ // on a weak ref access, holds (indirectly blocking for weak ref access through another thread
+ // and a mutex.) This needs to be done periodically because the thread may be preempted
+ // between the CheckEmptyCheckpointFromMutex call and the subsequent futex wait in
+ // Mutex::ExclusiveLock, etc. when the wakeup via WakeupToRespondToEmptyCheckpoint
+ // arrives. This could cause a *very rare* deadlock, if not repeated. Most of the cases are
+ // handled in the first iteration.
+ for (BaseMutex* mutex : Locks::expected_mutexes_on_weak_ref_access_) {
+ mutex->WakeupToRespondToEmptyCheckpoint();
+ }
+ static constexpr uint64_t kEmptyCheckpointPeriodicTimeoutMs = 100; // 100ms
+ static constexpr uint64_t kEmptyCheckpointTotalTimeoutMs = 600 * 1000; // 10 minutes.
+ size_t barrier_count = first_iter ? count : 0;
+ first_iter = false; // Don't add to the barrier count from the second iteration on.
+ bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointPeriodicTimeoutMs);
+ if (!timed_out) {
+ break; // Success
+ }
+ // This is a very rare case.
+ total_wait_time += kEmptyCheckpointPeriodicTimeoutMs;
+ if (kIsDebugBuild && total_wait_time > kEmptyCheckpointTotalTimeoutMs) {
+ std::ostringstream ss;
+ ss << "Empty checkpoint timeout\n";
+ ss << "Barrier count " << barrier->GetCount(self) << "\n";
+ ss << "Runnable thread IDs";
+ for (uint32_t tid : runnable_thread_ids) {
+ ss << " " << tid;
+ }
+ ss << "\n";
+ Locks::mutator_lock_->Dump(ss);
+ ss << "\n";
+ LOG(FATAL_WITHOUT_ABORT) << ss.str();
+ // Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
+ // Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
+ {
+ ScopedObjectAccess soa(self);
+ MutexLock mu1(self, *Locks::thread_list_lock_);
+ for (Thread* thread : GetList()) {
+ uint32_t tid = thread->GetThreadId();
+ bool is_in_runnable_thread_ids =
+ std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) !=
+ runnable_thread_ids.end();
+ if (is_in_runnable_thread_ids &&
+ thread->ReadFlag(kEmptyCheckpointRequest)) {
+ // Found a runnable thread that hasn't responded to the empty checkpoint request.
+ // Assume it's stuck and safe to dump its stack.
+ thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT),
+ /*dump_native_stack*/ true,
+ /*backtrace_map*/ nullptr,
+ /*force_dump_stack*/ true);
+ }
+ }
+ }
+ LOG(FATAL_WITHOUT_ABORT)
+ << "Dumped runnable threads that haven't responded to empty checkpoint.";
+ // Now use ThreadList::Dump() to dump more threads, noting it may get stuck.
+ Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+ LOG(FATAL) << "Dumped all threads.";
+ }
+ }
+ }
}
// Request that a checkpoint function be run on all active (non-suspended)
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index b60fca1fdc..70917eb0f7 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -109,9 +109,7 @@ class ThreadList {
// in-flight mutator heap access (eg. a read barrier.) Runnable threads will respond by
// decrementing the empty checkpoint barrier count. This works even when the weak ref access is
// disabled. Only one concurrent use is currently supported.
- // In debug build, runnable_thread_ids will be populated with the thread IDS of the runnable
- // thread to wait for.
- size_t RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids)
+ void RunEmptyCheckpoint()
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function)
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index a43c967092..97c1228038 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -35,7 +35,7 @@ class TransactionTest : public CommonRuntimeTest {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
// Load and initialize java.lang.ExceptionInInitializerError and the exception class used
// to abort transaction so they can be thrown during class initialization if the transaction
@@ -43,26 +43,26 @@ class TransactionTest : public CommonRuntimeTest {
MutableHandle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
"Ljava/lang/ExceptionInInitializerError;")));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
h_klass.Assign(class_linker_->FindSystemClass(soa.Self(),
Transaction::kAbortExceptionSignature));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
// Load and verify utility class.
h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$AbortHelperClass;",
class_loader));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
// Load and verify tested class.
h_klass.Assign(class_linker_->FindClass(soa.Self(), tested_class_signature, class_loader));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
@@ -95,12 +95,12 @@ TEST_F(TransactionTest, Object_class) {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
- ASSERT_TRUE(h_obj.Get() != nullptr);
+ ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
Runtime::Current()->ExitTransactionMode();
@@ -115,9 +115,9 @@ TEST_F(TransactionTest, Object_monitor) {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
- ASSERT_TRUE(h_obj.Get() != nullptr);
+ ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Lock object's monitor outside the transaction.
@@ -144,7 +144,7 @@ TEST_F(TransactionTest, Array_length) {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
constexpr int32_t kArraySize = 2;
@@ -157,7 +157,7 @@ TEST_F(TransactionTest, Array_length) {
mirror::Array::Alloc<true>(soa.Self(), h_klass.Get(), kArraySize,
h_klass->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
- ASSERT_TRUE(h_obj.Get() != nullptr);
+ ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
Runtime::Current()->ExitTransactionMode();
@@ -172,11 +172,11 @@ TEST_F(TransactionTest, StaticFieldsTest) {
StackHandleScope<4> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
@@ -232,9 +232,9 @@ TEST_F(TransactionTest, StaticFieldsTest) {
// Create a java.lang.Object instance to set objectField.
Handle<mirror::Class> object_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- ASSERT_TRUE(object_klass.Get() != nullptr);
+ ASSERT_TRUE(object_klass != nullptr);
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
- ASSERT_TRUE(h_obj.Get() != nullptr);
+ ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Modify fields inside transaction then rollback changes.
@@ -270,11 +270,11 @@ TEST_F(TransactionTest, InstanceFieldsTest) {
StackHandleScope<5> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
@@ -282,7 +282,7 @@ TEST_F(TransactionTest, InstanceFieldsTest) {
// Allocate an InstanceFieldTest object.
Handle<mirror::Object> h_instance(hs.NewHandle(h_klass->AllocObject(soa.Self())));
- ASSERT_TRUE(h_instance.Get() != nullptr);
+ ASSERT_TRUE(h_instance != nullptr);
// Lookup fields.
ArtField* booleanField = h_klass->FindDeclaredInstanceField("booleanField", "Z");
@@ -334,9 +334,9 @@ TEST_F(TransactionTest, InstanceFieldsTest) {
// Create a java.lang.Object instance to set objectField.
Handle<mirror::Class> object_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- ASSERT_TRUE(object_klass.Get() != nullptr);
+ ASSERT_TRUE(object_klass != nullptr);
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
- ASSERT_TRUE(h_obj.Get() != nullptr);
+ ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Modify fields inside transaction then rollback changes.
@@ -372,11 +372,11 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) {
StackHandleScope<4> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(success);
ASSERT_TRUE(h_klass->IsInitialized());
@@ -451,9 +451,9 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) {
// Create a java.lang.Object instance to set objectField.
Handle<mirror::Class> object_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- ASSERT_TRUE(object_klass.Get() != nullptr);
+ ASSERT_TRUE(object_klass != nullptr);
Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self())));
- ASSERT_TRUE(h_obj.Get() != nullptr);
+ ASSERT_TRUE(h_obj != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
// Modify fields inside transaction then rollback changes.
@@ -489,15 +489,15 @@ TEST_F(TransactionTest, ResolveString) {
StackHandleScope<3> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$ResolveString;",
class_loader)));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(h_klass->GetDexCache()));
- ASSERT_TRUE(h_dex_cache.Get() != nullptr);
+ ASSERT_TRUE(h_dex_cache != nullptr);
const DexFile* const dex_file = h_dex_cache->GetDexFile();
ASSERT_TRUE(dex_file != nullptr);
@@ -538,12 +538,12 @@ TEST_F(TransactionTest, EmptyClass) {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;",
class_loader)));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
@@ -562,12 +562,12 @@ TEST_F(TransactionTest, StaticFieldClass) {
StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction"))));
- ASSERT_TRUE(class_loader.Get() != nullptr);
+ ASSERT_TRUE(class_loader != nullptr);
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;",
class_loader)));
- ASSERT_TRUE(h_klass.Get() != nullptr);
+ ASSERT_TRUE(h_klass != nullptr);
class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
diff --git a/runtime/utils.h b/runtime/utils.h
index 67438b5881..96e5bfa8ec 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -301,6 +301,30 @@ constexpr PointerSize ConvertToPointerSize(T any) {
}
}
+// Returns a type cast pointer if object pointed to is within the provided bounds.
+// Otherwise returns nullptr.
+template <typename T>
+inline static T BoundsCheckedCast(const void* pointer,
+ const void* lower,
+ const void* upper) {
+ const uint8_t* bound_begin = static_cast<const uint8_t*>(lower);
+ const uint8_t* bound_end = static_cast<const uint8_t*>(upper);
+ DCHECK(bound_begin <= bound_end);
+
+ T result = reinterpret_cast<T>(pointer);
+ const uint8_t* begin = static_cast<const uint8_t*>(pointer);
+ const uint8_t* end = begin + sizeof(*result);
+ if (begin < bound_begin || end > bound_end || begin > end) {
+ return nullptr;
+ }
+ return result;
+}
+
+template <typename T, size_t size>
+constexpr size_t ArrayCount(const T (&)[size]) {
+ return size;
+}
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index bd1b044dae..98658215f7 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -29,7 +29,8 @@
namespace art {
inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size,
- const DexFile::Header& header)
+ const DexFile::Header& header,
+ uint32_t num_call_sites)
: pointer_size_(pointer_size),
/* types_offset_ is always 0u, so it's constexpr */
methods_offset_(
@@ -40,12 +41,14 @@ inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size,
RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())),
method_types_offset_(
RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), MethodTypesAlignment())),
- size_(
- RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) {
+ call_sites_offset_(
+ RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_),
+ MethodTypesAlignment())),
+ size_(RoundUp(call_sites_offset_ + CallSitesSize(num_call_sites), Alignment())) {
}
inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file)
- : DexCacheArraysLayout(pointer_size, dex_file->GetHeader()) {
+ : DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) {
}
inline constexpr size_t DexCacheArraysLayout::Alignment() {
@@ -131,10 +134,18 @@ inline size_t DexCacheArraysLayout::MethodTypesSize(size_t num_elements) const {
inline size_t DexCacheArraysLayout::MethodTypesAlignment() const {
static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
- "alignof(MethodTypeDexCacheType) != 8");
+ "Expecting alignof(MethodTypeDexCacheType) == 8");
return alignof(mirror::MethodTypeDexCacheType);
}
+inline size_t DexCacheArraysLayout::CallSitesSize(size_t num_elements) const {
+ return ArraySize(GcRootAsPointerSize<mirror::CallSite>(), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::CallSitesAlignment() const {
+ return alignof(GcRoot<mirror::CallSite>);
+}
+
inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) {
return static_cast<size_t>(element_size) * idx;
}
diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
index 7d4b23a8dd..ed677ed3f4 100644
--- a/runtime/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -37,11 +37,14 @@ class DexCacheArraysLayout {
strings_offset_(0u),
fields_offset_(0u),
method_types_offset_(0u),
+ call_sites_offset_(0u),
size_(0u) {
}
// Construct a layout for a particular dex file header.
- DexCacheArraysLayout(PointerSize pointer_size, const DexFile::Header& header);
+ DexCacheArraysLayout(PointerSize pointer_size,
+ const DexFile::Header& header,
+ uint32_t num_call_sites);
// Construct a layout for a particular dex file.
DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file);
@@ -104,6 +107,14 @@ class DexCacheArraysLayout {
size_t MethodTypesAlignment() const;
+ size_t CallSitesOffset() const {
+ return call_sites_offset_;
+ }
+
+ size_t CallSitesSize(size_t num_elements) const;
+
+ size_t CallSitesAlignment() const;
+
private:
static constexpr size_t types_offset_ = 0u;
const PointerSize pointer_size_; // Must be first for construction initialization order.
@@ -111,6 +122,7 @@ class DexCacheArraysLayout {
const size_t strings_offset_;
const size_t fields_offset_;
const size_t method_types_offset_;
+ const size_t call_sites_offset_;
const size_t size_;
static size_t Alignment(PointerSize pointer_size);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 02f1e1bbfe..634bd47f05 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -408,4 +408,23 @@ TEST_F(UtilsTest, IsValidDescriptor) {
IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0])));
}
+TEST_F(UtilsTest, ArrayCount) {
+ int i[64];
+ EXPECT_EQ(ArrayCount(i), 64u);
+ char c[7];
+ EXPECT_EQ(ArrayCount(c), 7u);
+}
+
+TEST_F(UtilsTest, BoundsCheckedCast) {
+ char buffer[64];
+ const char* buffer_end = buffer + ArrayCount(buffer);
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
+ reinterpret_cast<const uint64_t*>(buffer));
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
+ reinterpret_cast<const uint64_t*>(buffer + 56));
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
+ EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
+}
+
} // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5f55f3fd29..16739fa3bc 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3114,6 +3114,44 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
just_set_result = true;
break;
}
+ case Instruction::INVOKE_CUSTOM:
+ case Instruction::INVOKE_CUSTOM_RANGE: {
+ // Verify registers based on method_type in the call site.
+ bool is_range = (inst->Opcode() == Instruction::INVOKE_CUSTOM_RANGE);
+
+ // Step 1. Check the call site that produces the method handle for invocation
+ const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ if (!CheckCallSite(call_site_idx)) {
+ DCHECK(HasFailures());
+ break;
+ }
+
+ // Step 2. Check the register arguments correspond to the expected arguments for the
+ // method handle produced by step 1. The dex file verifier has checked ranges for
+ // the first three arguments and CheckCallSite has checked the method handle type.
+ CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+ it.Next(); // Skip to name.
+ it.Next(); // Skip to method type of the method handle
+ const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx);
+ DexFileParameterIterator param_it(*dex_file_, proto_id);
+ // Treat method as static as it has yet to be determined.
+ VerifyInvocationArgsFromIterator(&param_it, inst, METHOD_STATIC, is_range, nullptr);
+ const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(proto_id);
+
+ // Step 3. Propagate return type information
+ const RegType& return_type =
+ reg_types_.FromDescriptor(GetClassLoader(), return_descriptor, false);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(this, return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+ }
+ just_set_result = true;
+ // TODO: Add compiler support for invoke-custom (b/35337872).
+ Fail(VERIFY_ERROR_FORCE_INTERPRETER);
+ break;
+ }
case Instruction::NEG_INT:
case Instruction::NOT_INT:
work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer());
@@ -3423,7 +3461,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
/* These should never appear during verification. */
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
- case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_FE ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
@@ -4094,6 +4132,116 @@ void MethodVerifier::VerifyInvocationArgsUnresolvedMethod(const Instruction* ins
VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, nullptr);
}
+bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) {
+ CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx));
+ // Check essential arguments are provided. The dex file verifier has verified indicies of the
+ // main values (method handle, name, method_type).
+ if (it.Size() < 3) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " has too few arguments: "
+ << it.Size() << "< 3";
+ return false;
+ }
+
+ // Get and check the first argument: the method handle.
+ uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i);
+ it.Next();
+ const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
+ if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " argument 0 method handle type is not InvokeStatic";
+ return false;
+ }
+
+ // Skip the second argument, the name to resolve, as checked by the
+ // dex file verifier.
+ it.Next();
+
+ // Skip the third argument, the method type expected, as checked by
+ // the dex file verifier.
+ it.Next();
+
+ // Check the bootstrap method handle and remaining arguments.
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_);
+ uint32_t length;
+ const char* shorty = dex_file_->GetMethodShorty(method_id, &length);
+
+ if (it.Size() < length - 1) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " too few arguments for bootstrap method: "
+ << it.Size() << " < " << (length - 1);
+ return false;
+ }
+
+ // Check the return type and first 3 arguments are references
+ // (CallSite, Lookup, String, MethodType). If they are not of the
+ // expected types (or subtypes), it will trigger a
+ // WrongMethodTypeException during execution.
+ if (shorty[0] != 'L') {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " bootstrap return type is not a reference";
+ return false;
+ }
+
+ for (uint32_t i = 1; i < 4; ++i) {
+ if (shorty[i] != 'L') {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " bootstrap method argument " << (i - 1)
+ << " is not a reference";
+ return false;
+ }
+ }
+
+ // Check the optional arguments.
+ for (uint32_t i = 4; i < length; ++i, it.Next()) {
+ bool match = false;
+ switch (it.GetValueType()) {
+ case EncodedArrayValueIterator::ValueType::kBoolean:
+ case EncodedArrayValueIterator::ValueType::kByte:
+ case EncodedArrayValueIterator::ValueType::kShort:
+ case EncodedArrayValueIterator::ValueType::kChar:
+ case EncodedArrayValueIterator::ValueType::kInt:
+ // These all fit within one register and encoders do not seem
+ // too exacting on the encoding type they use (ie using
+ // integer for all of these).
+ match = (strchr("ZBCSI", shorty[i]) != nullptr);
+ break;
+ case EncodedArrayValueIterator::ValueType::kLong:
+ match = ('J' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kFloat:
+ match = ('F' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kDouble:
+ match = ('D' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kMethodType:
+ case EncodedArrayValueIterator::ValueType::kMethodHandle:
+ case EncodedArrayValueIterator::ValueType::kString:
+ case EncodedArrayValueIterator::ValueType::kType:
+ case EncodedArrayValueIterator::ValueType::kNull:
+ match = ('L' == shorty[i]);
+ break;
+ case EncodedArrayValueIterator::ValueType::kField:
+ case EncodedArrayValueIterator::ValueType::kMethod:
+ case EncodedArrayValueIterator::ValueType::kEnum:
+ case EncodedArrayValueIterator::ValueType::kArray:
+ case EncodedArrayValueIterator::ValueType::kAnnotation:
+ // Unreachable based on current EncodedArrayValueIterator::Next().
+ UNREACHABLE();
+ }
+
+ if (!match) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
+ << " bootstrap method argument " << (i - 1)
+ << " expected " << shorty[i]
+ << " got value type: " << it.GetValueType();
+ return false;
+ }
+ }
+ return true;
+}
+
class MethodParamListDescriptorIterator {
public:
explicit MethodParamListDescriptorIterator(ArtMethod* res_method) :
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index fa5a698423..7b67967c28 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -697,6 +697,11 @@ class MethodVerifier {
REQUIRES_SHARED(Locks::mutator_lock_);
/*
+ * Verify the arguments present for a call site. Returns "true" if all is well, "false" otherwise.
+ */
+ bool CheckCallSite(uint32_t call_site_idx);
+
+ /*
* Verify that the target instruction is not "move-exception". It's important that the only way
* to execute a move-exception is as the first instruction of an exception handler.
* Returns "true" if all is well, "false" if the target instruction is move-exception.
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 113160785c..000cf7c393 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -890,12 +890,12 @@ bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
source.Assign(
FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
- if (destination.Get() == nullptr) {
+ if (destination == nullptr) {
LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
return false;
}
- if (source.Get() == nullptr) {
+ if (source == nullptr) {
LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
return false;
}
@@ -925,7 +925,7 @@ bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
if (entry.IsResolved()) {
- if (cls.Get() == nullptr) {
+ if (cls == nullptr) {
LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
return false;
} else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
@@ -939,7 +939,7 @@ bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
<< std::dec;
return false;
}
- } else if (cls.Get() != nullptr) {
+ } else if (cls != nullptr) {
LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
return false;
}
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index cd79bb61f3..416873f4c3 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -23,10 +23,16 @@
#include <unistd.h>
#include <vector>
+#include "android-base/stringprintf.h"
#include "base/unix_file/fd_file.h"
namespace art {
+// Log file contents and mmap info when mapping entries directly.
+static constexpr const bool kDebugZipMapDirectly = false;
+
+using android::base::StringPrintf;
+
uint32_t ZipEntry::GetUncompressedLength() {
return zip_entry_->uncompressed_length;
}
@@ -35,6 +41,15 @@ uint32_t ZipEntry::GetCrc32() {
return zip_entry_->crc32;
}
+bool ZipEntry::IsUncompressed() {
+ return zip_entry_->method == kCompressStored;
+}
+
+bool ZipEntry::IsAlignedTo(size_t alignment) {
+ DCHECK(IsPowerOfTwo(alignment)) << alignment;
+ return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment));
+}
+
ZipEntry::~ZipEntry() {
delete zip_entry_;
}
@@ -73,6 +88,102 @@ MemMap* ZipEntry::ExtractToMemMap(const char* zip_filename, const char* entry_fi
return map.release();
}
+MemMap* ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
+ const int zip_fd = GetFileDescriptor(handle_);
+ const char* entry_filename = entry_name_.c_str();
+
+ // Should not happen since we don't have a memory ZipArchive constructor.
+ // However the underlying ZipArchive isn't required to have an FD,
+ // so check to be sure.
+ CHECK_GE(zip_fd, 0) <<
+ StringPrintf("Cannot map '%s' (in zip '%s') directly because the zip archive "
+ "is not file backed.",
+ entry_filename,
+ zip_filename);
+
+ if (!IsUncompressed()) {
+ *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because it is compressed.",
+ entry_filename,
+ zip_filename);
+ return nullptr;
+ } else if (zip_entry_->uncompressed_length != zip_entry_->compressed_length) {
+ *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because "
+ "entry has bad size (%u != %u).",
+ entry_filename,
+ zip_filename,
+ zip_entry_->uncompressed_length,
+ zip_entry_->compressed_length);
+ return nullptr;
+ }
+
+ std::string name(entry_filename);
+ name += " mapped directly in memory from ";
+ name += zip_filename;
+
+ const off_t offset = zip_entry_->offset;
+
+ if (kDebugZipMapDirectly) {
+ LOG(INFO) << "zip_archive: " << "make mmap of " << name << " @ offset = " << offset;
+ }
+
+ std::unique_ptr<MemMap> map(
+ MemMap::MapFileAtAddress(nullptr, // Expected pointer address
+ GetUncompressedLength(), // Byte count
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ zip_fd,
+ offset,
+ false, // Don't restrict allocation to lower4GB
+ false, // Doesn't overlap existing map (reuse=false)
+ name.c_str(),
+ /*out*/error_msg));
+
+ if (map == nullptr) {
+ DCHECK(!error_msg->empty());
+ }
+
+ if (kDebugZipMapDirectly) {
+ // Dump contents of file, same format as using this shell command:
+ // $> od -j <offset> -t x1 <zip_filename>
+ static constexpr const int kMaxDumpChars = 15;
+ lseek(zip_fd, 0, SEEK_SET);
+
+ int count = offset + kMaxDumpChars;
+
+ std::string tmp;
+ char buf;
+
+ // Dump file contents.
+ int i = 0;
+ while (read(zip_fd, &buf, 1) > 0 && i < count) {
+ tmp += StringPrintf("%3d ", (unsigned int)buf);
+ ++i;
+ }
+
+ LOG(INFO) << "map_fd raw bytes starting at 0";
+ LOG(INFO) << "" << tmp;
+ LOG(INFO) << "---------------------------";
+
+ // Dump map contents.
+ if (map != nullptr) {
+ tmp = "";
+
+ count = kMaxDumpChars;
+
+ uint8_t* begin = map->Begin();
+ for (i = 0; i < count; ++i) {
+ tmp += StringPrintf("%3d ", (unsigned int)begin[i]);
+ }
+
+ LOG(INFO) << "map address " << StringPrintf("%p", begin);
+ LOG(INFO) << "map first " << kMaxDumpChars << " chars:";
+ LOG(INFO) << tmp;
+ }
+ }
+
+ return map.release();
+}
+
static void SetCloseOnExec(int fd) {
// This dance is more portable than Linux's O_CLOEXEC open(2) flag.
int flags = fcntl(fd, F_GETFD);
@@ -129,7 +240,7 @@ ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const {
return nullptr;
}
- return new ZipEntry(handle_, zip_entry.release());
+ return new ZipEntry(handle_, zip_entry.release(), name);
}
ZipArchive::~ZipArchive() {
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index 42bf55cb3f..18584447e8 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -37,19 +37,35 @@ class MemMap;
class ZipEntry {
public:
bool ExtractToFile(File& file, std::string* error_msg);
+ // Extract this entry to anonymous memory (R/W).
+ // Returns null on failure and sets error_msg.
MemMap* ExtractToMemMap(const char* zip_filename, const char* entry_filename,
std::string* error_msg);
+ // Create a file-backed private (clean, R/W) memory mapping to this entry.
+ // 'zip_filename' is used for diagnostics only,
+ // the original file that the ZipArchive was open with is used
+ // for the mapping.
+ //
+ // Will only succeed if the entry is stored uncompressed.
+ // Returns null on failure and sets error_msg.
+ MemMap* MapDirectlyFromFile(const char* zip_filename, /*out*/std::string* error_msg);
virtual ~ZipEntry();
uint32_t GetUncompressedLength();
uint32_t GetCrc32();
+ bool IsUncompressed();
+ bool IsAlignedTo(size_t alignment);
+
private:
ZipEntry(ZipArchiveHandle handle,
- ::ZipEntry* zip_entry) : handle_(handle), zip_entry_(zip_entry) {}
+ ::ZipEntry* zip_entry,
+ const std::string& entry_name)
+ : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {}
ZipArchiveHandle handle_;
::ZipEntry* const zip_entry_;
+ std::string const entry_name_;
friend class ZipArchive;
DISALLOW_COPY_AND_ASSIGN(ZipEntry);
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index df0a3ddf48..5a43a4f23f 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -117,6 +117,9 @@ public class Main {
" " + $noinline$equals(s0_3, s0_1) +
" " + $noinline$equals(s0_3, s0_2) +
" " + $noinline$equals(s0_3, s0_3));
+
+ testEqualsConstString();
+ testConstStringEquals();
}
public static void testCompareToAndEquals() {
@@ -539,6 +542,266 @@ public class Main {
}
}
+ public static void testEqualsConstString() {
+ Assert.assertTrue($noinline$equalsConstString0(""));
+ Assert.assertFalse($noinline$equalsConstString0("1"));
+
+ Assert.assertTrue($noinline$equalsConstString7("0123456"));
+ Assert.assertFalse($noinline$equalsConstString7("012345"));
+ Assert.assertFalse($noinline$equalsConstString7("01234567"));
+ Assert.assertFalse($noinline$equalsConstString7("012345x"));
+ Assert.assertFalse($noinline$equalsConstString7("012345\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString14("01234567890123"));
+ Assert.assertFalse($noinline$equalsConstString14("0123456789012"));
+ Assert.assertFalse($noinline$equalsConstString14("012345678901234"));
+ Assert.assertFalse($noinline$equalsConstString14("0123456789012x"));
+ Assert.assertFalse($noinline$equalsConstString14("0123456789012\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString24("012345678901234567890123"));
+ Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012"));
+ Assert.assertFalse($noinline$equalsConstString24("0123456789012345678901234"));
+ Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012x"));
+ Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString29("01234567890123456789012345678"));
+ Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567"));
+ Assert.assertFalse($noinline$equalsConstString29("012345678901234567890123456789"));
+ Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567x"));
+ Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstString35("01234567890123456789012345678901234"));
+ Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123"));
+ Assert.assertFalse($noinline$equalsConstString35("012345678901234567890123456789012345"));
+ Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$equalsConstString35("0123456789012345678901234567890123\u0440"));
+
+ Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456"));
+
+ Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString14("01234567890123"));
+
+ Assert.assertTrue($noinline$equalsConstNonAsciiString24("\u044012345678901234567890123"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u0440123456789012345678901234"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString24("\012345678901234567890123"));
+
+ Assert.assertTrue(
+ $noinline$equalsConstNonAsciiString29("\u04401234567890123456789012345678"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString29("\u044012345678901234567890123456789"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString29("01234567890123456789012345678"));
+
+ Assert.assertTrue(
+ $noinline$equalsConstNonAsciiString35("\u04401234567890123456789012345678901234"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("\u044012345678901234567890123456789012345"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$equalsConstNonAsciiString35("01234567890123456789012345678901234"));
+ }
+
+ public static void testConstStringEquals() {
+ Assert.assertTrue($noinline$constString0Equals(""));
+ Assert.assertFalse($noinline$constString0Equals("1"));
+
+ Assert.assertTrue($noinline$constString7Equals("0123456"));
+ Assert.assertFalse($noinline$constString7Equals("012345"));
+ Assert.assertFalse($noinline$constString7Equals("01234567"));
+ Assert.assertFalse($noinline$constString7Equals("012345x"));
+ Assert.assertFalse($noinline$constString7Equals("012345\u0440"));
+
+ Assert.assertTrue($noinline$constString14Equals("01234567890123"));
+ Assert.assertFalse($noinline$constString14Equals("0123456789012"));
+ Assert.assertFalse($noinline$constString14Equals("012345678901234"));
+ Assert.assertFalse($noinline$constString14Equals("0123456789012x"));
+ Assert.assertFalse($noinline$constString14Equals("0123456789012\u0440"));
+
+ Assert.assertTrue($noinline$constString24Equals("012345678901234567890123"));
+ Assert.assertFalse($noinline$constString24Equals("01234567890123456789012"));
+ Assert.assertFalse($noinline$constString24Equals("0123456789012345678901234"));
+ Assert.assertFalse($noinline$constString24Equals("01234567890123456789012x"));
+ Assert.assertFalse($noinline$constString24Equals("01234567890123456789012\u0440"));
+
+ Assert.assertTrue($noinline$constString29Equals("01234567890123456789012345678"));
+ Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567"));
+ Assert.assertFalse($noinline$constString29Equals("012345678901234567890123456789"));
+ Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567x"));
+ Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567\u0440"));
+
+ Assert.assertTrue($noinline$constString35Equals("01234567890123456789012345678901234"));
+ Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123"));
+ Assert.assertFalse($noinline$constString35Equals("012345678901234567890123456789012345"));
+ Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$constString35Equals("0123456789012345678901234567890123\u0040"));
+
+ Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x"));
+ Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456"));
+
+ Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012x"));
+ Assert.assertFalse($noinline$constNonAsciiString14Equals("01234567890123"));
+
+ Assert.assertTrue($noinline$constNonAsciiString24Equals("\u044012345678901234567890123"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\u0440123456789012345678901234"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012x"));
+ Assert.assertFalse($noinline$constNonAsciiString24Equals("\012345678901234567890123"));
+
+ Assert.assertTrue(
+ $noinline$constNonAsciiString29Equals("\u04401234567890123456789012345678"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString29Equals("\u044012345678901234567890123456789"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567x"));
+ Assert.assertFalse($noinline$constNonAsciiString29Equals("01234567890123456789012345678"));
+
+ Assert.assertTrue(
+ $noinline$constNonAsciiString35Equals("\u04401234567890123456789012345678901234"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("\u044012345678901234567890123456789012345"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x"));
+ Assert.assertFalse(
+ $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234"));
+ }
+
+ public static boolean $noinline$equalsConstString0(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("");
+ }
+
+ public static boolean $noinline$equalsConstString7(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("0123456");
+ }
+
+ public static boolean $noinline$equalsConstString14(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("01234567890123");
+ }
+
+ public static boolean $noinline$equalsConstString24(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("012345678901234567890123");
+ }
+
+ public static boolean $noinline$equalsConstString29(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("01234567890123456789012345678");
+ }
+
+ public static boolean $noinline$equalsConstString35(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("01234567890123456789012345678901234");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString7(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u0440123456");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString14(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u04401234567890123");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString24(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u044012345678901234567890123");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString29(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u04401234567890123456789012345678");
+ }
+
+ public static boolean $noinline$equalsConstNonAsciiString35(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("\u04401234567890123456789012345678901234");
+ }
+
+ public static boolean $noinline$constString0Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return s.equals("");
+ }
+
+ public static boolean $noinline$constString7Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "0123456".equals(s);
+ }
+
+ public static boolean $noinline$constString14Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "01234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constString24Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "012345678901234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constString29Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "01234567890123456789012345678".equals(s);
+ }
+
+ public static boolean $noinline$constString35Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "01234567890123456789012345678901234".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString7Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u0440123456".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString14Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u04401234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString24Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u044012345678901234567890123".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString29Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u04401234567890123456789012345678".equals(s);
+ }
+
+ public static boolean $noinline$constNonAsciiString35Equals(String s) {
+ if (doThrow) { throw new Error(); }
+ return "\u04401234567890123456789012345678901234".equals(s);
+ }
+
public static int $noinline$compareTo(String lhs, String rhs) {
if (doThrow) { throw new Error(); }
return lhs.compareTo(rhs);
diff --git a/test/044-proxy/build b/test/044-proxy/build
new file mode 100755
index 0000000000..ab956e26e1
--- /dev/null
+++ b/test/044-proxy/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+./default-build "$@" --experimental java-8
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index 2a5f0b90db..63a46200d9 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -87,7 +87,7 @@ Got expected exception
Proxy methods: [public final boolean $PROXY_CLASS_NAME1$.equals(java.lang.Object), public final java.lang.Object $PROXY_CLASS_NAME1$.foo(), public final java.lang.String $PROXY_CLASS_NAME1$.foo(), public final int $PROXY_CLASS_NAME1$.hashCode(), public final java.lang.String $PROXY_CLASS_NAME1$.toString()]
Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
Invoking foo using I2 type: hello
-Invocation of public abstract java.lang.Object NarrowingTest$I1.foo()
+Invocation of public default java.lang.Object NarrowingTest$I2.foo()
Invoking foo using I1 type: 1
Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
Got expected exception
diff --git a/test/071-dexfile-map-clean/build b/test/071-dexfile-map-clean/build
new file mode 100755
index 0000000000..a171fc3b3b
--- /dev/null
+++ b/test/071-dexfile-map-clean/build
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed.
+# This is needed for our test correctness which validates classes.dex are mapped file-backed.
+#
+# In addition, align to at least 4 bytes since that's the dex alignment requirement.
+./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/071-dexfile-map-clean/expected.txt b/test/071-dexfile-map-clean/expected.txt
new file mode 100644
index 0000000000..af7fb2806b
--- /dev/null
+++ b/test/071-dexfile-map-clean/expected.txt
@@ -0,0 +1,3 @@
+Another
+Secondary dexfile mmap is clean
+Another Instance
diff --git a/test/071-dexfile-map-clean/info.txt b/test/071-dexfile-map-clean/info.txt
new file mode 100644
index 0000000000..7e45808603
--- /dev/null
+++ b/test/071-dexfile-map-clean/info.txt
@@ -0,0 +1,11 @@
+Exercise Dalvik-specific DEX file feature. Will not work on RI.
+
+If these conditions are met:
+* When we are loading in a secondary dex file
+* and when dex2oat is not used
+* and the dex file is stored uncompressed in a ZIP file
+
+Then check:
+* The dex file is memory-mapped file-backed as clean memory
+(i.e. there is no extraction step)
+
diff --git a/test/071-dexfile-map-clean/run b/test/071-dexfile-map-clean/run
new file mode 100755
index 0000000000..9c100ec497
--- /dev/null
+++ b/test/071-dexfile-map-clean/run
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright 2017 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 without dex2oat so that we don't create oat/vdex files
+# when trying to load the secondary dex file.
+
+# In this way, the secondary dex file will be forced to be
+# loaded directly.
+#
+# In addition, make sure we call 'sync'
+# before executing dalvikvm because otherwise
+# it's highly likely the pushed JAR files haven't
+# been committed to permanent storage yet,
+# and when we mmap them the kernel will think
+# the memory is dirty (despite being file-backed).
+# (Note: this was reproducible 100% of the time on
+# a target angler device).
+./default-run "$@" --no-dex2oat --sync
diff --git a/test/071-dexfile-map-clean/src-ex/Another.java b/test/071-dexfile-map-clean/src-ex/Another.java
new file mode 100644
index 0000000000..58464a65ab
--- /dev/null
+++ b/test/071-dexfile-map-clean/src-ex/Another.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Another {
+ public Another() {
+ System.out.println("Another Instance");
+ }
+}
diff --git a/test/071-dexfile-map-clean/src/Main.java b/test/071-dexfile-map-clean/src/Main.java
new file mode 100644
index 0000000000..8a196dd52e
--- /dev/null
+++ b/test/071-dexfile-map-clean/src/Main.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 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;
+import java.util.Enumeration;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * DexFile tests (Dalvik-specific).
+ */
+public class Main {
+ private static final String CLASS_PATH =
+ System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar";
+
+ /**
+ * Prep the environment then run the test.
+ */
+ public static void main(String[] args) throws Exception {
+ // Load the dex file, this is a pre-requisite to mmap-ing it in.
+ Class<?> AnotherClass = testDexFile();
+ // Check that the memory maps are clean.
+ testDexMemoryMaps();
+
+ // Prevent garbage collector from collecting our DexFile
+ // (and unmapping too early) by using it after we finish
+ // our verification.
+ AnotherClass.newInstance();
+ }
+
+ private static boolean checkSmapsEntry(String[] smapsLines, int offset) {
+ String nameDescription = smapsLines[offset];
+ String[] split = nameDescription.split(" ");
+
+ String permissions = split[1];
+ // Mapped as read-only + anonymous.
+ if (!permissions.startsWith("r--p")) {
+ return false;
+ }
+
+ boolean validated = false;
+
+ // We have the right entry, now make sure it's valid.
+ for (int i = offset; i < smapsLines.length; ++i) {
+ String line = smapsLines[i];
+
+ if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) {
+ String lineTrimmed = line.trim();
+ String[] lineSplit = lineTrimmed.split(" +");
+
+ String sizeUsuallyInKb = lineSplit[lineSplit.length - 2];
+
+ sizeUsuallyInKb = sizeUsuallyInKb.trim();
+
+ if (!sizeUsuallyInKb.equals("0")) {
+ System.out.println(
+ "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty");
+ System.out.println(line);
+ } else {
+ validated = true;
+ }
+ }
+
+ // VmFlags marks the "end" of an smaps entry.
+ if (line.startsWith("VmFlags")) {
+ break;
+ }
+ }
+
+ if (validated) {
+ System.out.println("Secondary dexfile mmap is clean");
+ } else {
+ System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries");
+ }
+
+ return true;
+ }
+
+ // This test takes relies on dex2oat being skipped.
+ // (enforced in 'run' file by using '--no-dex2oat'
+ //
+ // This could happen in a non-test situation
+ // if a secondary dex file is loaded (but not yet maintenance-mode compiled)
+ // with JIT.
+ //
+ // Or it could also happen if a secondary dex file is loaded and forced
+ // into running into the interpreter (e.g. duplicate classes).
+ //
+ // Rather than relying on those weird fallbacks,
+ // we force the runtime not to dex2oat the dex file to ensure
+ // this test is repeatable and less brittle.
+ private static void testDexMemoryMaps() throws Exception {
+ // Ensure that the secondary dex file is mapped clean (directly from JAR file).
+ String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps")));
+
+ String[] smapsLines = smaps.split("\n");
+ boolean found = true;
+ for (int i = 0; i < smapsLines.length; ++i) {
+ if (smapsLines[i].contains(CLASS_PATH)) {
+ if (checkSmapsEntry(smapsLines, i)) {
+ return;
+ } // else we found the wrong one, keep going.
+ }
+ }
+
+ // Error case:
+ System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry");
+ System.out.println(smaps);
+ }
+
+ private static Class<?> testDexFile() throws Exception {
+ ClassLoader classLoader = Main.class.getClassLoader();
+ Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile");
+ Method DexFile_loadDex = DexFile.getMethod("loadDex",
+ String.class,
+ String.class,
+ Integer.TYPE);
+ Method DexFile_entries = DexFile.getMethod("entries");
+ Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0);
+ Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile);
+ while (e.hasMoreElements()) {
+ String className = e.nextElement();
+ System.out.println(className);
+ }
+
+ Method DexFile_loadClass = DexFile.getMethod("loadClass",
+ String.class,
+ ClassLoader.class);
+ Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile,
+ "Another", Main.class.getClassLoader());
+ return AnotherClass;
+ }
+}
diff --git a/test/121-modifiers/build b/test/121-modifiers/build
deleted file mode 100644
index 771dd51829..0000000000
--- a/test/121-modifiers/build
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/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
-
-# The classes are pre-compiled and modified with ASM.
-#
-# To reproduce, compile the source files. Asm.java needs the ASM libraries (core and tree). Then
-# run Asm.java, which produces Inf.out and NonInf.out. Rename these to class files and put them
-# into the classes directory (this assumes the ASM libraries are names asm.jar and asm-tree.jar):
-#
-# javac Inf.java NonInf.java Main.java
-# javac -cp asm.jar:asm-tree.jar:. Asm.java
-# java -cp asm.jar:asm-tree.jar:. Asm
-# mv Inf.out classes/Inf.class
-# mv NonInf.out classes/NonInf.class
-# mv Main.class A.class A\$B.class A\$C.class classes/
-
-if [ ${USE_JACK} = "true" ]; then
- jar cf classes.jill.jar -C classes .
- # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers.
- ${JACK} --sanity-checks off --import classes.jill.jar --output-dex .
-else
- ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
-fi
-zip $TEST_NAME.jar classes.dex
diff --git a/test/121-modifiers/info.txt b/test/121-modifiers/info.txt
index 943cbf8aba..129aee8ae6 100644
--- a/test/121-modifiers/info.txt
+++ b/test/121-modifiers/info.txt
@@ -1 +1,18 @@
This is a test checking the modifier (access flags) handling of ART.
+
+The classes are pre-compiled and modified with ASM.
+
+To reproduce, compile the source files. Asm.java needs the ASM libraries (core and tree). Then
+run Asm.java, which produces Inf.out and NonInf.out. Rename these to class files and put them
+into the classes directory (this assumes the ASM libraries are names asm.jar and asm-tree.jar).
+Finally, compile with jack/jill or dx, and run baksmali.
+
+javac Inf.java NonInf.java Main.java
+javac -cp asm.jar:asm-tree.jar:. Asm.java
+java -cp asm.jar:asm-tree.jar:. Asm
+mv Inf.out classes/Inf.class
+mv NonInf.out classes/NonInf.class
+mv Main.class A.class A\$B.class A\$C.class classes/
+dx --debug --dex --output=classes.dex classes
+baksmali classes.dex
+mv out/*.smali smali/
diff --git a/test/121-modifiers/smali/A$B.smali b/test/121-modifiers/smali/A$B.smali
new file mode 100644
index 0000000000..d6addc2c94
--- /dev/null
+++ b/test/121-modifiers/smali/A$B.smali
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+.class LA$B;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/EnclosingClass;
+ value = LA;
+.end annotation
+
+.annotation system Ldalvik/annotation/InnerClass;
+ accessFlags = 0xa
+ name = "B"
+.end annotation
+
+
+# direct methods
+.method private constructor <init>()V
+ .registers 1
+
+ .prologue
+ .line 19
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
diff --git a/test/121-modifiers/smali/A$C.smali b/test/121-modifiers/smali/A$C.smali
new file mode 100644
index 0000000000..eba4756ad6
--- /dev/null
+++ b/test/121-modifiers/smali/A$C.smali
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+.class public interface abstract LA$C;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/EnclosingClass;
+ value = LA;
+.end annotation
+
+.annotation system Ldalvik/annotation/InnerClass;
+ accessFlags = 0x60c
+ name = "C"
+.end annotation
diff --git a/test/121-modifiers/smali/A.smali b/test/121-modifiers/smali/A.smali
new file mode 100644
index 0000000000..b1078bcce0
--- /dev/null
+++ b/test/121-modifiers/smali/A.smali
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+
+.class LA;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/MemberClasses;
+ value = {
+ LA$B;,
+ LA$C;
+ }
+.end annotation
+
+
+# direct methods
+.method constructor <init>()V
+ .registers 1
+
+ .prologue
+ .line 18
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ .line 21
+ return-void
+.end method
diff --git a/test/121-modifiers/smali/Inf.smali b/test/121-modifiers/smali/Inf.smali
new file mode 100644
index 0000000000..6a3a7ab24c
--- /dev/null
+++ b/test/121-modifiers/smali/Inf.smali
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+.class public interface abstract LInf;
+.super Ljava/lang/Object;
+.source "Inf.java"
+
+
+# static fields
+.field public static final I:I
diff --git a/test/121-modifiers/smali/NonInf.smali b/test/121-modifiers/smali/NonInf.smali
new file mode 100644
index 0000000000..34bf0312e4
--- /dev/null
+++ b/test/121-modifiers/smali/NonInf.smali
@@ -0,0 +1,177 @@
+#
+# 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.
+#
+
+.class public abstract LNonInf;
+.super Ljava/lang/Object;
+.source "NonInf.java"
+
+
+# static fields
+.field static staticField:I
+
+
+# instance fields
+.field final finalField:I
+
+.field private privateField:I
+
+.field protected protectedField:I
+
+.field public publicField:I
+
+.field transient transientField:I
+
+.field volatile volatileField:I
+
+
+# direct methods
+.method public constructor <init>()V
+ .registers 2
+
+ .prologue
+ .line 11
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ .line 12
+ const/4 v0, 0x0
+
+ iput v0, p0, LNonInf;->publicField:I
+
+ .line 13
+ const/4 v0, 0x1
+
+ iput v0, p0, LNonInf;->privateField:I
+
+ .line 14
+ const/4 v0, 0x2
+
+ iput v0, p0, LNonInf;->protectedField:I
+
+ .line 15
+ const/4 v0, 0x3
+
+ sput v0, LNonInf;->staticField:I
+
+ .line 16
+ const/4 v0, 0x4
+
+ iput v0, p0, LNonInf;->transientField:I
+
+ .line 17
+ const/4 v0, 0x5
+
+ iput v0, p0, LNonInf;->volatileField:I
+
+ .line 18
+ const/4 v0, 0x6
+
+ iput v0, p0, LNonInf;->finalField:I
+
+ .line 19
+ return-void
+.end method
+
+.method private privateMethod()I
+ .registers 2
+
+ .prologue
+ .line 24
+ const/4 v0, 0x0
+
+ return v0
+.end method
+
+.method public static staticMethod()I
+ .registers 1
+
+ .prologue
+ .line 42
+ const/4 v0, 0x0
+
+ return v0
+.end method
+
+
+# virtual methods
+.method public abstract abstractMethod()I
+.end method
+
+.method public final finalMethod()I
+ .registers 2
+
+ .prologue
+ .line 54
+ const/4 v0, 0x0
+
+ return v0
+.end method
+
+.method public native nativeMethod()V
+.end method
+
+.method protected protectedMethod()I
+ .registers 2
+
+ .prologue
+ .line 28
+ const/4 v0, 0x0
+
+ return v0
+.end method
+
+.method public publicMethod()I
+ .registers 2
+
+ .prologue
+ .line 32
+ const/4 v0, 0x0
+
+ return v0
+.end method
+
+.method public strictfp strictfpMethod()D
+ .registers 3
+
+ .prologue
+ .line 46
+ const-wide/16 v0, 0x0
+
+ return-wide v0
+.end method
+
+.method public declared-synchronized synchronizedMethod()I
+ .registers 2
+
+ .prologue
+ monitor-enter p0
+
+ .line 38
+ const/4 v0, 0x0
+
+ monitor-exit p0
+
+ return v0
+.end method
+
+.method public varargs varargsMethod([Ljava/lang/Object;)I
+ .registers 3
+
+ .prologue
+ .line 50
+ const/4 v0, 0x0
+
+ return v0
+.end method
diff --git a/test/121-modifiers/src-java/A.java b/test/121-modifiers/src-java/A.java
new file mode 100644
index 0000000000..d97f6b3f89
--- /dev/null
+++ b/test/121-modifiers/src-java/A.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+// These classes are to check the additional flags for inner classes.
+class A {
+ private static class B {
+ }
+ protected static interface C {
+ }
+}
diff --git a/test/121-modifiers/src/Asm.java b/test/121-modifiers/src-java/Asm.java
index f120622120..f120622120 100644
--- a/test/121-modifiers/src/Asm.java
+++ b/test/121-modifiers/src-java/Asm.java
diff --git a/test/121-modifiers/src/Inf.java b/test/121-modifiers/src-java/Inf.java
index 1dadae0e47..1dadae0e47 100644
--- a/test/121-modifiers/src/Inf.java
+++ b/test/121-modifiers/src-java/Inf.java
diff --git a/test/121-modifiers/src/NonInf.java b/test/121-modifiers/src-java/NonInf.java
index 52e4882532..52e4882532 100644
--- a/test/121-modifiers/src/NonInf.java
+++ b/test/121-modifiers/src-java/NonInf.java
diff --git a/test/121-modifiers/src/Main.java b/test/121-modifiers/src2/Main.java
index e21b789cc7..62e65a8fbe 100644
--- a/test/121-modifiers/src/Main.java
+++ b/test/121-modifiers/src2/Main.java
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-// These classes are to check the additional flags for inner classes.
-class A {
- private static class B {
- }
- protected static interface C {
- }
-}
-
public class Main {
public final static int INTERFACE_DEFINED_BITS =
0x0001 | // public, may be set.
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index ed7524c7ad..52f3f84406 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -329,7 +329,7 @@ public class Main {
/// CHECK-NOT: cbz
// Terminate the scope for the CHECK-NOT search at the reference or length comparison,
// whichever comes first.
- /// CHECK: cmp {{w.*,}} {{w.*}}
+ /// CHECK: cmp {{w.*,}} {{w.*|#.*}}
public static boolean stringArgumentNotNull(Object obj) {
obj.getClass();
return "foo".equals(obj);
@@ -380,10 +380,10 @@ public class Main {
// so repeat the check twice.
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}]
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0]
- /// CHECK: cmp {{w\d+}}, {{w\d+}}
+ /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}}
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}]
/// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0]
- /// CHECK: cmp {{w\d+}}, {{w\d+}}
+ /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}}
public static boolean stringArgumentIsString() {
return "foo".equals(myString);
}
diff --git a/test/616-cha-native/expected.txt b/test/616-cha-native/expected.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/616-cha-native/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-native/info.txt b/test/616-cha-native/info.txt
new file mode 100644
index 0000000000..a17bcaba9c
--- /dev/null
+++ b/test/616-cha-native/info.txt
@@ -0,0 +1,2 @@
+Test for Class Hierarchy Analysis (CHA) single-implementation status updating
+behavior on an overridden native method.
diff --git a/test/616-cha-native/src/Main.java b/test/616-cha-native/src/Main.java
new file mode 100644
index 0000000000..53a463ce87
--- /dev/null
+++ b/test/616-cha-native/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+abstract class A {
+ public abstract void foo();
+}
+
+class B extends A {
+ public native void foo();
+}
+
+class C extends B {
+ public void foo() {}
+}
+
+public class Main {
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ }
+}
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index b6179449df..beea90a57d 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -196,8 +196,6 @@ public class Main {
// should return true for those cases.
assertSingleImplementation(java.lang.String.class, "charAt", true);
assertSingleImplementation(java.lang.Thread.class, "join", true);
- // We don't set single-implementation modifier bit for native methods.
- assertSingleImplementation(java.lang.Thread.class, "isInterrupted", false);
if (isInterpreted()) {
sIsOptimizing = false;
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index c0aedc199f..b35dff48ee 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -49,7 +49,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_nativeDumpClasses(JNIEnv*, jclass, j
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ObjectArray<mirror::Object>> classes =
hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(array));
- CHECK(classes.Get() != nullptr);
+ CHECK(classes != nullptr);
for (size_t i = 0, length = classes->GetLength(); i != length; ++i) {
CHECK(classes->Get(i) != nullptr) << i;
CHECK(classes->Get(i)->IsClass())
diff --git a/test/636-arm64-veneer-pool/build b/test/636-arm64-veneer-pool/build
new file mode 100755
index 0000000000..27cc4d6d98
--- /dev/null
+++ b/test/636-arm64-veneer-pool/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+# Make us exit on a failure.
+
+set -e
+
+# Use javac+dx instead of jack.
+export USE_JACK=false
+./default-build "$@"
diff --git a/test/636-arm64-veneer-pool/expected.txt b/test/636-arm64-veneer-pool/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/636-arm64-veneer-pool/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/636-arm64-veneer-pool/info.txt b/test/636-arm64-veneer-pool/info.txt
new file mode 100644
index 0000000000..2393be0538
--- /dev/null
+++ b/test/636-arm64-veneer-pool/info.txt
@@ -0,0 +1 @@
+Regression test for an issue with VIXL ARM64 veneer pools (b/34850123).
diff --git a/test/636-arm64-veneer-pool/src/Main.java b/test/636-arm64-veneer-pool/src/Main.java
new file mode 100644
index 0000000000..8229fee378
--- /dev/null
+++ b/test/636-arm64-veneer-pool/src/Main.java
@@ -0,0 +1,4223 @@
+/*
+ * Copyright (C) 2017 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 C0 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C2 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C3 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C4 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C5 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mImpl(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C6 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C7 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C8 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C9 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C10 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C11 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C12 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C13 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C14 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C15 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C16 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C17 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C18 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C19 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C20 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C21 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C22 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C23 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C24 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C25 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C26 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C27 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C28 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C29 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C30 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C31 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C32 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C33 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C34 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C35 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C36 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C37 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C38 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C39 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C40 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C41 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C42 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C43 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C44 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C45 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C46 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C47 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C48 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C49 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C50 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C51 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C52 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C53 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C54 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C55 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C56 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C57 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C58 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C59 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C60 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C61 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C62 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C63 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C64 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C65 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C66 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C67 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C68 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C69 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C70 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C71 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C72 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C73 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C74 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C75 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C76 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C77 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C78 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C79 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C80 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C81 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C82 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C83 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C84 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C85 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C86 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C87 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C88 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C89 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C90 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mReport_Factory(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mApi(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C91 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C92 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C93 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C94 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mImpl(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C95 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C96 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C97 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C98 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C99 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C100 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C101 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C102 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C103 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C104 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C105 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void m_InMemoryScanner(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void m_Scanner(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C106 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C107 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C108 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C109 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C110 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C111 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C112 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C113 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C114 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C115 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C116 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C117 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C118 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C119 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C120 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C121 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C122 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C123 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C124 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C125 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C126 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C127 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C128 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C129 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C130 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C131 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C132 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C133 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C134 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C135 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C136 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C137 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C138 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C139 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C140 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C141 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C142 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C143 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C144 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C145 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C146 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C147 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C148 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C149 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C150 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C151 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C152 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C153 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C154 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C155 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C156 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C157 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C158 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C159 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C160 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C161 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C162 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C163 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C164 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C165 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C166 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C167 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C168 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C169 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C170 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C171 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C172 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C173 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C174 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C175 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C176 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C177 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C178 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C179 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C180 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C181 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C182 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C183 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C184 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C185 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C186 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C187 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C188 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C189 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C190 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C191 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C192 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C193 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C194 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C195 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C196 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C197 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C198 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C199 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C200 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C201 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C202 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C203 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C204 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C205 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C206 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C207 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C208 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C209 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C210 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C211 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C212 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C213 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C214 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C215 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C216 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C217 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C218 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C219 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C220 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C221 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C222 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C223 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C224 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C225 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C226 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C227 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C228 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C229 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C230 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C231 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C232 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C233 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C234 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C235 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C236 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C237 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C238 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C239 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C240 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C241 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C242 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C243 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C244 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C245 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C246 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C247 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C248 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C249 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C250 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C251 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C252 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C253 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C254 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C255 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mFactory(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C256 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C257 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C258 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C259 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C260 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C261 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C262 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C263 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C264 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C265 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C266 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C267 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C268 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C269 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C270 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C271 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C272 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C273 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C274 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C275 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C276 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C277 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C278 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C279 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C280 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C281 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C282 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C283 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C284 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C285 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C286 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C287 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C288 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C289 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C290 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C291 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C292 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C293 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C294 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C295 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C296 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C297 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C298 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C299 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C300 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C301 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C302 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C303 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C304 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C305 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C306 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C307 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C308 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C309 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C310 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C311 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C312 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C313 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C314 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C315 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C316 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C317 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C318 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C319 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C320 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C321 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C322 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C323 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C324 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C325 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C326 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C327 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C328 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C329 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C330 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C331 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C332 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C333 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C334 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C335 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C336 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C337 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C338 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C339 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C340 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C341 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C342 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C343 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C344 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C345 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C346 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C347 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C348 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C349 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C350 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C351 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C352 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C353 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C354 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C355 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C356 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C357 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C358 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C359 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C360 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C361 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C362 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C363 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C364 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C365 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C366 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C367 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C368 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C369 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C370 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C371 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C372 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C373 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C374 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C375 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C376 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C377 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C378 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C379 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C380 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C381 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C382 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C383 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C384 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C385 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C386 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C387 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C388 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C389 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C390 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C391 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C392 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C393 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C394 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C395 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C396 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C397 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C398 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C399 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C400 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C401 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C402 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C403 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C404 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C405 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C406 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C407 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C408 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C409 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C410 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C411 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C412 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C413 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C414 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C415 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C416 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C417 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C418 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C419 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C420 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C421 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C422 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C423 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C424 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C425 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C426 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C427 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C428 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C429 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C430 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C431 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C432 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C433 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C434 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C435 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C436 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C437 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C438 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C439 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C440 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C441 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C442 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C443 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C444 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C445 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C446 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C447 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C448 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C449 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C450 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C451 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C452 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C453 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C454 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C455 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C456 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C457 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mMap(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C458 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C459 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C460 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C461 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C462 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C463 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C464 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C465 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C466 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C467 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C468 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C469 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C470 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C471 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C472 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C473 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C474 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C475 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C476 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C477 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C478 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C479 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C480 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C481 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C482 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C483 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C484 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C485 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C486 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C487 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C488 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C489 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C490 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C491 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C492 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C493 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C494 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C495 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C496 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C497 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C498 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C499 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C500 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C501 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C502 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C503 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C504 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C505 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C506 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C507 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C508 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C509 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C510 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C511 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C512 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C513 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C514 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C515 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C516 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C517 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C518 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C519 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C520 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C521 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C522 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C523 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C524 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C525 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C526 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C527 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C528 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C529 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C530 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C531 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C532 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C533 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C534 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C535 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C536 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C537 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C538 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C539 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C540 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C541 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C542 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C543 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C544 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C545 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C546 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C547 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C548 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C549 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C550 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C551 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C552 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C553 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C554 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C555 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C556 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C557 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C558 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C559 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C560 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C561 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C562 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C563 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C564 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C565 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C566 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C567 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C568 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C569 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C570 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C571 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C572 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C573 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C574 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C575 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C576 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C577 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mDebug(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C578 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C579 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C580 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C581 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C582 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C583 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C584 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C585 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C586 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C587 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C588 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C589 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C590 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C591 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C592 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C593 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C594 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C595 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C596 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C597 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C598 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C599 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C600 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C601 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C602 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C603 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C604 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C605 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C606 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C607 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C608 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C609 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C610 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C611 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C612 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C613 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C614 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C615 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C616 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C617 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C618 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C619 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C620 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C621 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C622 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C623 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C624 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C625 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C626 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C627 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C628 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C629 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C630 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C631 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C632 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C633 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C634 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C635 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C636 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C637 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C638 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C639 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C640 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C641 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C642 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C643 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C644 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C645 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C646 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C647 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C648 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C649 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C650 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C651 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C652 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C653 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C654 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C655 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C656 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C657 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C658 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C659 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C660 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C661 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C662 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C663 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C664 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C665 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C666 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C667 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C668 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C669 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C670 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C671 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C672 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C673 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C674 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C675 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C676 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C677 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C678 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C679 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C680 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C681 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C682 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C683 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C684 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C685 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C686 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C687 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C688 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C689 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C690 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C691 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C692 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C693 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C694 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C695 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C696 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C697 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C698 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C699 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C700 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C701 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C702 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C703 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C704 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C705 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C706 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C707 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C708 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C709 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C710 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C711 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C712 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C713 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C714 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C715 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C716 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C717 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C718 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C719 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C720 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C721 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C722 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C723 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C724 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C725 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C726 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C727 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C728 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C729 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C730 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C731 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C732 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C733 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C734 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C735 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C736 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C737 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C738 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C739 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C740 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C741 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C742 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C743 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C744 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C745 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C746 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C747 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C748 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C749 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C750 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C751 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C752 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C753 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C754 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C755 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C756 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C757 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C758 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C759 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C760 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C761 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C762 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C763 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C764 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C765 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C766 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C767 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C768 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C769 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C770 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C771 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C772 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C773 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C774 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C775 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C776 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C777 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C778 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C779 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C780 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C781 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C782 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C783 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C784 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C785 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C786 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C787 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C788 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C789 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C790 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C791 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C792 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C793 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C794 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C795 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C796 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C797 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C798 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C799 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C800 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C801 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C802 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C803 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C804 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C805 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C806 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C807 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C808 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C809 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C810 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C811 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C812 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C813 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C814 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C815 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C816 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C817 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C818 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C819 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C820 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C821 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C822 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C823 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C824 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C825 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C826 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C827 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C828 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C829 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C830 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C831 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C832 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C833 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C834 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C835 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C836 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C837 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C838 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C839 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C840 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C841 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C842 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C843 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C844 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C845 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C846 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C847 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C848 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mMap(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C849 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C850 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C851 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C852 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C853 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C854 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C855 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C856 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C857 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C858 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C859 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C860 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C861 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C862 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C863 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C864 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C865 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C866 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C867 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C868 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C869 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C870 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mImpl(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C871 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C872 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C873 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C874 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C875 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C876 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C877 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C878 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C879 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C880 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C881 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C882 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C883 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C884 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C885 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C886 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C887 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C888 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C889 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C890 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C891 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C892 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C893 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C894 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C895 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C896 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C897 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C898 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C899 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C900 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C901 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C902 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C903 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C904 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C905 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C906 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C907 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C908 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C909 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C910 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C911 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C912 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C913 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C914 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C915 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C916 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C917 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C918 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C919 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C920 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C921 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C922 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C923 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C924 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C925 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C926 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C927 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C928 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C929 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C930 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C931 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C932 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C933 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C934 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C935 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C936 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C937 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C938 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C939 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C940 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C941 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C942 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C943 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C944 {
+ public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+ public static void mManager(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } }
+}
+class C945 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C946 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C947 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C948 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C949 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C950 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C951 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C952 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C953 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C954 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C955 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C956 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C957 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C958 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C959 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C960 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C961 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C962 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C963 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C964 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C965 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C966 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C967 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C968 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C969 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C970 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C971 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C972 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C973 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C974 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C975 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C976 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C977 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C978 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C979 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C980 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C981 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C982 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C983 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C984 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C985 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C986 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C987 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C988 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C989 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C990 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C991 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C992 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C993 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C994 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C995 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C996 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C997 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C998 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C999 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1000 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1001 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1002 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1003 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1004 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1005 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1006 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1007 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1008 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1009 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1010 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1011 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1012 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1013 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1014 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1015 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1016 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1017 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1018 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1019 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1020 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1021 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1022 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1023 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1024 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1025 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1026 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1027 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1028 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1029 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1030 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1031 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1032 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1033 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1034 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1035 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1036 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1037 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1038 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1039 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1040 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1041 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1042 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1043 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1044 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1045 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1046 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1047 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1048 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1049 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1050 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1051 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1052 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1053 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1054 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1055 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1056 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1057 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1058 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1059 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1060 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1061 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1062 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1063 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1064 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1065 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1066 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1067 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1068 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1069 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1070 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1071 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1072 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1073 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1074 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1075 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1076 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1077 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1078 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1079 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1080 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1081 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1082 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1083 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1084 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1085 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1086 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1087 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1088 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1089 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1090 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1091 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1092 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1093 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1094 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1095 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1096 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1097 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1098 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1099 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1100 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1101 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1102 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1103 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1104 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1105 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1106 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1107 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1108 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1109 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1110 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1111 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1112 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1113 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1114 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1115 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1116 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1117 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1118 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1119 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1120 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1121 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1122 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1123 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1124 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1125 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1126 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1127 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1128 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1129 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1130 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1131 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1132 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1133 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1134 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1135 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1136 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1137 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1138 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1139 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1140 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1141 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1142 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1143 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1144 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1145 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1146 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1147 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1148 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1149 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1150 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1151 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1152 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1153 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1154 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1155 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1156 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1157 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1158 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1159 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1160 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1161 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1162 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1163 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1164 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1165 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1166 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1167 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1168 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1169 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1170 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1171 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1172 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1173 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1174 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1175 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1176 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1177 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1178 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1179 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1180 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1181 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1181a { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1181b { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1182 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1183 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1184 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1185 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1186 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1187 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1188 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1189 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1190 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1191 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1192 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1193 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1194 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1195 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1196 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1197 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1198 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1199 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1200 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1201 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1202 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1203 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1204 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1205 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1206 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1207 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1208 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1209 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1210 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1211 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+class C1212 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } }
+
+class Context {}
+class Binder {}
+
+public class Main {
+
+ java.util.HashMap<String, Integer> typeMap;
+ private void buildTypeMap() {}
+
+ // TODO: Add Checker assertions?
+ public void configure(Context context, Class<?> cls, Binder binder) {
+ if (this.typeMap == null) {
+ buildTypeMap();
+ }
+ Integer num = (Integer) this.typeMap.get(cls.getName());
+ if (num != null) {
+ switch (num.intValue()) {
+ case 0:
+ C0.m(context, binder);
+ return;
+ case 1:
+ C1.m(context, binder);
+ return;
+ case 2:
+ C2.m(context, binder);
+ return;
+ case 3:
+ C3.m(context, binder);
+ return;
+ case 4:
+ C4.m(context, binder);
+ return;
+ case 5:
+ C5.m(context, binder);
+ return;
+ case 6:
+ C6.m(context, binder);
+ C7.m(context, binder);
+ C8.m(context, binder);
+ return;
+ case 7:
+ C9.m(context, binder);
+ return;
+ case 8:
+ C10.m(context, binder);
+ return;
+ case 9:
+ C11.m(context, binder);
+ return;
+ case 10:
+ C12.m(context, binder);
+ return;
+ case 11:
+ C13.m(context, binder);
+ return;
+ case 12:
+ C14.m(context, binder);
+ return;
+ case 13:
+ C15.m(context, binder);
+ return;
+ case 14:
+ C16.m(context, binder);
+ return;
+ case 15:
+ C17.m(context, binder);
+ return;
+ case 16:
+ C18.m(context, binder);
+ C19.m(context, binder);
+ return;
+ case 17:
+ C20.m(context, binder);
+ return;
+ case 18:
+ C21.m(context, binder);
+ return;
+ case 19:
+ C22.m(context, binder);
+ return;
+ case 20:
+ C23.m(context, binder);
+ C24.m(context, binder);
+ C25.m(context, binder);
+ C26.m(context, binder);
+ C27.m(context, binder);
+ C28.m(context, binder);
+ C29.m(context, binder);
+ C30.m(context, binder);
+ C31.m(context, binder);
+ C32.m(context, binder);
+ C33.m(context, binder);
+ C34.m(context, binder);
+ C35.m(context, binder);
+ C36.m(context, binder);
+ C37.m(context, binder);
+ C38.m(context, binder);
+ C39.m(context, binder);
+ C40.m(context, binder);
+ C41.m(context, binder);
+ C42.m(context, binder);
+ C43.m(context, binder);
+ C44.m(context, binder);
+ C45.m(context, binder);
+ C46.m(context, binder);
+ C47.m(context, binder);
+ C48.m(context, binder);
+ C49.m(context, binder);
+ C50.m(context, binder);
+ C51.m(context, binder);
+ C52.m(context, binder);
+ C53.m(context, binder);
+ C54.m(context, binder);
+ C55.m(context, binder);
+ C56.m(context, binder);
+ C57.m(context, binder);
+ C58.m(context, binder);
+ C59.m(context, binder);
+ C60.m(context, binder);
+ C61.m(context, binder);
+ C62.m(context, binder);
+ C63.m(context, binder);
+ C64.m(context, binder);
+ C65.m(context, binder);
+ C66.m(context, binder);
+ C67.m(context, binder);
+ C68.m(context, binder);
+ C69.m(context, binder);
+ C70.m(context, binder);
+ C71.m(context, binder);
+ C72.m(context, binder);
+ C73.m(context, binder);
+ C74.m(context, binder);
+ C75.m(context, binder);
+ C76.m(context, binder);
+ C77.m(context, binder);
+ C78.m(context, binder);
+ C79.m(context, binder);
+ C80.m(context, binder);
+ C81.m(context, binder);
+ C82.m(context, binder);
+ C83.m(context, binder);
+ C84.m(context, binder);
+ C85.m(context, binder);
+ return;
+ case 21:
+ C86.m(context, binder);
+ return;
+ case 22:
+ C87.m(context, binder);
+ return;
+ case 23:
+ C88.m(context, binder);
+ C89.m(context, binder);
+ return;
+ case 24:
+ C90.m(context, binder);
+ return;
+ case 25:
+ C91.m(context, binder);
+ return;
+ case 26:
+ C92.m(context, binder);
+ return;
+ case 27:
+ C93.m(context, binder);
+ return;
+ case 28:
+ C94.m(context, binder);
+ return;
+ case 29:
+ C95.m(context, binder);
+ return;
+ case 30:
+ C96.m(context, binder);
+ return;
+ case 31:
+ C97.m(context, binder);
+ return;
+ case 32:
+ C98.m(context, binder);
+ C99.m(context, binder);
+ return;
+ case 33:
+ C100.m(context, binder);
+ return;
+ case 34:
+ C101.m(context, binder);
+ return;
+ case 35:
+ C102.m(context, binder);
+ return;
+ case 36:
+ C103.m(context, binder);
+ return;
+ case 37:
+ C104.m(context, binder);
+ return;
+ case 38:
+ C105.m(context, binder);
+ return;
+ case 39:
+ C106.m(context, binder);
+ return;
+ case 40:
+ C107.m(context, binder);
+ return;
+ case 41:
+ C108.m(context, binder);
+ return;
+ case 42:
+ C109.m(context, binder);
+ return;
+ case 43:
+ C110.m(context, binder);
+ return;
+ case 44:
+ C111.m(context, binder);
+ return;
+ case 45:
+ C112.m(context, binder);
+ return;
+ case 46:
+ C113.m(context, binder);
+ return;
+ case 47:
+ C114.m(context, binder);
+ return;
+ case 48:
+ C115.m(context, binder);
+ return;
+ case 49:
+ C116.m(context, binder);
+ return;
+ case 50:
+ C117.m(context, binder);
+ C118.m(context, binder);
+ return;
+ case 51:
+ C119.m(context, binder);
+ return;
+ case 52:
+ C120.m(context, binder);
+ return;
+ case 53:
+ C121.m(context, binder);
+ return;
+ case 54:
+ C122.m(context, binder);
+ return;
+ case 55:
+ C123.m(context, binder);
+ return;
+ case 56:
+ C124.m(context, binder);
+ return;
+ case 57:
+ C125.m(context, binder);
+ return;
+ case 58:
+ C126.m(context, binder);
+ return;
+ case 59:
+ C127.m(context, binder);
+ return;
+ case 60:
+ C128.m(context, binder);
+ return;
+ case 61:
+ C129.m(context, binder);
+ return;
+ case 62:
+ C130.m(context, binder);
+ C131.m(context, binder);
+ C132.m(context, binder);
+ C133.m(context, binder);
+ C134.m(context, binder);
+ C135.m(context, binder);
+ C136.m(context, binder);
+ C137.m(context, binder);
+ return;
+ case 63:
+ C138.m(context, binder);
+ return;
+ case 64:
+ C139.m(context, binder);
+ return;
+ case 65:
+ C140.m(context, binder);
+ return;
+ case 66:
+ C141.m(context, binder);
+ return;
+ case 67:
+ C142.m(context, binder);
+ return;
+ case 68:
+ C143.m(context, binder);
+ C144.m(context, binder);
+ C145.m(context, binder);
+ return;
+ case 69:
+ C146.m(context, binder);
+ return;
+ case 70:
+ C147.m(context, binder);
+ return;
+ case 71:
+ C148.m(context, binder);
+ return;
+ case 72:
+ C149.m(context, binder);
+ return;
+ case 73:
+ C150.m(context, binder);
+ return;
+ case 74:
+ C151.m(context, binder);
+ return;
+ case 75:
+ C152.m(context, binder);
+ return;
+ case 76:
+ C153.m(context, binder);
+ return;
+ case 77:
+ C154.m(context, binder);
+ return;
+ case 78:
+ C155.m(context, binder);
+ return;
+ case 79:
+ C156.m(context, binder);
+ return;
+ case 80:
+ C157.m(context, binder);
+ C158.m(context, binder);
+ C159.m(context, binder);
+ return;
+ case 81:
+ C160.m(context, binder);
+ return;
+ case 82:
+ C161.m(context, binder);
+ return;
+ case 83:
+ C162.m(context, binder);
+ return;
+ case 84:
+ C163.m(context, binder);
+ return;
+ case 85:
+ C164.m(context, binder);
+ C165.m(context, binder);
+ C166.m(context, binder);
+ C167.m(context, binder);
+ C168.m(context, binder);
+ C169.m(context, binder);
+ C170.m(context, binder);
+ C171.m(context, binder);
+ C172.m(context, binder);
+ C173.m(context, binder);
+ C174.m(context, binder);
+ C175.m(context, binder);
+ C176.m(context, binder);
+ C177.m(context, binder);
+ C178.m(context, binder);
+ C179.m(context, binder);
+ C180.m(context, binder);
+ C181.m(context, binder);
+ C182.m(context, binder);
+ C183.m(context, binder);
+ C184.m(context, binder);
+ return;
+ case 86:
+ C185.m(context, binder);
+ return;
+ case 87:
+ C186.m(context, binder);
+ return;
+ case 88:
+ C187.m(context, binder);
+ return;
+ case 89:
+ C188.m(context, binder);
+ return;
+ case 90:
+ C189.m(context, binder);
+ return;
+ case 91:
+ C190.m(context, binder);
+ return;
+ case 92:
+ C191.m(context, binder);
+ return;
+ case 93:
+ C192.m(context, binder);
+ return;
+ case 94:
+ C193.m(context, binder);
+ return;
+ case 95:
+ C194.m(context, binder);
+ return;
+ case 96:
+ C195.m(context, binder);
+ return;
+ case 97:
+ C196.m(context, binder);
+ return;
+ case 98:
+ C197.m(context, binder);
+ return;
+ case 99:
+ C198.m(context, binder);
+ return;
+ case 100:
+ C199.m(context, binder);
+ return;
+ case 101:
+ C200.m(context, binder);
+ return;
+ case 102:
+ C201.m(context, binder);
+ return;
+ case 103:
+ C202.m(context, binder);
+ C203.m(context, binder);
+ C204.m(context, binder);
+ C205.m(context, binder);
+ C206.m(context, binder);
+ return;
+ case 104:
+ C207.m(context, binder);
+ return;
+ case 105:
+ C208.m(context, binder);
+ return;
+ case 106:
+ C209.m(context, binder);
+ return;
+ case 107:
+ C210.m(context, binder);
+ return;
+ case 108:
+ C211.m(context, binder);
+ return;
+ case 109:
+ C212.m(context, binder);
+ return;
+ case 110:
+ C213.m(context, binder);
+ return;
+ case 111:
+ C214.m(context, binder);
+ return;
+ case 112:
+ C215.m(context, binder);
+ C216.m(context, binder);
+ C217.m(context, binder);
+ C218.m(context, binder);
+ C219.m(context, binder);
+ C220.m(context, binder);
+ C221.m(context, binder);
+ C222.m(context, binder);
+ C223.m(context, binder);
+ C224.m(context, binder);
+ C225.m(context, binder);
+ C226.m(context, binder);
+ return;
+ case 113:
+ C227.m(context, binder);
+ return;
+ case 114:
+ C228.m(context, binder);
+ return;
+ case 115:
+ C229.m(context, binder);
+ return;
+ case 116:
+ C230.m(context, binder);
+ return;
+ case 117:
+ C231.m(context, binder);
+ return;
+ case 118:
+ C232.m(context, binder);
+ return;
+ case 119:
+ C233.m(context, binder);
+ return;
+ case 120:
+ C234.m(context, binder);
+ return;
+ case 121:
+ C235.m(context, binder);
+ return;
+ case 122:
+ C236.m(context, binder);
+ return;
+ case 123:
+ C237.m(context, binder);
+ return;
+ case 124:
+ C238.m(context, binder);
+ return;
+ case 125:
+ C239.m(context, binder);
+ return;
+ case 126:
+ C240.m(context, binder);
+ return;
+ case 127:
+ C241.m(context, binder);
+ return;
+ case 128:
+ C242.m(context, binder);
+ return;
+ case 129:
+ C243.m(context, binder);
+ C244.m(context, binder);
+ C245.m(context, binder);
+ C246.m(context, binder);
+ C247.m(context, binder);
+ C248.m(context, binder);
+ C249.m(context, binder);
+ C250.m(context, binder);
+ C251.m(context, binder);
+ return;
+ case 130:
+ C252.m(context, binder);
+ return;
+ case 131:
+ C253.m(context, binder);
+ return;
+ case 132:
+ C254.m(context, binder);
+ return;
+ case 133:
+ C255.m(context, binder);
+ return;
+ case 134:
+ C256.m(context, binder);
+ return;
+ case 135:
+ C257.m(context, binder);
+ return;
+ case 136:
+ C258.m(context, binder);
+ return;
+ case 137:
+ C259.m(context, binder);
+ return;
+ case 138:
+ C260.m(context, binder);
+ return;
+ case 139:
+ C261.m(context, binder);
+ return;
+ case 140:
+ C262.m(context, binder);
+ return;
+ case 141:
+ C263.m(context, binder);
+ return;
+ case 142:
+ C264.m(context, binder);
+ return;
+ case 143:
+ C265.m(context, binder);
+ return;
+ case 144:
+ C266.m(context, binder);
+ C267.m(context, binder);
+ return;
+ case 145:
+ C268.m(context, binder);
+ return;
+ case 146:
+ C269.m(context, binder);
+ return;
+ case 147:
+ C270.m(context, binder);
+ return;
+ case 148:
+ C271.m(context, binder);
+ return;
+ case 149:
+ C272.m(context, binder);
+ return;
+ case 150:
+ C273.m(context, binder);
+ return;
+ case 151:
+ C274.m(context, binder);
+ return;
+ case 152:
+ C275.m(context, binder);
+ return;
+ case 153:
+ C276.m(context, binder);
+ return;
+ case 154:
+ C277.m(context, binder);
+ return;
+ case 155:
+ C278.m(context, binder);
+ return;
+ case 156:
+ C279.m(context, binder);
+ return;
+ case 157:
+ C280.m(context, binder);
+ return;
+ case 158:
+ C281.m(context, binder);
+ return;
+ case 159:
+ C282.m(context, binder);
+ return;
+ case 160:
+ C283.m(context, binder);
+ return;
+ case 161:
+ C284.m(context, binder);
+ return;
+ case 162:
+ C285.m(context, binder);
+ return;
+ case 163:
+ C286.m(context, binder);
+ return;
+ case 164:
+ C287.m(context, binder);
+ return;
+ case 165:
+ C288.m(context, binder);
+ return;
+ case 166:
+ C289.m(context, binder);
+ return;
+ case 167:
+ C290.m(context, binder);
+ return;
+ case 168:
+ C291.m(context, binder);
+ C292.m(context, binder);
+ C293.m(context, binder);
+ C294.m(context, binder);
+ C295.m(context, binder);
+ C296.m(context, binder);
+ C297.m(context, binder);
+ return;
+ case 169:
+ C298.m(context, binder);
+ return;
+ case 170:
+ C299.m(context, binder);
+ return;
+ case 171:
+ C300.m(context, binder);
+ return;
+ case 172:
+ C301.m(context, binder);
+ return;
+ case 173:
+ C302.m(context, binder);
+ return;
+ case 174:
+ C303.m(context, binder);
+ return;
+ case 175:
+ C304.m(context, binder);
+ return;
+ case 176:
+ C305.m(context, binder);
+ return;
+ case 177:
+ C306.m(context, binder);
+ return;
+ case 178:
+ C307.m(context, binder);
+ return;
+ case 179:
+ C308.m(context, binder);
+ return;
+ case 180:
+ C309.m(context, binder);
+ return;
+ case 181:
+ C310.m(context, binder);
+ return;
+ case 182:
+ C311.m(context, binder);
+ return;
+ case 183:
+ C312.m(context, binder);
+ return;
+ case 184:
+ C313.m(context, binder);
+ return;
+ case 185:
+ C314.m(context, binder);
+ return;
+ case 186:
+ C315.m(context, binder);
+ return;
+ case 187:
+ C316.m(context, binder);
+ return;
+ case 188:
+ C317.m(context, binder);
+ return;
+ case 189:
+ C318.m(context, binder);
+ return;
+ case 190:
+ C319.m(context, binder);
+ return;
+ case 191:
+ C320.m(context, binder);
+ return;
+ case 192:
+ C321.m(context, binder);
+ return;
+ case 193:
+ C322.m(context, binder);
+ return;
+ case 194:
+ C323.m(context, binder);
+ C324.m(context, binder);
+ C325.m(context, binder);
+ return;
+ case 195:
+ C326.m(context, binder);
+ return;
+ case 196:
+ C327.m(context, binder);
+ return;
+ case 197:
+ C328.m(context, binder);
+ return;
+ case 198:
+ C329.m(context, binder);
+ return;
+ case 199:
+ C330.m(context, binder);
+ return;
+ case 200:
+ C331.m(context, binder);
+ return;
+ case 201:
+ C332.m(context, binder);
+ return;
+ case 202:
+ C333.m(context, binder);
+ return;
+ case 203:
+ C334.m(context, binder);
+ C335.m(context, binder);
+ C336.m(context, binder);
+ C337.m(context, binder);
+ C338.m(context, binder);
+ C339.m(context, binder);
+ C340.m(context, binder);
+ C341.m(context, binder);
+ C342.m(context, binder);
+ C343.m(context, binder);
+ C344.m(context, binder);
+ C345.m(context, binder);
+ return;
+ case 204:
+ C346.m(context, binder);
+ return;
+ case 205:
+ C347.m(context, binder);
+ return;
+ case 206:
+ C348.m(context, binder);
+ return;
+ case 207:
+ C349.m(context, binder);
+ return;
+ case 208:
+ C350.m(context, binder);
+ return;
+ case 209:
+ C351.m(context, binder);
+ return;
+ case 210:
+ C352.m(context, binder);
+ C353.m(context, binder);
+ return;
+ case 211:
+ C354.m(context, binder);
+ return;
+ case 212:
+ C355.m(context, binder);
+ C356.m(context, binder);
+ C357.m(context, binder);
+ C358.m(context, binder);
+ C359.m(context, binder);
+ C360.m(context, binder);
+ C361.m(context, binder);
+ C362.m(context, binder);
+ C363.m(context, binder);
+ C364.m(context, binder);
+ C365.m(context, binder);
+ C366.m(context, binder);
+ C367.m(context, binder);
+ C368.m(context, binder);
+ C369.m(context, binder);
+ C370.m(context, binder);
+ C371.m(context, binder);
+ return;
+ case 213:
+ C372.m(context, binder);
+ return;
+ case 214:
+ C373.m(context, binder);
+ return;
+ case 215:
+ C374.m(context, binder);
+ return;
+ case 216:
+ C375.m(context, binder);
+ C376.m(context, binder);
+ C377.m(context, binder);
+ C378.m(context, binder);
+ C379.m(context, binder);
+ C380.m(context, binder);
+ C381.m(context, binder);
+ C382.m(context, binder);
+ return;
+ case 217:
+ C383.m(context, binder);
+ return;
+ case 218:
+ C384.m(context, binder);
+ return;
+ case 219:
+ C385.m(context, binder);
+ return;
+ case 220:
+ C386.m(context, binder);
+ return;
+ case 221:
+ C387.m(context, binder);
+ return;
+ case 222:
+ C388.m(context, binder);
+ return;
+ case 223:
+ C389.m(context, binder);
+ return;
+ case 224:
+ C390.m(context, binder);
+ return;
+ case 225:
+ C391.m(context, binder);
+ return;
+ case 226:
+ C392.m(context, binder);
+ return;
+ case 227:
+ C393.m(context, binder);
+ C394.m(context, binder);
+ return;
+ case 228:
+ C395.m(context, binder);
+ return;
+ case 229:
+ C396.m(context, binder);
+ return;
+ case 230:
+ C397.m(context, binder);
+ return;
+ case 231:
+ C398.m(context, binder);
+ return;
+ case 232:
+ C399.m(context, binder);
+ return;
+ case 233:
+ C400.m(context, binder);
+ return;
+ case 234:
+ C401.m(context, binder);
+ return;
+ case 235:
+ C402.m(context, binder);
+ return;
+ case 236:
+ C403.m(context, binder);
+ return;
+ case 237:
+ C404.m(context, binder);
+ return;
+ case 238:
+ C405.m(context, binder);
+ return;
+ case 239:
+ C406.m(context, binder);
+ return;
+ case 240:
+ C407.m(context, binder);
+ return;
+ case 241:
+ C408.m(context, binder);
+ return;
+ case 242:
+ C409.m(context, binder);
+ return;
+ case 243:
+ C410.m(context, binder);
+ return;
+ case 244:
+ C411.m(context, binder);
+ return;
+ case 245:
+ C412.m(context, binder);
+ return;
+ case 246:
+ C413.m(context, binder);
+ return;
+ case 247:
+ C414.m(context, binder);
+ return;
+ case 248:
+ C415.m(context, binder);
+ return;
+ case 249:
+ C416.m(context, binder);
+ return;
+ case 250:
+ C417.m(context, binder);
+ return;
+ case 251:
+ C418.m(context, binder);
+ return;
+ case 252:
+ C419.m(context, binder);
+ return;
+ case 253:
+ C420.m(context, binder);
+ return;
+ case 254:
+ C421.m(context, binder);
+ return;
+ case 255:
+ C422.m(context, binder);
+ return;
+ case 256:
+ C423.m(context, binder);
+ return;
+ case 257:
+ C424.m(context, binder);
+ return;
+ case 258:
+ C425.m(context, binder);
+ return;
+ case 259:
+ C426.m(context, binder);
+ return;
+ case 260:
+ C427.m(context, binder);
+ return;
+ case 261:
+ C428.m(context, binder);
+ return;
+ case 262:
+ C429.m(context, binder);
+ return;
+ case 263:
+ C430.m(context, binder);
+ return;
+ case 264:
+ C431.m(context, binder);
+ return;
+ case 265:
+ C432.m(context, binder);
+ return;
+ case 266:
+ C433.m(context, binder);
+ return;
+ case 267:
+ C434.m(context, binder);
+ C435.m(context, binder);
+ C436.m(context, binder);
+ C437.m(context, binder);
+ return;
+ case 268:
+ C438.m(context, binder);
+ return;
+ case 269:
+ C439.m(context, binder);
+ return;
+ case 270:
+ C440.m(context, binder);
+ return;
+ case 271:
+ C441.m(context, binder);
+ return;
+ case 272:
+ C442.m(context, binder);
+ return;
+ case 273:
+ C443.m(context, binder);
+ return;
+ case 274:
+ C444.m(context, binder);
+ return;
+ case 275:
+ C445.m(context, binder);
+ return;
+ case 276:
+ C446.m(context, binder);
+ return;
+ case 277:
+ C447.m(context, binder);
+ return;
+ case 278:
+ C448.m(context, binder);
+ return;
+ case 279:
+ C449.m(context, binder);
+ return;
+ case 280:
+ C450.m(context, binder);
+ return;
+ case 281:
+ C451.m(context, binder);
+ return;
+ case 282:
+ C452.m(context, binder);
+ return;
+ case 283:
+ C453.m(context, binder);
+ return;
+ case 284:
+ C454.m(context, binder);
+ return;
+ case 285:
+ C455.m(context, binder);
+ return;
+ case 286:
+ C456.m(context, binder);
+ return;
+ case 287:
+ C457.m(context, binder);
+ return;
+ case 288:
+ C458.m(context, binder);
+ return;
+ case 289:
+ C459.m(context, binder);
+ return;
+ case 290:
+ C460.m(context, binder);
+ return;
+ case 291:
+ C461.m(context, binder);
+ return;
+ case 292:
+ C462.m(context, binder);
+ return;
+ case 293:
+ C463.m(context, binder);
+ return;
+ case 294:
+ C464.m(context, binder);
+ return;
+ case 295:
+ C465.m(context, binder);
+ return;
+ case 296:
+ C466.m(context, binder);
+ return;
+ case 297:
+ C467.m(context, binder);
+ return;
+ case 298:
+ C468.m(context, binder);
+ return;
+ case 299:
+ C469.m(context, binder);
+ return;
+ case 300:
+ C470.m(context, binder);
+ return;
+ case 301:
+ C471.m(context, binder);
+ return;
+ case 302:
+ C472.m(context, binder);
+ return;
+ case 303:
+ C473.m(context, binder);
+ return;
+ case 304:
+ C474.m(context, binder);
+ return;
+ case 305:
+ C475.m(context, binder);
+ return;
+ case 306:
+ C476.m(context, binder);
+ return;
+ case 307:
+ C477.m(context, binder);
+ return;
+ case 308:
+ C478.m(context, binder);
+ return;
+ case 309:
+ C479.m(context, binder);
+ return;
+ case 310:
+ C480.m(context, binder);
+ return;
+ case 311:
+ C481.m(context, binder);
+ return;
+ case 312:
+ C482.m(context, binder);
+ return;
+ case 313:
+ C483.m(context, binder);
+ return;
+ case 314:
+ C484.m(context, binder);
+ return;
+ case 315:
+ C485.m(context, binder);
+ return;
+ case 316:
+ C486.m(context, binder);
+ return;
+ case 317:
+ C487.m(context, binder);
+ return;
+ case 318:
+ C488.m(context, binder);
+ return;
+ case 319:
+ C489.m(context, binder);
+ return;
+ case 320:
+ C490.m(context, binder);
+ return;
+ case 321:
+ C491.m(context, binder);
+ C492.m(context, binder);
+ C493.m(context, binder);
+ C494.m(context, binder);
+ C495.m(context, binder);
+ C496.m(context, binder);
+ C497.m(context, binder);
+ C498.m(context, binder);
+ return;
+ case 322:
+ C499.m(context, binder);
+ return;
+ case 323:
+ C500.m(context, binder);
+ return;
+ case 324:
+ C501.m(context, binder);
+ return;
+ case 325:
+ C502.m(context, binder);
+ return;
+ case 326:
+ C503.m(context, binder);
+ return;
+ case 327:
+ C504.m(context, binder);
+ return;
+ case 328:
+ C505.m(context, binder);
+ return;
+ case 329:
+ C506.m(context, binder);
+ return;
+ case 330:
+ C507.m(context, binder);
+ return;
+ case 331:
+ C508.m(context, binder);
+ return;
+ case 332:
+ C509.m(context, binder);
+ return;
+ case 333:
+ C510.m(context, binder);
+ return;
+ case 334:
+ C511.m(context, binder);
+ return;
+ case 335:
+ C512.m(context, binder);
+ return;
+ case 336:
+ C513.m(context, binder);
+ return;
+ case 337:
+ C514.m(context, binder);
+ return;
+ case 338:
+ C515.m(context, binder);
+ return;
+ case 339:
+ C516.m(context, binder);
+ return;
+ case 340:
+ C517.m(context, binder);
+ return;
+ case 341:
+ C518.m(context, binder);
+ return;
+ case 342:
+ C519.m(context, binder);
+ return;
+ case 343:
+ C520.m(context, binder);
+ return;
+ case 344:
+ C255.mFactory(context, binder);
+ return;
+ case 345:
+ C522.m(context, binder);
+ return;
+ case 346:
+ C523.m(context, binder);
+ return;
+ case 347:
+ C524.m(context, binder);
+ return;
+ case 348:
+ C525.m(context, binder);
+ return;
+ case 349:
+ C526.m(context, binder);
+ return;
+ case 350:
+ C527.m(context, binder);
+ return;
+ case 351:
+ C528.m(context, binder);
+ return;
+ case 352:
+ C529.m(context, binder);
+ return;
+ case 353:
+ C530.m(context, binder);
+ return;
+ case 354:
+ C531.m(context, binder);
+ return;
+ case 355:
+ C532.m(context, binder);
+ return;
+ case 356:
+ C533.m(context, binder);
+ return;
+ case 357:
+ C534.m(context, binder);
+ return;
+ case 358:
+ C535.m(context, binder);
+ return;
+ case 359:
+ C536.m(context, binder);
+ return;
+ case 360:
+ C537.m(context, binder);
+ return;
+ case 361:
+ C538.m(context, binder);
+ return;
+ case 362:
+ C539.m(context, binder);
+ return;
+ case 363:
+ C540.m(context, binder);
+ return;
+ case 364:
+ C541.m(context, binder);
+ return;
+ case 365:
+ C542.m(context, binder);
+ return;
+ case 366:
+ C543.m(context, binder);
+ return;
+ case 367:
+ C544.m(context, binder);
+ C545.m(context, binder);
+ return;
+ case 368:
+ C546.m(context, binder);
+ return;
+ case 369:
+ C547.m(context, binder);
+ return;
+ case 370:
+ C548.m(context, binder);
+ return;
+ case 371:
+ C549.m(context, binder);
+ return;
+ case 372:
+ C550.m(context, binder);
+ return;
+ case 373:
+ C551.m(context, binder);
+ return;
+ case 374:
+ C552.m(context, binder);
+ return;
+ case 375:
+ C553.m(context, binder);
+ return;
+ case 376:
+ C554.m(context, binder);
+ return;
+ case 377:
+ C555.m(context, binder);
+ return;
+ case 378:
+ C556.m(context, binder);
+ return;
+ case 379:
+ C557.m(context, binder);
+ return;
+ case 380:
+ C5.mImpl(context, binder);
+ return;
+ case 381:
+ C559.m(context, binder);
+ return;
+ case 382:
+ C560.m(context, binder);
+ return;
+ case 383:
+ C561.m(context, binder);
+ return;
+ case 384:
+ C562.m(context, binder);
+ return;
+ case 385:
+ C563.m(context, binder);
+ return;
+ case 386:
+ C564.m(context, binder);
+ return;
+ case 387:
+ C565.m(context, binder);
+ return;
+ case 388:
+ C566.m(context, binder);
+ return;
+ case 389:
+ C567.m(context, binder);
+ return;
+ case 390:
+ C568.m(context, binder);
+ return;
+ case 391:
+ C569.m(context, binder);
+ return;
+ case 392:
+ C570.m(context, binder);
+ return;
+ case 393:
+ C571.m(context, binder);
+ return;
+ case 394:
+ C572.m(context, binder);
+ return;
+ case 395:
+ C573.m(context, binder);
+ return;
+ case 396:
+ C574.m(context, binder);
+ return;
+ case 397:
+ C575.m(context, binder);
+ return;
+ case 398:
+ C576.m(context, binder);
+ return;
+ case 399:
+ C577.m(context, binder);
+ return;
+ case 400:
+ C578.m(context, binder);
+ return;
+ case 401:
+ C579.m(context, binder);
+ return;
+ case 402:
+ C580.m(context, binder);
+ return;
+ case 403:
+ C581.m(context, binder);
+ return;
+ case 404:
+ C582.m(context, binder);
+ return;
+ case 405:
+ C583.m(context, binder);
+ return;
+ case 406:
+ C584.m(context, binder);
+ C585.m(context, binder);
+ C586.m(context, binder);
+ C587.m(context, binder);
+ C588.m(context, binder);
+ C589.m(context, binder);
+ C590.m(context, binder);
+ C591.m(context, binder);
+ C592.m(context, binder);
+ C593.m(context, binder);
+ C594.m(context, binder);
+ return;
+ case 407:
+ C595.m(context, binder);
+ return;
+ case 408:
+ C596.m(context, binder);
+ return;
+ case 409:
+ C597.m(context, binder);
+ C598.m(context, binder);
+ return;
+ case 410:
+ C599.m(context, binder);
+ return;
+ case 411:
+ C600.m(context, binder);
+ return;
+ case 412:
+ C601.m(context, binder);
+ return;
+ case 413:
+ C602.m(context, binder);
+ return;
+ case 414:
+ C603.m(context, binder);
+ return;
+ case 415:
+ C604.m(context, binder);
+ return;
+ case 416:
+ C605.m(context, binder);
+ return;
+ case 417:
+ C606.m(context, binder);
+ return;
+ case 418:
+ C607.m(context, binder);
+ return;
+ case 419:
+ C608.m(context, binder);
+ return;
+ case 420:
+ C609.m(context, binder);
+ return;
+ case 421:
+ C610.m(context, binder);
+ return;
+ case 422:
+ C611.m(context, binder);
+ return;
+ case 423:
+ C612.m(context, binder);
+ return;
+ case 424:
+ C613.m(context, binder);
+ return;
+ case 425:
+ C614.m(context, binder);
+ C615.m(context, binder);
+ C616.m(context, binder);
+ return;
+ case 426:
+ C617.m(context, binder);
+ return;
+ case 427:
+ C618.m(context, binder);
+ return;
+ case 428:
+ C619.m(context, binder);
+ return;
+ case 429:
+ C620.m(context, binder);
+ return;
+ case 430:
+ C621.m(context, binder);
+ return;
+ case 431:
+ C622.m(context, binder);
+ return;
+ case 432:
+ C623.m(context, binder);
+ return;
+ case 433:
+ C624.m(context, binder);
+ return;
+ case 434:
+ C625.m(context, binder);
+ return;
+ case 435:
+ C626.m(context, binder);
+ return;
+ case 436:
+ C627.m(context, binder);
+ return;
+ case 437:
+ C628.m(context, binder);
+ return;
+ case 438:
+ C629.m(context, binder);
+ return;
+ case 439:
+ C630.m(context, binder);
+ return;
+ case 440:
+ C631.m(context, binder);
+ return;
+ case 441:
+ C632.m(context, binder);
+ return;
+ case 442:
+ C633.m(context, binder);
+ return;
+ case 443:
+ C634.m(context, binder);
+ return;
+ case 444:
+ C635.m(context, binder);
+ return;
+ case 445:
+ C636.m(context, binder);
+ return;
+ case 446:
+ C637.m(context, binder);
+ return;
+ case 447:
+ C638.m(context, binder);
+ return;
+ case 448:
+ C639.m(context, binder);
+ return;
+ case 449:
+ C640.m(context, binder);
+ return;
+ case 450:
+ C641.m(context, binder);
+ return;
+ case 451:
+ C642.m(context, binder);
+ return;
+ case 452:
+ C643.m(context, binder);
+ return;
+ case 453:
+ C644.m(context, binder);
+ return;
+ case 454:
+ C645.m(context, binder);
+ return;
+ case 455:
+ C646.m(context, binder);
+ return;
+ case 456:
+ C647.m(context, binder);
+ return;
+ case 457:
+ C648.m(context, binder);
+ return;
+ case 458:
+ C649.m(context, binder);
+ return;
+ case 459:
+ C650.m(context, binder);
+ return;
+ case 460:
+ C651.m(context, binder);
+ return;
+ case 461:
+ C652.m(context, binder);
+ return;
+ case 462:
+ C653.m(context, binder);
+ return;
+ case 463:
+ C654.m(context, binder);
+ return;
+ case 464:
+ C655.m(context, binder);
+ return;
+ case 465:
+ C656.m(context, binder);
+ return;
+ case 466:
+ C657.m(context, binder);
+ return;
+ case 467:
+ C658.m(context, binder);
+ return;
+ case 468:
+ C659.m(context, binder);
+ return;
+ case 469:
+ C660.m(context, binder);
+ return;
+ case 470:
+ C661.m(context, binder);
+ return;
+ case 471:
+ C90.mReport_Factory(context, binder);
+ return;
+ case 472:
+ C663.m(context, binder);
+ return;
+ case 473:
+ C664.m(context, binder);
+ return;
+ case 474:
+ C665.m(context, binder);
+ return;
+ case 475:
+ C666.m(context, binder);
+ return;
+ case 476:
+ C667.m(context, binder);
+ return;
+ case 477:
+ C668.m(context, binder);
+ return;
+ case 478:
+ C105.m_InMemoryScanner(context, binder);
+ return;
+ case 479:
+ C670.m(context, binder);
+ C671.m(context, binder);
+ return;
+ case 480:
+ C672.m(context, binder);
+ return;
+ case 481:
+ C673.m(context, binder);
+ return;
+ case 482:
+ C674.m(context, binder);
+ return;
+ case 483:
+ C675.m(context, binder);
+ return;
+ case 484:
+ C676.m(context, binder);
+ return;
+ case 485:
+ C677.m(context, binder);
+ return;
+ case 486:
+ C678.m(context, binder);
+ return;
+ case 487:
+ C679.m(context, binder);
+ C680.m(context, binder);
+ C681.m(context, binder);
+ C682.m(context, binder);
+ C683.m(context, binder);
+ C684.m(context, binder);
+ C685.m(context, binder);
+ return;
+ case 488:
+ C686.m(context, binder);
+ return;
+ case 489:
+ C687.m(context, binder);
+ return;
+ case 490:
+ C688.m(context, binder);
+ return;
+ case 491:
+ C689.m(context, binder);
+ return;
+ case 492:
+ C690.m(context, binder);
+ return;
+ case 493:
+ C691.m(context, binder);
+ C692.m(context, binder);
+ C693.m(context, binder);
+ C694.m(context, binder);
+ C695.m(context, binder);
+ C696.m(context, binder);
+ C697.m(context, binder);
+ C698.m(context, binder);
+ C699.m(context, binder);
+ C700.m(context, binder);
+ C701.m(context, binder);
+ C702.m(context, binder);
+ C703.m(context, binder);
+ C704.m(context, binder);
+ C705.m(context, binder);
+ C706.m(context, binder);
+ C707.m(context, binder);
+ C708.m(context, binder);
+ C709.m(context, binder);
+ C710.m(context, binder);
+ C711.m(context, binder);
+ C712.m(context, binder);
+ C713.m(context, binder);
+ C714.m(context, binder);
+ C715.m(context, binder);
+ C716.m(context, binder);
+ C717.m(context, binder);
+ C718.m(context, binder);
+ C719.m(context, binder);
+ C720.m(context, binder);
+ return;
+ case 494:
+ C721.m(context, binder);
+ C722.m(context, binder);
+ C723.m(context, binder);
+ return;
+ case 495:
+ C724.m(context, binder);
+ return;
+ case 496:
+ C725.m(context, binder);
+ return;
+ case 497:
+ C726.m(context, binder);
+ return;
+ case 498:
+ C727.m(context, binder);
+ return;
+ case 499:
+ C728.m(context, binder);
+ return;
+ case 500:
+ C729.m(context, binder);
+ return;
+ case 501:
+ C730.m(context, binder);
+ return;
+ case 502:
+ C731.m(context, binder);
+ return;
+ case 503:
+ C732.m(context, binder);
+ return;
+ case 504:
+ C733.m(context, binder);
+ return;
+ case 505:
+ C734.m(context, binder);
+ return;
+ case 506:
+ C735.m(context, binder);
+ return;
+ case 507:
+ C736.m(context, binder);
+ return;
+ case 508:
+ C737.m(context, binder);
+ return;
+ case 509:
+ C738.m(context, binder);
+ return;
+ case 510:
+ C739.m(context, binder);
+ return;
+ case 511:
+ C740.m(context, binder);
+ return;
+ case 512:
+ C741.m(context, binder);
+ return;
+ case 513:
+ C742.m(context, binder);
+ return;
+ case 514:
+ C743.m(context, binder);
+ return;
+ case 515:
+ C744.m(context, binder);
+ return;
+ case 516:
+ C745.m(context, binder);
+ return;
+ case 517:
+ C746.m(context, binder);
+ return;
+ case 518:
+ C747.m(context, binder);
+ return;
+ case 519:
+ C748.m(context, binder);
+ return;
+ case 520:
+ C749.m(context, binder);
+ return;
+ case 521:
+ C750.m(context, binder);
+ C751.m(context, binder);
+ C752.m(context, binder);
+ C753.m(context, binder);
+ C754.m(context, binder);
+ C755.m(context, binder);
+ C756.m(context, binder);
+ C757.m(context, binder);
+ C758.m(context, binder);
+ C759.m(context, binder);
+ C760.m(context, binder);
+ C761.m(context, binder);
+ return;
+ case 522:
+ C762.m(context, binder);
+ return;
+ case 523:
+ C763.m(context, binder);
+ return;
+ case 524:
+ C764.m(context, binder);
+ return;
+ case 525:
+ C765.m(context, binder);
+ return;
+ case 526:
+ C766.m(context, binder);
+ return;
+ case 527:
+ C767.m(context, binder);
+ return;
+ case 528:
+ C768.m(context, binder);
+ return;
+ case 529:
+ C769.m(context, binder);
+ return;
+ case 530:
+ C770.m(context, binder);
+ return;
+ case 531:
+ C771.m(context, binder);
+ return;
+ case 532:
+ C772.m(context, binder);
+ return;
+ case 533:
+ C773.m(context, binder);
+ return;
+ case 534:
+ C774.m(context, binder);
+ return;
+ case 535:
+ C775.m(context, binder);
+ return;
+ case 536:
+ C776.m(context, binder);
+ return;
+ case 537:
+ C777.m(context, binder);
+ return;
+ case 538:
+ C778.m(context, binder);
+ return;
+ case 539:
+ C779.m(context, binder);
+ return;
+ case 540:
+ C780.m(context, binder);
+ return;
+ case 541:
+ C781.m(context, binder);
+ return;
+ case 542:
+ C90.mApi(context, binder);
+ return;
+ case 543:
+ C783.m(context, binder);
+ return;
+ case 544:
+ C784.m(context, binder);
+ return;
+ case 545:
+ C785.m(context, binder);
+ return;
+ case 546:
+ C786.m(context, binder);
+ return;
+ case 547:
+ C787.m(context, binder);
+ return;
+ case 548:
+ C788.m(context, binder);
+ return;
+ case 549:
+ C789.m(context, binder);
+ return;
+ case 550:
+ C790.m(context, binder);
+ return;
+ case 551:
+ C791.m(context, binder);
+ return;
+ case 552:
+ C792.m(context, binder);
+ return;
+ case 553:
+ C793.m(context, binder);
+ return;
+ case 554:
+ C794.m(context, binder);
+ return;
+ case 555:
+ C795.m(context, binder);
+ C796.m(context, binder);
+ return;
+ case 556:
+ C797.m(context, binder);
+ return;
+ case 557:
+ C798.m(context, binder);
+ return;
+ case 558:
+ C799.m(context, binder);
+ return;
+ case 559:
+ C800.m(context, binder);
+ return;
+ case 560:
+ C801.m(context, binder);
+ return;
+ case 561:
+ C802.m(context, binder);
+ return;
+ case 562:
+ C803.m(context, binder);
+ C804.m(context, binder);
+ C805.m(context, binder);
+ C806.m(context, binder);
+ return;
+ case 563:
+ C807.m(context, binder);
+ return;
+ case 564:
+ C808.m(context, binder);
+ return;
+ case 565:
+ C809.m(context, binder);
+ return;
+ case 566:
+ C810.m(context, binder);
+ return;
+ case 567:
+ C811.m(context, binder);
+ return;
+ case 568:
+ C812.m(context, binder);
+ return;
+ case 569:
+ C813.m(context, binder);
+ C814.m(context, binder);
+ C815.m(context, binder);
+ C816.m(context, binder);
+ C817.m(context, binder);
+ C818.m(context, binder);
+ C819.m(context, binder);
+ C820.m(context, binder);
+ C821.m(context, binder);
+ return;
+ case 570:
+ C822.m(context, binder);
+ C823.m(context, binder);
+ return;
+ case 571:
+ C824.m(context, binder);
+ return;
+ case 572:
+ C825.m(context, binder);
+ return;
+ case 573:
+ C826.m(context, binder);
+ return;
+ case 574:
+ C827.m(context, binder);
+ return;
+ case 575:
+ C828.m(context, binder);
+ return;
+ case 576:
+ C829.m(context, binder);
+ return;
+ case 577:
+ C830.m(context, binder);
+ return;
+ case 578:
+ C831.m(context, binder);
+ return;
+ case 579:
+ C832.m(context, binder);
+ return;
+ case 580:
+ C833.m(context, binder);
+ return;
+ case 581:
+ C834.m(context, binder);
+ return;
+ case 582:
+ C835.m(context, binder);
+ return;
+ case 583:
+ C836.m(context, binder);
+ return;
+ case 584:
+ C837.m(context, binder);
+ return;
+ case 585:
+ C838.m(context, binder);
+ return;
+ case 586:
+ C105.m_Scanner(context, binder);
+ C840.m(context, binder);
+ return;
+ case 587:
+ C94.mImpl(context, binder);
+ return;
+ case 588:
+ C842.m(context, binder);
+ C843.m(context, binder);
+ C844.m(context, binder);
+ C845.m(context, binder);
+ return;
+ case 589:
+ C846.m(context, binder);
+ return;
+ case 590:
+ C847.m(context, binder);
+ return;
+ case 591:
+ C848.m(context, binder);
+ return;
+ case 592:
+ C849.m(context, binder);
+ return;
+ case 593:
+ C850.m(context, binder);
+ return;
+ case 594:
+ C851.m(context, binder);
+ return;
+ case 595:
+ C852.m(context, binder);
+ return;
+ case 596:
+ C853.m(context, binder);
+ return;
+ case 597:
+ C854.m(context, binder);
+ return;
+ case 598:
+ C855.m(context, binder);
+ return;
+ case 599:
+ C856.m(context, binder);
+ return;
+ case 600:
+ C857.m(context, binder);
+ return;
+ case 601:
+ C858.m(context, binder);
+ return;
+ case 602:
+ C859.m(context, binder);
+ return;
+ case 603:
+ C860.m(context, binder);
+ return;
+ case 604:
+ C861.m(context, binder);
+ return;
+ case 605:
+ C862.m(context, binder);
+ return;
+ case 606:
+ C863.m(context, binder);
+ return;
+ case 607:
+ C864.m(context, binder);
+ return;
+ case 608:
+ C865.m(context, binder);
+ return;
+ case 609:
+ C866.m(context, binder);
+ return;
+ case 610:
+ C867.m(context, binder);
+ return;
+ case 611:
+ C868.m(context, binder);
+ return;
+ case 612:
+ C869.m(context, binder);
+ return;
+ case 613:
+ C870.m(context, binder);
+ return;
+ case 614:
+ C871.m(context, binder);
+ return;
+ case 615:
+ C872.m(context, binder);
+ return;
+ case 616:
+ C873.m(context, binder);
+ return;
+ case 617:
+ C874.m(context, binder);
+ return;
+ case 618:
+ C875.m(context, binder);
+ return;
+ case 619:
+ C876.m(context, binder);
+ return;
+ case 620:
+ C877.m(context, binder);
+ return;
+ case 621:
+ C878.m(context, binder);
+ return;
+ case 622:
+ C879.m(context, binder);
+ C880.m(context, binder);
+ return;
+ case 623:
+ C881.m(context, binder);
+ return;
+ case 624:
+ C882.m(context, binder);
+ return;
+ case 625:
+ C883.m(context, binder);
+ return;
+ case 626:
+ C884.m(context, binder);
+ C885.m(context, binder);
+ C886.m(context, binder);
+ C887.m(context, binder);
+ C888.m(context, binder);
+ return;
+ case 627:
+ C889.m(context, binder);
+ return;
+ case 628:
+ C890.m(context, binder);
+ return;
+ case 629:
+ C891.m(context, binder);
+ return;
+ case 630:
+ C892.m(context, binder);
+ return;
+ case 631:
+ C893.m(context, binder);
+ return;
+ case 632:
+ C894.m(context, binder);
+ return;
+ case 633:
+ C895.m(context, binder);
+ return;
+ case 634:
+ C896.m(context, binder);
+ return;
+ case 635:
+ C897.m(context, binder);
+ return;
+ case 636:
+ C898.m(context, binder);
+ return;
+ case 637:
+ C899.m(context, binder);
+ return;
+ case 638:
+ C900.m(context, binder);
+ return;
+ case 639:
+ C901.m(context, binder);
+ return;
+ case 640:
+ C870.mImpl(context, binder);
+ return;
+ case 641:
+ C903.m(context, binder);
+ return;
+ case 642:
+ C904.m(context, binder);
+ return;
+ case 643:
+ C905.m(context, binder);
+ return;
+ case 644:
+ C906.m(context, binder);
+ C907.m(context, binder);
+ C908.m(context, binder);
+ C909.m(context, binder);
+ C910.m(context, binder);
+ return;
+ case 645:
+ C911.m(context, binder);
+ return;
+ case 646:
+ C912.m(context, binder);
+ C913.m(context, binder);
+ return;
+ case 647:
+ C914.m(context, binder);
+ return;
+ case 648:
+ C915.m(context, binder);
+ return;
+ case 649:
+ C916.m(context, binder);
+ return;
+ case 650:
+ C917.m(context, binder);
+ C918.m(context, binder);
+ return;
+ case 651:
+ C919.m(context, binder);
+ return;
+ case 652:
+ C920.m(context, binder);
+ return;
+ case 653:
+ C921.m(context, binder);
+ return;
+ case 654:
+ C922.m(context, binder);
+ return;
+ case 655:
+ C923.m(context, binder);
+ C924.m(context, binder);
+ C925.m(context, binder);
+ C926.m(context, binder);
+ C927.m(context, binder);
+ C928.m(context, binder);
+ C929.m(context, binder);
+ C930.m(context, binder);
+ C931.m(context, binder);
+ C932.m(context, binder);
+ C933.m(context, binder);
+ C934.m(context, binder);
+ C935.m(context, binder);
+ return;
+ case 656:
+ C936.m(context, binder);
+ return;
+ case 657:
+ C937.m(context, binder);
+ return;
+ case 658:
+ C938.m(context, binder);
+ return;
+ case 659:
+ C939.m(context, binder);
+ return;
+ case 660:
+ C940.m(context, binder);
+ return;
+ case 661:
+ C941.m(context, binder);
+ return;
+ case 662:
+ C942.m(context, binder);
+ return;
+ case 663:
+ C943.m(context, binder);
+ return;
+ case 664:
+ C944.m(context, binder);
+ return;
+ case 665:
+ C945.m(context, binder);
+ return;
+ case 666:
+ C946.m(context, binder);
+ return;
+ case 667:
+ C947.m(context, binder);
+ return;
+ case 668:
+ C948.m(context, binder);
+ return;
+ case 669:
+ C949.m(context, binder);
+ return;
+ case 670:
+ C950.m(context, binder);
+ return;
+ case 671:
+ C951.m(context, binder);
+ return;
+ case 672:
+ C952.m(context, binder);
+ C953.m(context, binder);
+ C954.m(context, binder);
+ C955.m(context, binder);
+ return;
+ case 673:
+ C956.m(context, binder);
+ return;
+ case 674:
+ C957.m(context, binder);
+ C958.m(context, binder);
+ return;
+ case 675:
+ C959.m(context, binder);
+ return;
+ case 676:
+ C960.m(context, binder);
+ C961.m(context, binder);
+ return;
+ case 677:
+ C962.m(context, binder);
+ return;
+ case 678:
+ C963.m(context, binder);
+ return;
+ case 679:
+ C964.m(context, binder);
+ return;
+ case 680:
+ C965.m(context, binder);
+ return;
+ case 681:
+ C966.m(context, binder);
+ return;
+ case 682:
+ C967.m(context, binder);
+ return;
+ case 683:
+ C968.m(context, binder);
+ return;
+ case 684:
+ C969.m(context, binder);
+ return;
+ case 685:
+ C970.m(context, binder);
+ return;
+ case 686:
+ C971.m(context, binder);
+ C972.m(context, binder);
+ return;
+ case 687:
+ C973.m(context, binder);
+ return;
+ case 688:
+ C974.m(context, binder);
+ return;
+ case 689:
+ C975.m(context, binder);
+ return;
+ case 690:
+ C976.m(context, binder);
+ return;
+ case 691:
+ C977.m(context, binder);
+ return;
+ case 692:
+ C978.m(context, binder);
+ return;
+ case 693:
+ C979.m(context, binder);
+ return;
+ case 694:
+ C980.m(context, binder);
+ return;
+ case 695:
+ C981.m(context, binder);
+ return;
+ case 696:
+ C982.m(context, binder);
+ return;
+ case 697:
+ C983.m(context, binder);
+ C984.m(context, binder);
+ C985.m(context, binder);
+ return;
+ case 698:
+ C986.m(context, binder);
+ return;
+ case 699:
+ C987.m(context, binder);
+ return;
+ case 700:
+ C988.m(context, binder);
+ return;
+ case 701:
+ C989.m(context, binder);
+ return;
+ case 702:
+ C990.m(context, binder);
+ return;
+ case 703:
+ C991.m(context, binder);
+ return;
+ case 704:
+ C992.m(context, binder);
+ return;
+ case 705:
+ C993.m(context, binder);
+ return;
+ case 706:
+ C994.m(context, binder);
+ return;
+ case 707:
+ C995.m(context, binder);
+ return;
+ case 708:
+ C996.m(context, binder);
+ return;
+ case 709:
+ C997.m(context, binder);
+ return;
+ case 710:
+ C998.m(context, binder);
+ return;
+ case 711:
+ C999.m(context, binder);
+ return;
+ case 712:
+ C1000.m(context, binder);
+ return;
+ case 713:
+ C1001.m(context, binder);
+ return;
+ case 714:
+ C1002.m(context, binder);
+ return;
+ case 715:
+ C1003.m(context, binder);
+ return;
+ case 716:
+ C1004.m(context, binder);
+ return;
+ case 717:
+ C1005.m(context, binder);
+ return;
+ case 718:
+ C1006.m(context, binder);
+ return;
+ case 719:
+ C1007.m(context, binder);
+ return;
+ case 720:
+ C1008.m(context, binder);
+ return;
+ case 721:
+ C1009.m(context, binder);
+ return;
+ case 722:
+ C1010.m(context, binder);
+ return;
+ case 723:
+ C1011.m(context, binder);
+ return;
+ case 724:
+ C1012.m(context, binder);
+ return;
+ case 725:
+ C1013.m(context, binder);
+ return;
+ case 726:
+ C1014.m(context, binder);
+ return;
+ case 727:
+ C1015.m(context, binder);
+ return;
+ case 728:
+ C577.mDebug(context, binder);
+ return;
+ case 729:
+ C1017.m(context, binder);
+ return;
+ case 730:
+ C1018.m(context, binder);
+ return;
+ case 731:
+ C1019.m(context, binder);
+ return;
+ case 732:
+ C1020.m(context, binder);
+ return;
+ case 733:
+ C1021.m(context, binder);
+ return;
+ case 734:
+ C1022.m(context, binder);
+ return;
+ case 735:
+ C1023.m(context, binder);
+ return;
+ case 736:
+ C1024.m(context, binder);
+ return;
+ case 737:
+ C1025.m(context, binder);
+ return;
+ case 738:
+ C1026.m(context, binder);
+ return;
+ case 739:
+ C1027.m(context, binder);
+ return;
+ case 740:
+ C1028.m(context, binder);
+ return;
+ case 741:
+ C1029.m(context, binder);
+ return;
+ case 742:
+ C1030.m(context, binder);
+ return;
+ case 743:
+ C1031.m(context, binder);
+ return;
+ case 744:
+ C1032.m(context, binder);
+ return;
+ case 745:
+ C1033.m(context, binder);
+ return;
+ case 746:
+ C1034.m(context, binder);
+ return;
+ case 747:
+ C1035.m(context, binder);
+ return;
+ case 748:
+ C1036.m(context, binder);
+ C1037.m(context, binder);
+ return;
+ case 749:
+ C1038.m(context, binder);
+ C1039.m(context, binder);
+ C1040.m(context, binder);
+ C1041.m(context, binder);
+ return;
+ case 750:
+ C1042.m(context, binder);
+ return;
+ case 751:
+ C1043.m(context, binder);
+ return;
+ case 752:
+ C1044.m(context, binder);
+ return;
+ case 753:
+ C1045.m(context, binder);
+ return;
+ case 754:
+ C1046.m(context, binder);
+ return;
+ case 755:
+ C1047.m(context, binder);
+ return;
+ case 756:
+ C848.mMap(context, binder);
+ return;
+ case 757:
+ C1049.m(context, binder);
+ return;
+ case 758:
+ C1050.m(context, binder);
+ return;
+ case 759:
+ C1051.m(context, binder);
+ return;
+ case 760:
+ C1052.m(context, binder);
+ return;
+ case 761:
+ C1053.m(context, binder);
+ return;
+ case 762:
+ C1054.m(context, binder);
+ return;
+ case 763:
+ C1055.m(context, binder);
+ return;
+ case 764:
+ C1056.m(context, binder);
+ return;
+ case 765:
+ C1057.m(context, binder);
+ return;
+ case 766:
+ C1058.m(context, binder);
+ return;
+ case 767:
+ C1059.m(context, binder);
+ return;
+ case 768:
+ C1060.m(context, binder);
+ return;
+ case 769:
+ C1061.m(context, binder);
+ return;
+ case 770:
+ C1062.m(context, binder);
+ return;
+ case 771:
+ C1063.m(context, binder);
+ return;
+ case 772:
+ C1064.m(context, binder);
+ return;
+ case 773:
+ C1065.m(context, binder);
+ return;
+ case 774:
+ C1066.m(context, binder);
+ return;
+ case 775:
+ C1067.m(context, binder);
+ return;
+ case 776:
+ C1068.m(context, binder);
+ return;
+ case 777:
+ C1069.m(context, binder);
+ return;
+ case 778:
+ C1070.m(context, binder);
+ return;
+ case 779:
+ C1071.m(context, binder);
+ return;
+ case 780:
+ C1072.m(context, binder);
+ return;
+ case 781:
+ C1073.m(context, binder);
+ C1074.m(context, binder);
+ C1075.m(context, binder);
+ C1076.m(context, binder);
+ C1077.m(context, binder);
+ C1078.m(context, binder);
+ C1079.m(context, binder);
+ C1080.m(context, binder);
+ return;
+ case 782:
+ C1081.m(context, binder);
+ return;
+ case 783:
+ C1082.m(context, binder);
+ return;
+ case 784:
+ C1083.m(context, binder);
+ return;
+ case 785:
+ C1084.m(context, binder);
+ return;
+ case 786:
+ C1085.m(context, binder);
+ return;
+ case 787:
+ C1086.m(context, binder);
+ return;
+ case 788:
+ C1087.m(context, binder);
+ return;
+ case 789:
+ C1088.m(context, binder);
+ return;
+ case 790:
+ C944.mManager(context, binder);
+ return;
+ case 791:
+ C1090.m(context, binder);
+ return;
+ case 792:
+ C1091.m(context, binder);
+ return;
+ case 793:
+ C1092.m(context, binder);
+ return;
+ case 794:
+ C1093.m(context, binder);
+ return;
+ case 795:
+ C1094.m(context, binder);
+ return;
+ case 796:
+ C1095.m(context, binder);
+ return;
+ case 797:
+ C1096.m(context, binder);
+ return;
+ case 798:
+ C1097.m(context, binder);
+ return;
+ case 799:
+ C1098.m(context, binder);
+ return;
+ case 800:
+ C1099.m(context, binder);
+ return;
+ case 801:
+ C1100.m(context, binder);
+ return;
+ case 802:
+ C1101.m(context, binder);
+ C1102.m(context, binder);
+ C1103.m(context, binder);
+ C1104.m(context, binder);
+ C1105.m(context, binder);
+ C1106.m(context, binder);
+ C1107.m(context, binder);
+ C1108.m(context, binder);
+ return;
+ case 803:
+ C1109.m(context, binder);
+ return;
+ case 804:
+ C1110.m(context, binder);
+ return;
+ case 805:
+ C1111.m(context, binder);
+ return;
+ case 806:
+ C1112.m(context, binder);
+ return;
+ case 807:
+ C1113.m(context, binder);
+ return;
+ case 808:
+ C1114.m(context, binder);
+ C1115.m(context, binder);
+ C1116.m(context, binder);
+ C1117.m(context, binder);
+ return;
+ case 809:
+ C1118.m(context, binder);
+ return;
+ case 810:
+ C1119.m(context, binder);
+ C1120.m(context, binder);
+ C1121.m(context, binder);
+ return;
+ case 811:
+ C1122.m(context, binder);
+ return;
+ case 812:
+ C1123.m(context, binder);
+ return;
+ case 813:
+ C1124.m(context, binder);
+ return;
+ case 814:
+ C1125.m(context, binder);
+ return;
+ case 815:
+ C1126.m(context, binder);
+ return;
+ case 816:
+ C1127.m(context, binder);
+ return;
+ case 817:
+ C1128.m(context, binder);
+ return;
+ case 818:
+ C1129.m(context, binder);
+ return;
+ case 819:
+ C1130.m(context, binder);
+ return;
+ case 820:
+ C1131.m(context, binder);
+ return;
+ case 821:
+ C1132.m(context, binder);
+ return;
+ case 822:
+ C1133.m(context, binder);
+ return;
+ case 823:
+ C1134.m(context, binder);
+ return;
+ case 824:
+ C1135.m(context, binder);
+ return;
+ case 825:
+ C1136.m(context, binder);
+ return;
+ case 826:
+ C1137.m(context, binder);
+ C1138.m(context, binder);
+ C1139.m(context, binder);
+ C1140.m(context, binder);
+ C1141.m(context, binder);
+ C1142.m(context, binder);
+ C1143.m(context, binder);
+ C1144.m(context, binder);
+ C1145.m(context, binder);
+ C1146.m(context, binder);
+ C1147.m(context, binder);
+ C1148.m(context, binder);
+ C1149.m(context, binder);
+ C1150.m(context, binder);
+ C1151.m(context, binder);
+ C1152.m(context, binder);
+ C1153.m(context, binder);
+ C1154.m(context, binder);
+ C1155.m(context, binder);
+ C1156.m(context, binder);
+ C1157.m(context, binder);
+ C1158.m(context, binder);
+ return;
+ case 827:
+ C1159.m(context, binder);
+ return;
+ case 828:
+ C457.mMap(context, binder);
+ return;
+ case 829:
+ C1161.m(context, binder);
+ C1162.m(context, binder);
+ C1163.m(context, binder);
+ C1164.m(context, binder);
+ C1165.m(context, binder);
+ C1166.m(context, binder);
+ C1167.m(context, binder);
+ C1168.m(context, binder);
+ C1169.m(context, binder);
+ C1170.m(context, binder);
+ C1171.m(context, binder);
+ C1172.m(context, binder);
+ C1173.m(context, binder);
+ C1174.m(context, binder);
+ C1175.m(context, binder);
+ C1176.m(context, binder);
+ C1177.m(context, binder);
+ C1178.m(context, binder);
+ C1179.m(context, binder);
+ C1180.m(context, binder);
+ C1181.m(context, binder);
+ C1181a.m(context, binder);
+ C1181b.m(context, binder);
+ return;
+ case 830:
+ C1184.m(context, binder);
+ return;
+ case 831:
+ C1185.m(context, binder);
+ return;
+ case 832:
+ C1186.m(context, binder);
+ return;
+ case 833:
+ C1187.m(context, binder);
+ return;
+ case 834:
+ C1188.m(context, binder);
+ return;
+ case 835:
+ C1189.m(context, binder);
+ return;
+ case 836:
+ C1190.m(context, binder);
+ return;
+ case 837:
+ C1191.m(context, binder);
+ return;
+ case 838:
+ C1192.m(context, binder);
+ return;
+ case 839:
+ C1193.m(context, binder);
+ return;
+ case 840:
+ C1194.m(context, binder);
+ return;
+ case 841:
+ C1195.m(context, binder);
+ return;
+ case 842:
+ C1196.m(context, binder);
+ return;
+ case 843:
+ C1197.m(context, binder);
+ return;
+ case 844:
+ C1198.m(context, binder);
+ return;
+ case 845:
+ C1199.m(context, binder);
+ return;
+ case 846:
+ C1200.m(context, binder);
+ return;
+ case 847:
+ C1201.m(context, binder);
+ return;
+ case 848:
+ C1202.m(context, binder);
+ return;
+ case 849:
+ C1203.m(context, binder);
+ return;
+ case 850:
+ C1204.m(context, binder);
+ return;
+ case 851:
+ C1205.m(context, binder);
+ return;
+ case 852:
+ C1206.m(context, binder);
+ return;
+ case 853:
+ C1207.m(context, binder);
+ return;
+ case 854:
+ C1208.m(context, binder);
+ return;
+ case 855:
+ C1209.m(context, binder);
+ return;
+ case 856:
+ C1210.m(context, binder);
+ return;
+ case 857:
+ C1211.m(context, binder);
+ return;
+ case 858:
+ C1212.m(context, binder);
+ return;
+ default:
+ return;
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.println("passed");
+ }
+
+ static boolean doThrow = false;
+}
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 2687f85b09..feabb2087e 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -211,52 +211,34 @@ From bottom
### Other threads (suspended) ###
################################
---------
-FinalizerDaemon
-<not printed>
----------
-FinalizerWatchdogDaemon
-<not printed>
----------
-HeapTaskDaemon
-<not printed>
----------
-ReferenceQueueDaemon
-<not printed>
----------
-Signal Catcher
-
----------
-Thread-10
+AllTraces Thread 0
---------
-Thread-11
+AllTraces Thread 1
---------
-Thread-12
+AllTraces Thread 2
---------
-Thread-13
+AllTraces Thread 3
---------
-Thread-4
+AllTraces Thread 4
---------
-Thread-5
+AllTraces Thread 5
---------
-Thread-6
+AllTraces Thread 6
---------
-Thread-7
+AllTraces Thread 7
---------
-Thread-8
+AllTraces Thread 8
---------
-Thread-9
-
----------
-main
+AllTraces Thread 9
---------
FinalizerDaemon
@@ -274,7 +256,10 @@ ReferenceQueueDaemon
Signal Catcher
---------
-Thread-10
+main
+
+---------
+AllTraces Thread 0
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -282,7 +267,7 @@ Thread-10
foo (IIILControlData;)I 0 19
---------
-Thread-11
+AllTraces Thread 1
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -290,7 +275,7 @@ Thread-11
foo (IIILControlData;)I 0 19
---------
-Thread-12
+AllTraces Thread 2
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -298,7 +283,7 @@ Thread-12
foo (IIILControlData;)I 0 19
---------
-Thread-13
+AllTraces Thread 3
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -306,7 +291,7 @@ Thread-13
foo (IIILControlData;)I 0 19
---------
-Thread-4
+AllTraces Thread 4
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -314,7 +299,7 @@ Thread-4
foo (IIILControlData;)I 0 19
---------
-Thread-5
+AllTraces Thread 5
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -322,7 +307,7 @@ Thread-5
foo (IIILControlData;)I 0 19
---------
-Thread-6
+AllTraces Thread 6
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -330,7 +315,7 @@ Thread-6
foo (IIILControlData;)I 0 19
---------
-Thread-7
+AllTraces Thread 7
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -338,7 +323,7 @@ Thread-7
foo (IIILControlData;)I 0 19
---------
-Thread-8
+AllTraces Thread 8
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -346,7 +331,7 @@ Thread-8
foo (IIILControlData;)I 0 19
---------
-Thread-9
+AllTraces Thread 9
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -354,13 +339,6 @@ Thread-9
foo (IIILControlData;)I 0 19
---------
-main
- getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
- printAll (I)V 0 73
- doTest ()V 102 57
- main ([Ljava/lang/String;)V 27 33
-
----------
FinalizerDaemon
<not printed>
---------
@@ -376,7 +354,14 @@ ReferenceQueueDaemon
Signal Catcher
---------
-Thread-10
+main
+ getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
+ printAll (I)V 0 73
+ doTest ()V 128 57
+ main ([Ljava/lang/String;)V 27 33
+
+---------
+AllTraces Thread 0
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -397,7 +382,7 @@ Thread-10
run ()V 4 45
---------
-Thread-11
+AllTraces Thread 1
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -418,7 +403,7 @@ Thread-11
run ()V 4 45
---------
-Thread-12
+AllTraces Thread 2
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -439,7 +424,7 @@ Thread-12
run ()V 4 45
---------
-Thread-13
+AllTraces Thread 3
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -460,7 +445,7 @@ Thread-13
run ()V 4 45
---------
-Thread-4
+AllTraces Thread 4
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -481,7 +466,7 @@ Thread-4
run ()V 4 45
---------
-Thread-5
+AllTraces Thread 5
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -502,7 +487,7 @@ Thread-5
run ()V 4 45
---------
-Thread-6
+AllTraces Thread 6
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -523,7 +508,7 @@ Thread-6
run ()V 4 45
---------
-Thread-7
+AllTraces Thread 7
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -544,7 +529,7 @@ Thread-7
run ()V 4 45
---------
-Thread-8
+AllTraces Thread 8
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -565,7 +550,7 @@ Thread-8
run ()V 4 45
---------
-Thread-9
+AllTraces Thread 9
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -586,10 +571,25 @@ Thread-9
run ()V 4 45
---------
+FinalizerDaemon
+<not printed>
+---------
+FinalizerWatchdogDaemon
+<not printed>
+---------
+HeapTaskDaemon
+<not printed>
+---------
+ReferenceQueueDaemon
+<not printed>
+---------
+Signal Catcher
+
+---------
main
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 73
- doTest ()V 107 59
+ doTest ()V 133 59
main ([Ljava/lang/String;)V 27 33
@@ -597,25 +597,25 @@ main
### Other select threads (suspended) ###
########################################
---------
-Thread-14
+ThreadListTraces Thread 0
---------
-Thread-16
+ThreadListTraces Thread 2
---------
-Thread-18
+ThreadListTraces Thread 4
---------
-Thread-20
+ThreadListTraces Thread 6
---------
-Thread-22
+ThreadListTraces Thread 8
---------
main
---------
-Thread-14
+ThreadListTraces Thread 0
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -623,7 +623,7 @@ Thread-14
foo (IIILControlData;)I 0 19
---------
-Thread-16
+ThreadListTraces Thread 2
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -631,7 +631,7 @@ Thread-16
foo (IIILControlData;)I 0 19
---------
-Thread-18
+ThreadListTraces Thread 4
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -639,7 +639,7 @@ Thread-18
foo (IIILControlData;)I 0 19
---------
-Thread-20
+ThreadListTraces Thread 6
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -647,7 +647,7 @@ Thread-20
foo (IIILControlData;)I 0 19
---------
-Thread-22
+ThreadListTraces Thread 8
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -658,11 +658,11 @@ Thread-22
main
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 66
- doTest ()V 96 52
+ doTest ()V 116 52
main ([Ljava/lang/String;)V 35 37
---------
-Thread-14
+ThreadListTraces Thread 0
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -683,7 +683,7 @@ Thread-14
run ()V 4 35
---------
-Thread-16
+ThreadListTraces Thread 2
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -704,7 +704,7 @@ Thread-16
run ()V 4 35
---------
-Thread-18
+ThreadListTraces Thread 4
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -725,7 +725,7 @@ Thread-18
run ()V 4 35
---------
-Thread-20
+ThreadListTraces Thread 6
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -746,7 +746,7 @@ Thread-20
run ()V 4 35
---------
-Thread-22
+ThreadListTraces Thread 8
wait ()V -1 -2
printOrWait (IILControlData;)V 24 45
baz (IIILControlData;)Ljava/lang/Object; 2 30
@@ -770,7 +770,7 @@ Thread-22
main
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 66
- doTest ()V 101 54
+ doTest ()V 121 54
main ([Ljava/lang/String;)V 35 37
diff --git a/test/911-get-stack-trace/src/AllTraces.java b/test/911-get-stack-trace/src/AllTraces.java
index adf6f38f5a..1d9aa96d6e 100644
--- a/test/911-get-stack-trace/src/AllTraces.java
+++ b/test/911-get-stack-trace/src/AllTraces.java
@@ -26,8 +26,8 @@ public class AllTraces {
System.out.println("################################");
// Also create an unstarted and a dead thread.
- RETAIN.add(new Thread());
- Thread deadThread = new Thread();
+ RETAIN.add(new Thread("UNSTARTED"));
+ Thread deadThread = new Thread("DEAD");
RETAIN.add(deadThread);
deadThread.start();
deadThread.join();
@@ -40,7 +40,7 @@ public class AllTraces {
Thread threads[] = new Thread[N];
for (int i = 0; i < N; i++) {
- Thread t = new Thread() {
+ Thread t = new Thread("AllTraces Thread " + i) {
public void run() {
Recurse.foo(4, 0, 0, data);
}
diff --git a/test/911-get-stack-trace/src/Frames.java b/test/911-get-stack-trace/src/Frames.java
index a1a11c3785..54d4165130 100644
--- a/test/911-get-stack-trace/src/Frames.java
+++ b/test/911-get-stack-trace/src/Frames.java
@@ -59,7 +59,7 @@ public class Frames {
System.out.println("################################");
final ControlData data = new ControlData();
data.waitFor = new Object();
- Thread t = new Thread() {
+ Thread t = new Thread("Frames doTestOtherThreadWait") {
public void run() {
Recurse.foo(4, 0, 0, data);
}
@@ -97,7 +97,7 @@ public class Frames {
System.out.println("### Other thread (live) ###");
System.out.println("###########################");
final ControlData data = new ControlData();
- Thread t = new Thread() {
+ Thread t = new Thread("Frames doTestOtherThreadBusyLoop") {
public void run() {
Recurse.foo(4, 0, 0, data);
}
diff --git a/test/911-get-stack-trace/src/OtherThread.java b/test/911-get-stack-trace/src/OtherThread.java
index 0748433a20..0a78523a91 100644
--- a/test/911-get-stack-trace/src/OtherThread.java
+++ b/test/911-get-stack-trace/src/OtherThread.java
@@ -21,7 +21,7 @@ public class OtherThread {
System.out.println("################################");
final ControlData data = new ControlData();
data.waitFor = new Object();
- Thread t = new Thread() {
+ Thread t = new Thread("OtherThread doTestOtherThreadWait") {
public void run() {
Recurse.foo(4, 0, 0, data);
}
@@ -54,7 +54,7 @@ public class OtherThread {
System.out.println("### Other thread (live) ###");
System.out.println("###########################");
final ControlData data = new ControlData();
- Thread t = new Thread() {
+ Thread t = new Thread("OtherThread doTestOtherThreadBusyLoop") {
public void run() {
Recurse.foo(4, 0, 0, data);
}
diff --git a/test/911-get-stack-trace/src/ThreadListTraces.java b/test/911-get-stack-trace/src/ThreadListTraces.java
index f66557f3bd..14868e9c36 100644
--- a/test/911-get-stack-trace/src/ThreadListTraces.java
+++ b/test/911-get-stack-trace/src/ThreadListTraces.java
@@ -30,7 +30,7 @@ public class ThreadListTraces {
Thread list[] = new Thread[N/2 + 1];
for (int i = 0; i < N; i++) {
- Thread t = new Thread() {
+ Thread t = new Thread("ThreadListTraces Thread " + i) {
public void run() {
Recurse.foo(4, 0, 0, data);
}
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index e659ea3bfb..3ccfe86bed 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -430,5 +430,145 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isLoadedClass(
return found ? JNI_TRUE : JNI_FALSE;
}
+class ClassLoadPrepareEquality {
+ public:
+ static constexpr const char* kClassName = "LMain$ClassE;";
+ static constexpr const char* kStorageFieldName = "STATIC";
+ static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;";
+ static constexpr const char* kStorageWeakFieldName = "WEAK";
+ static constexpr const char* kStorageWeakFieldSig = "Ljava/lang/ref/Reference;";
+ static constexpr const char* kWeakClassName = "java/lang/ref/WeakReference";
+ static constexpr const char* kWeakInitSig = "(Ljava/lang/Object;)V";
+ static constexpr const char* kWeakGetSig = "()Ljava/lang/Object;";
+
+ static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED,
+ jclass klass) {
+ std::string name = GetClassName(jenv, jni_env, klass);
+ if (name == kClassName) {
+ found_ = true;
+ stored_class_ = jni_env->NewGlobalRef(klass);
+ weakly_stored_class_ = jni_env->NewWeakGlobalRef(klass);
+ // The following is bad and relies on implementation details. But otherwise a test would be
+ // a lot more complicated.
+ local_stored_class_ = jni_env->NewLocalRef(klass);
+ // Store the value into a field in the heap.
+ SetOrCompare(jni_env, klass, true);
+ }
+ }
+
+ static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED,
+ jclass klass) {
+ std::string name = GetClassName(jenv, jni_env, klass);
+ if (name == kClassName) {
+ CHECK(stored_class_ != nullptr);
+ CHECK(jni_env->IsSameObject(stored_class_, klass));
+ CHECK(jni_env->IsSameObject(weakly_stored_class_, klass));
+ CHECK(jni_env->IsSameObject(local_stored_class_, klass));
+ // Look up the value in a field in the heap.
+ SetOrCompare(jni_env, klass, false);
+ compared_ = true;
+ }
+ }
+
+ static void SetOrCompare(JNIEnv* jni_env, jobject value, bool set) {
+ CHECK(storage_class_ != nullptr);
+
+ // Simple direct storage.
+ jfieldID field = jni_env->GetStaticFieldID(storage_class_, kStorageFieldName, kStorageFieldSig);
+ CHECK(field != nullptr);
+
+ if (set) {
+ jni_env->SetStaticObjectField(storage_class_, field, value);
+ CHECK(!jni_env->ExceptionCheck());
+ } else {
+ ScopedLocalRef<jobject> stored(jni_env, jni_env->GetStaticObjectField(storage_class_, field));
+ CHECK(jni_env->IsSameObject(value, stored.get()));
+ }
+
+ // Storage as a reference.
+ ScopedLocalRef<jclass> weak_ref_class(jni_env, jni_env->FindClass(kWeakClassName));
+ CHECK(weak_ref_class.get() != nullptr);
+ jfieldID weak_field = jni_env->GetStaticFieldID(storage_class_,
+ kStorageWeakFieldName,
+ kStorageWeakFieldSig);
+ CHECK(weak_field != nullptr);
+ if (set) {
+ // Create a WeakReference.
+ jmethodID weak_init = jni_env->GetMethodID(weak_ref_class.get(), "<init>", kWeakInitSig);
+ CHECK(weak_init != nullptr);
+ ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->NewObject(weak_ref_class.get(),
+ weak_init,
+ value));
+ CHECK(weak_obj.get() != nullptr);
+ jni_env->SetStaticObjectField(storage_class_, weak_field, weak_obj.get());
+ CHECK(!jni_env->ExceptionCheck());
+ } else {
+ // Check the reference value.
+ jmethodID get_referent = jni_env->GetMethodID(weak_ref_class.get(), "get", kWeakGetSig);
+ CHECK(get_referent != nullptr);
+ ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->GetStaticObjectField(storage_class_,
+ weak_field));
+ CHECK(weak_obj.get() != nullptr);
+ ScopedLocalRef<jobject> weak_referent(jni_env, jni_env->CallObjectMethod(weak_obj.get(),
+ get_referent));
+ CHECK(weak_referent.get() != nullptr);
+ CHECK(jni_env->IsSameObject(value, weak_referent.get()));
+ }
+ }
+
+ static void CheckFound() {
+ CHECK(found_);
+ CHECK(compared_);
+ }
+
+ static void Free(JNIEnv* env) {
+ if (stored_class_ != nullptr) {
+ env->DeleteGlobalRef(stored_class_);
+ DCHECK(weakly_stored_class_ != nullptr);
+ env->DeleteWeakGlobalRef(weakly_stored_class_);
+ // Do not attempt to delete the local ref. It will be out of date by now.
+ }
+ }
+
+ static jclass storage_class_;
+
+ private:
+ static jobject stored_class_;
+ static jweak weakly_stored_class_;
+ static jobject local_stored_class_;
+ static bool found_;
+ static bool compared_;
+};
+jclass ClassLoadPrepareEquality::storage_class_ = nullptr;
+jobject ClassLoadPrepareEquality::stored_class_ = nullptr;
+jweak ClassLoadPrepareEquality::weakly_stored_class_ = nullptr;
+jobject ClassLoadPrepareEquality::local_stored_class_ = nullptr;
+bool ClassLoadPrepareEquality::found_ = false;
+bool ClassLoadPrepareEquality::compared_ = false;
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setEqualityEventStorageClass(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ ClassLoadPrepareEquality::storage_class_ =
+ reinterpret_cast<jclass>(env->NewGlobalRef(klass));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadPrepareEqualityEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
+ EnableEvents(env,
+ b,
+ ClassLoadPrepareEquality::ClassLoadCallback,
+ ClassLoadPrepareEquality::ClassPrepareCallback);
+ if (b == JNI_FALSE) {
+ ClassLoadPrepareEquality::Free(env);
+ ClassLoadPrepareEquality::CheckFound();
+ env->DeleteGlobalRef(ClassLoadPrepareEquality::storage_class_);
+ ClassLoadPrepareEquality::storage_class_ = nullptr;
+ }
+}
+
} // namespace Test912Classes
} // namespace art
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index e3aceb9a5f..005074f8c1 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.ref.Reference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
import java.util.Arrays;
@@ -290,6 +291,8 @@ public class Main {
if (hasJit() && !isLoadedClass("Main$ClassD")) {
testClassEventsJit();
}
+
+ testClassLoadPrepareEquality();
}
private static void testClassEventsJit() throws Exception {
@@ -312,6 +315,16 @@ public class Main {
}
}
+ private static void testClassLoadPrepareEquality() throws Exception {
+ setEqualityEventStorageClass(ClassF.class);
+
+ enableClassLoadPrepareEqualityEvents(true);
+
+ Class.forName("Main$ClassE");
+
+ enableClassLoadPrepareEqualityEvents(false);
+ }
+
private static void printClassLoaderClasses(ClassLoader cl) {
for (;;) {
if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
@@ -383,6 +396,9 @@ public class Main {
private static native void enableClassLoadSeenEvents(boolean b);
private static native boolean hadLoadEvent();
+ private static native void setEqualityEventStorageClass(Class<?> c);
+ private static native void enableClassLoadPrepareEqualityEvents(boolean b);
+
private static class TestForNonInit {
public static double dummy = Math.random(); // So it can't be compile-time initialized.
}
@@ -409,6 +425,18 @@ public class Main {
static int x = 1;
}
+ public static class ClassE {
+ public void foo() {
+ }
+ public void bar() {
+ }
+ }
+
+ public static class ClassF {
+ public static Object STATIC = null;
+ public static Reference<Object> WEAK = null;
+ }
+
private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar";
private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar";
diff --git a/test/945-obsolete-native/build b/test/945-obsolete-native/build
new file mode 100755
index 0000000000..898e2e54a2
--- /dev/null
+++ b/test/945-obsolete-native/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/945-obsolete-native/expected.txt b/test/945-obsolete-native/expected.txt
new file mode 100644
index 0000000000..83efda144d
--- /dev/null
+++ b/test/945-obsolete-native/expected.txt
@@ -0,0 +1,9 @@
+hello
+Not doing anything here
+goodbye
+hello
+transforming calling function
+goodbye
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
diff --git a/test/945-obsolete-native/info.txt b/test/945-obsolete-native/info.txt
new file mode 100644
index 0000000000..c8b892cedd
--- /dev/null
+++ b/test/945-obsolete-native/info.txt
@@ -0,0 +1 @@
+Tests basic obsolete method support
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
new file mode 100644
index 0000000000..061e7afbbc
--- /dev/null
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/stringprintf.h"
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test945ObsoleteNative {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ BindFunctions(jvmti_env, env, "Transform");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject runnable) {
+ jclass runnable_klass = env->FindClass("java/lang/Runnable");
+ DCHECK(runnable_klass != nullptr);
+ jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V");
+ env->CallVoidMethod(runnable, run_method);
+}
+
+
+} // namespace Test945ObsoleteNative
+} // namespace art
diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/945-obsolete-native/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java
new file mode 100644
index 0000000000..5e2154e9a3
--- /dev/null
+++ b/test/945-obsolete-native/src/Main.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+
+public class Main {
+ // class Transform {
+ // public void sayHi(Runnable r) {
+ // System.out.println("Hello - Transformed");
+ // doExecute(r);
+ // System.out.println("Goodbye - Transformed");
+ // }
+ //
+ // private static native void doExecute(Runnable r);
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+ "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" +
+ "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" +
+ "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" +
+ "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" +
+ "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" +
+ "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" +
+ "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" +
+ "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" +
+ "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" +
+ "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+ "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" +
+ "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" +
+ "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" +
+ "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+ "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+ "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+ "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" +
+ "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" +
+ "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" +
+ "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" +
+ "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" +
+ "AA==");
+
+ public static void main(String[] args) {
+ bindTest945ObsoleteNative();
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ System.out.println("transforming calling function");
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ private static native void bindTest945ObsoleteNative();
+}
diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java
new file mode 100644
index 0000000000..2b7cc1b3a1
--- /dev/null
+++ b/test/945-obsolete-native/src/Transform.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+ public void sayHi(Runnable r) {
+ System.out.println("hello");
+ doExecute(r);
+ System.out.println("goodbye");
+ }
+
+ private static native void doExecute(Runnable r);
+}
diff --git a/test/952-invoke-custom/build b/test/952-invoke-custom/build
new file mode 100644
index 0000000000..a423ca6b4e
--- /dev/null
+++ b/test/952-invoke-custom/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt
new file mode 100644
index 0000000000..bb87296e60
--- /dev/null
+++ b/test/952-invoke-custom/expected.txt
@@ -0,0 +1,14 @@
+Caught exception from uninitialized call site
+Caught exception from uninitialized call site
+linkerMethod failure type 1
+Returning null instead of CallSite for add (int,int)int
+linkerMethod failure type 2
+Throwing InstantiationException in linkerMethod()
+linkerMethod failure type 3
+Throwing ArithmeticException in add()
+Failure Type + 0 (1013)
+Linking add (int,int)int
+100
+-9000
+9000
+Winners 1 Votes 16
diff --git a/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000000..5d5cae4147
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import java.lang.Thread;
+import java.lang.ThreadLocal;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TestInvokeCustomWithConcurrentThreads extends Thread {
+ private static final int NUMBER_OF_THREADS = 16;
+
+ private static final AtomicInteger nextIndex = new AtomicInteger(0);
+
+ private static final ThreadLocal<Integer> threadIndex =
+ new ThreadLocal<Integer>() {
+ @Override
+ protected Integer initialValue() {
+ return nextIndex.getAndIncrement();
+ }
+ };
+
+ // Array of call sites instantiated, one per thread
+ private static CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS];
+
+ // Array of counters for how many times each instantiated call site is called
+ private static AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS];
+
+ // Array of call site indicies of which call site a thread invoked
+ private static AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS];
+
+ private TestInvokeCustomWithConcurrentThreads() {}
+
+ private static int getThreadIndex() {
+ return threadIndex.get().intValue();
+ }
+
+ public static int notUsed(int x) {
+ return x;
+ }
+
+ @Override
+ public void run() {
+ int x = setCalled(-1 /* argument dropped */);
+ notUsed(x);
+ }
+
+ @CalledByInvokeCustom(
+ invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
+ enclosingType = TestInvokeCustomWithConcurrentThreads.class,
+ name = "linkerMethod",
+ argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}),
+ name = "setCalled",
+ returnType = int.class,
+ argumentTypes = {int.class})
+ private static int setCalled(int index) {
+ called[index].getAndIncrement();
+ targetted[getThreadIndex()].set(index);
+ return 0;
+ }
+
+ @SuppressWarnings("unused")
+ private static CallSite linkerMethod(MethodHandles.Lookup caller,
+ String name,
+ MethodType methodType) throws Throwable {
+ int threadIndex = getThreadIndex();
+ MethodHandle mh =
+ caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType);
+ assertEquals(methodType, mh.type());
+ assertEquals(mh.type().parameterCount(), 1);
+ mh = MethodHandles.insertArguments(mh, 0, threadIndex);
+ mh = MethodHandles.dropArguments(mh, 0, int.class);
+ assertEquals(mh.type().parameterCount(), 1);
+ assertEquals(methodType, mh.type());
+
+ // Sleep to try to get concurrent executions of this
+ // method. Multiple call sites should be created, but only one
+ // invoked.
+ Thread.sleep(125);
+
+ instantiated[getThreadIndex()] = new ConstantCallSite(mh);
+ return instantiated[getThreadIndex()];
+ }
+
+ public static void test() throws Throwable {
+ // Initialize counters for which call site gets invoked
+ for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+ called[i] = new AtomicInteger(0);
+ targetted[i] = new AtomicInteger(0);
+ }
+
+ // Run threads that each invoke-custom the call site
+ Thread [] threads = new Thread[NUMBER_OF_THREADS];
+ for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+ threads[i] = new TestInvokeCustomWithConcurrentThreads();
+ threads[i].start();
+ }
+
+ // Wait for all threads to complete
+ for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+ threads[i].join();
+ }
+
+ // Check one call site instance won
+ int winners = 0;
+ int votes = 0;
+ for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+ assertNotEquals(instantiated[i], null);
+ if (called[i].get() != 0) {
+ winners++;
+ votes += called[i].get();
+ }
+ }
+
+ System.out.println("Winners " + winners + " Votes " + votes);
+
+ // We assert this below but output details when there's an error as
+ // it's non-deterministic.
+ if (winners != 1) {
+ System.out.println("Threads did not the same call-sites:");
+ for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+ System.out.format(" Thread % 2d invoked call site instance #%02d\n",
+ i, targetted[i].get());
+ }
+ }
+
+ // We assert this below but output details when there's an error as
+ // it's non-deterministic.
+ if (votes != NUMBER_OF_THREADS) {
+ System.out.println("Call-sites invocations :");
+ for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+ System.out.format(" Call site instance #%02d was invoked % 2d times\n",
+ i, called[i].get());
+ }
+ }
+
+ assertEquals(winners, 1);
+ assertEquals(votes, NUMBER_OF_THREADS);
+ }
+
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("assertTrue value: " + value);
+ }
+ }
+
+ public static void assertEquals(byte b1, byte b2) {
+ if (b1 == b2) { return; }
+ throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+ }
+
+ public static void assertEquals(char c1, char c2) {
+ if (c1 == c2) { return; }
+ throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+ }
+
+ public static void assertEquals(short s1, short s2) {
+ if (s1 == s2) { return; }
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+
+ public static void assertEquals(int i1, int i2) {
+ if (i1 == i2) { return; }
+ throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+ }
+
+ public static void assertEquals(long l1, long l2) {
+ if (l1 == l2) { return; }
+ throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+ }
+
+ public static void assertEquals(float f1, float f2) {
+ if (f1 == f2) { return; }
+ throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+ }
+
+ public static void assertEquals(double d1, double d2) {
+ if (d1 == d2) { return; }
+ throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+ }
+
+ public static void assertEquals(Object o, Object p) {
+ if (o == p) { return; }
+ if (o != null && p != null && o.equals(p)) { return; }
+ throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+ }
+
+ public static void assertNotEquals(Object o, Object p) {
+ if (o != p) { return; }
+ if (o != null && p != null && !o.equals(p)) { return; }
+ throw new AssertionError("assertNotEquals: o1: " + o + ", o2: " + p);
+ }
+
+ public static void assertEquals(String s1, String s2) {
+ if (s1 == s2) {
+ return;
+ }
+
+ if (s1 != null && s2 != null && s1.equals(s2)) {
+ return;
+ }
+
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000000..93d96a9a7b
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 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 com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMinimalArguments {
+
+ private static int forceFailureType = 0;
+
+ private static int FAILURE_TYPE_NONE = 0;
+ private static int FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL = 1;
+ private static int FAILURE_TYPE_LINKER_METHOD_THROWS = 2;
+ private static int FAILURE_TYPE_TARGET_METHOD_THROWS = 3;
+
+ @CalledByInvokeCustom(
+ invokeMethodHandle = @LinkerMethodHandle(
+ kind = MethodHandleKind.INVOKE_STATIC,
+ enclosingType = TestLinkerMethodMinimalArguments.class,
+ argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+ name = "linkerMethod"),
+ name = "add",
+ returnType = int.class,
+ argumentTypes = {int.class, int.class})
+ private static int add(int a, int b) {
+ if (forceFailureType == FAILURE_TYPE_TARGET_METHOD_THROWS) {
+ System.out.println("Throwing ArithmeticException in add()");
+ throw new ArithmeticException("add");
+ }
+ return a + b;
+ }
+
+ @SuppressWarnings("unused")
+ private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
+ MethodType methodType) throws Throwable {
+ System.out.println("linkerMethod failure type " + forceFailureType);
+ MethodHandle mh_add =
+ caller.findStatic(TestLinkerMethodMinimalArguments.class, name, methodType);
+ if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL) {
+ System.out.println("Returning null instead of CallSite for " + name + " " + methodType);
+ return null;
+ } else if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_THROWS) {
+ System.out.println("Throwing InstantiationException in linkerMethod()");
+ throw new InstantiationException("linkerMethod");
+ } else {
+ return new ConstantCallSite(mh_add);
+ }
+ }
+
+ public static void test(int failureType, int x, int y) throws Throwable {
+ assertTrue(failureType >= FAILURE_TYPE_NONE);
+ assertTrue(failureType <= FAILURE_TYPE_TARGET_METHOD_THROWS);
+ forceFailureType = failureType;
+ assertEquals(x + y, add(x, y));
+ System.out.println("Failure Type + " + failureType + " (" + x + y+ ")");
+ }
+
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("assertTrue value: " + value);
+ }
+ }
+
+ public static void assertEquals(byte b1, byte b2) {
+ if (b1 == b2) { return; }
+ throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+ }
+
+ public static void assertEquals(char c1, char c2) {
+ if (c1 == c2) { return; }
+ throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+ }
+
+ public static void assertEquals(short s1, short s2) {
+ if (s1 == s2) { return; }
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+
+ public static void assertEquals(int i1, int i2) {
+ if (i1 == i2) { return; }
+ throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+ }
+
+ public static void assertEquals(long l1, long l2) {
+ if (l1 == l2) { return; }
+ throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+ }
+
+ public static void assertEquals(float f1, float f2) {
+ if (f1 == f2) { return; }
+ throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+ }
+
+ public static void assertEquals(double d1, double d2) {
+ if (d1 == d2) { return; }
+ throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+ }
+
+ public static void assertEquals(Object o, Object p) {
+ if (o == p) { return; }
+ if (o != null && p != null && o.equals(p)) { return; }
+ throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+ }
+
+ public static void assertEquals(String s1, String s2) {
+ if (s1 == s2) {
+ return;
+ }
+
+ if (s1 != null && s2 != null && s1.equals(s2)) {
+ return;
+ }
+
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000000..4e4d97e15e
--- /dev/null
+++ b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.jack.annotations.CalledByInvokeCustom;
+import com.android.jack.annotations.Constant;
+import com.android.jack.annotations.LinkerMethodHandle;
+import com.android.jack.annotations.MethodHandleKind;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMultipleArgumentTypes {
+
+ private static int bootstrapRunCount = 0;
+
+ @CalledByInvokeCustom(
+ invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
+ enclosingType = TestLinkerMethodMultipleArgumentTypes.class,
+ name = "linkerMethod",
+ argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class,
+ boolean.class, byte.class, char.class, short.class, int.class,
+ float.class, double.class, String.class, Class.class, long.class}),
+ methodHandleExtraArgs = {@Constant(booleanValue = true), @Constant(byteValue = 1),
+ @Constant(charValue = 'a'), @Constant(shortValue = 1024),
+ @Constant(intValue = 1), @Constant(floatValue = 11.1f),
+ @Constant(doubleValue = 2.2), @Constant(stringValue = "Hello"),
+ @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class),
+ @Constant(longValue = 123456789L)},
+ name = "add",
+ returnType = int.class,
+ argumentTypes = {int.class, int.class})
+ private static int add(int a, int b) {
+ return a + b;
+ }
+
+ @SuppressWarnings("unused")
+ private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
+ MethodType methodType, boolean v1, byte v2, char v3,
+ short v4, int v5, float v6, double v7,
+ String v8, Class<?> v9, long v10) throws Throwable {
+ System.out.println("Linking " + name + " " + methodType);
+ assertTrue(v1);
+ assertEquals(1, v2);
+ assertEquals('a', v3);
+ assertEquals(1024, v4);
+ assertEquals(1, v5);
+ assertEquals(11.1f, v6);
+ assertEquals(2.2, v7);
+ assertEquals("Hello", v8);
+ assertEquals(TestLinkerMethodMultipleArgumentTypes.class, v9);
+ assertEquals(123456789L, v10);
+ MethodHandle mh_add =
+ caller.findStatic(TestLinkerMethodMultipleArgumentTypes.class, name, methodType);
+ return new ConstantCallSite(mh_add);
+ }
+
+ public int GetBootstrapRunCount() {
+ return bootstrapRunCount;
+ }
+
+ public static void test(int x, int y) throws Throwable {
+ assertEquals(x + y, add(x, y));
+ System.out.println(x + y);
+ }
+
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new AssertionError("assertTrue value: " + value);
+ }
+ }
+
+ public static void assertEquals(byte b1, byte b2) {
+ if (b1 == b2) { return; }
+ throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+ }
+
+ public static void assertEquals(char c1, char c2) {
+ if (c1 == c2) { return; }
+ throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+ }
+
+ public static void assertEquals(short s1, short s2) {
+ if (s1 == s2) { return; }
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+
+ public static void assertEquals(int i1, int i2) {
+ if (i1 == i2) { return; }
+ throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+ }
+
+ public static void assertEquals(long l1, long l2) {
+ if (l1 == l2) { return; }
+ throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+ }
+
+ public static void assertEquals(float f1, float f2) {
+ if (f1 == f2) { return; }
+ throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+ }
+
+ public static void assertEquals(double d1, double d2) {
+ if (d1 == d2) { return; }
+ throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+ }
+
+ public static void assertEquals(Object o, Object p) {
+ if (o == p) { return; }
+ if (o != null && p != null && o.equals(p)) { return; }
+ throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+ }
+
+ public static void assertEquals(String s1, String s2) {
+ if (s1 == s2) {
+ return;
+ }
+
+ if (s1 != null && s2 != null && s1.equals(s2)) {
+ return;
+ }
+
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+}
diff --git a/test/952-invoke-custom/generator/build-test.sh b/test/952-invoke-custom/generator/build-test.sh
new file mode 100755
index 0000000000..90a60e6f8f
--- /dev/null
+++ b/test/952-invoke-custom/generator/build-test.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+args="$@"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+test_dir="test-$$"
+if [ -z "$TMPDIR" ]; then
+ tmp_dir="/tmp/$USER/${test_dir}"
+else
+ tmp_dir="${TMPDIR}/${test_dir}"
+fi
+
+# This is a custom drop that necessitates this complexity.
+JACK_ANNOTATIONS_LIB=$HOME/Downloads/jack-test-annotations-lib.jack
+
+# Compile test into a base64 string that can be instantiated via
+# reflection on hosts without the jack-test-annotations-lib.jack file.
+mkdir $tmp_dir
+for input_file in $progdir/*.java; do
+ i=${input_file##*/Test}
+ i=${i%%.java}
+ src_file=$progdir/Test$i.java
+ jack_file=./src.jack
+ dex_file=./classes.dex
+ base_64_file=$tmp_dir/TestData$i.base64
+ output_file=$progdir/../src/TestData$i.java
+ # Compile source file to jack file.
+ jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack:$JACK_ANNOTATIONS_LIB -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --output-jack $jack_file $src_file
+ # Compile jack file to classes.dex.
+ jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --import $jack_file --output-dex .
+ # Pack the classes.dex file into a base64 string.
+ base64 -w 72 $dex_file > $base_64_file
+ # Emit a managed source file containing the base64 string. The test can be
+ # run by loading this string as a dex file and invoking it via reflection.
+cat > $output_file <<HEADER
+/* Generated by ${prog##*/} from ${src_file##*/} */
+public class TestData$i {
+ public static final String BASE64_DEX_FILE =
+HEADER
+sed -e 's/^\(.*\)$/ "\1" +/' -e '$s/ +/;/' $base_64_file >> $output_file
+cat >> $output_file <<FOOTER
+}
+FOOTER
+ rm $dex_file $jack_file
+done
+
+rm -rf $tmp_dir
diff --git a/test/952-invoke-custom/info.txt b/test/952-invoke-custom/info.txt
new file mode 100644
index 0000000000..e9a9f6c4a5
--- /dev/null
+++ b/test/952-invoke-custom/info.txt
@@ -0,0 +1,7 @@
+A test that is only available as a DEX binary.
+
+This tests execution of invoke-custom. There is no bytecode to emit
+invoke-custom directly. This test is generated using jack-test-annotations-lib.jack
+which is not a publicly supported at this time.
+
+The tests and a script to generate data from them are in the generator/ directory.
diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java
new file mode 100644
index 0000000000..2abc3122fa
--- /dev/null
+++ b/test/952-invoke-custom/src/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 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 dalvik.system.InMemoryDexClassLoader;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Base64;
+
+// This test is a stop-gap until we have support for generating invoke-custom
+// in the Android tree.
+
+public class Main {
+
+ private static void TestUninitializedCallSite() throws Throwable {
+ CallSite callSite = new MutableCallSite(MethodType.methodType(int.class));
+ try {
+ callSite.getTarget().invoke();
+ fail();
+ } catch (IllegalStateException e) {
+ System.out.println("Caught exception from uninitialized call site");
+ }
+
+ callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class));
+ try {
+ callSite.getTarget().invoke(1535, 'd');
+ fail();
+ } catch (IllegalStateException e) {
+ System.out.println("Caught exception from uninitialized call site");
+ }
+ }
+
+ private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable {
+ // This is a more comprehensive test of invoke-custom, the linker
+ // method takes additional arguments of types boolean, byte, char,
+ // short, int, float, double, String, Class, and long (in this order)
+ // The test asserts the values passed to the linker method match their
+ // expected values.
+ byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes();
+ Base64.Decoder decoder = Base64.getDecoder();
+ ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+ InMemoryDexClassLoader classLoader =
+ new InMemoryDexClassLoader(dexBuffer,
+ ClassLoader.getSystemClassLoader());
+ Class<?> testClass =
+ classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes");
+ Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class);
+ // First invocation should link via the bootstrap method (outputs "Linking add" ...).
+ testMethod.invoke(null, 33, 67);
+ // Subsequent invocations use the cached value of the CallSite and do not require linking.
+ testMethod.invoke(null, -10000, +1000);
+ testMethod.invoke(null, -1000, +10000);
+ }
+
+ private static void TestLinkerMethodMinimalArguments() throws Throwable {
+ // This test checks various failures when running the linker
+ // method and during invocation of the method handle.
+ byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes();
+ Base64.Decoder decoder = Base64.getDecoder();
+ ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+ InMemoryDexClassLoader classLoader =
+ new InMemoryDexClassLoader(dexBuffer,
+ ClassLoader.getSystemClassLoader());
+ Class<?> testClass =
+ classLoader.loadClass("TestLinkerMethodMinimalArguments");
+ Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class);
+
+ try {
+ testMethod.invoke(null, 1 /* linker method return null */, 10, 10);
+ } catch (InvocationTargetException e) {
+ assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
+ assertEquals(
+ e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException");
+ }
+
+ try {
+ testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11);
+ } catch (InvocationTargetException e) {
+ assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
+ assertEquals(
+ e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException");
+ }
+ try {
+ // Creating the CallSite works here, but fail invoking the method.
+ testMethod.invoke(null, 3 /* target throw NPE */, 10, 12);
+ } catch (InvocationTargetException e) {
+ assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException");
+ }
+
+ // This should succeed using already resolved CallSite.
+ testMethod.invoke(null, 0 /* no error */, 10, 13);
+ }
+
+ private static void TestInvokeCustomWithConcurrentThreads() throws Throwable {
+ // This is a concurrency test that attempts to run invoke-custom on the same
+ // call site.
+ byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes();
+ Base64.Decoder decoder = Base64.getDecoder();
+ ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
+
+ InMemoryDexClassLoader classLoader =
+ new InMemoryDexClassLoader(dexBuffer,
+ ClassLoader.getSystemClassLoader());
+ Class<?> testClass =
+ classLoader.loadClass("TestInvokeCustomWithConcurrentThreads");
+ Method testMethod = testClass.getDeclaredMethod("test");
+ testMethod.invoke(null);
+ }
+
+ public static void assertEquals(Object o, Object p) {
+ if (o == p) { return; }
+ if (o != null && p != null && o.equals(p)) { return; }
+ throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+ }
+
+ public static void assertEquals(String s1, String s2) {
+ if (s1 == s2) {
+ return;
+ }
+
+ if (s1 != null && s2 != null && s1.equals(s2)) {
+ return;
+ }
+
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+
+ private static void fail() {
+ System.out.println("fail");
+ Thread.dumpStack();
+ }
+
+ public static void main(String[] args) throws Throwable {
+ TestUninitializedCallSite();
+ TestLinkerMethodMinimalArguments();
+ TestLinkerMethodMultipleArgumentTypes();
+ TestInvokeCustomWithConcurrentThreads();
+ }
+} \ No newline at end of file
diff --git a/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000000..9a77e67e49
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,145 @@
+/* Generated by build-test.sh from TestInvokeCustomWithConcurrentThreads.java */
+public class TestDataInvokeCustomWithConcurrentThreads {
+ public static final String BASE64_DEX_FILE =
+ "ZGV4CjAzOADq+VhgIFlZEzwomlDWIN5pEqcVmb8SN2y8HQAAcAAAAHhWNBIAAAAAAAAAANQc" +
+ "AACqAAAAcAAAACoAAAAYAwAAJgAAAMADAAAJAAAAiAUAADgAAADQBQAAAgAAAJQHAADgFQAA" +
+ "3AcAAKQRAADXEQAABxIAABASAAAYEgAAIBIAACgSAAAwEgAAOBIAAEASAABIEgAAUBIAAFcS" +
+ "AABaEgAAZBIAAGwSAABwEgAAcxIAAHYSAACQEgAAkxIAAJYSAACZEgAAnRIAAKwSAACvEgAA" +
+ "shIAALYSAAC6EgAAvhIAAMISAADGEgAAyhIAANASAADVEgAA2xIAAAYTAAAvEwAAMxMAAGgT" +
+ "AACbEwAAzBMAAPATAAAQFAAAMxQAAFIUAABuFAAAhRQAAKEUAAC0FAAAyRQAAN0UAADxFAAA" +
+ "DBUAACAVAAA0FQAATBUAAGUVAAB8FQAAmRUAAL4VAADfFQAACBYAACoWAABJFgAAdhYAAIkW" +
+ "AACMFgAAkhYAAL4WAADkFgAA5xYAAOwWAADxFgAA9hYAAPsWAAD/FgAABBcAAAgXAAANFwAA" +
+ "ERcAABYXAAAbFwAAHxcAACkXAAAsFwAAMBcAAEQXAABZFwAAbhcAAIwXAAC6FwAAxxcAAM8X" +
+ "AADeFwAA7BcAAP8XAAASGAAAJRgAADgYAABLGAAAXhgAAHEYAACFGAAAlhgAAK0YAAC5GAAA" +
+ "zRgAANEYAADVGAAA2RgAAN0YAADlGAAA7RgAAPEYAAD1GAAABBkAABgZAAAnGQAALxkAADMZ" +
+ "AAA3GQAAQxkAAEsZAABQGQAAYRkAAHEZAAB0GQAAeBkAAHwZAACDGQAAkRkAAKIZAACwGQAA" +
+ "uhkAAM4ZAADUGQAA2hkAAN4ZAADiGQAA8BkAAPwZAAAAGgAABhoAABEaAAAaGgAAHRoAACIa" +
+ "AAAlGgAANRoAAD4aAABKGgAATxoAAFMaAABXGgAAXBoAAGcaAABuGgAAdRoAAIAaAACGGgAA" +
+ "jBoAAJkaAACiGgAArBoAALIaAAC5GgAAwhoAAMkaAADSGgAAEAAAABEAAAATAAAAFAAAABUA" +
+ "AAAYAAAAIwAAACQAAAAmAAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAA" +
+ "MAAAADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADgAAAA5AAAAOgAAADsAAAA8AAAAPQAAAD4A" +
+ "AAA/AAAAQAAAAEIAAABGAAAAVAAAAFYAAABXAAAAWAAAAFkAAABaAAAAFQAAAAQAAAAAAAAA" +
+ "FgAAAAQAAADcEAAAIQAAABAAAADkEAAAGQAAABMAAAAAAAAAHQAAABMAAADcEAAAGQAAABQA" +
+ "AAAAAAAAGQAAABUAAAAAAAAAGgAAABYAAADsEAAAGwAAABYAAAD0EAAAHAAAABYAAAD8EAAA" +
+ "HQAAABYAAADcEAAAHgAAABYAAAAEEQAAHwAAABYAAAAMEQAAHwAAABYAAAAUEQAAJQAAABYA" +
+ "AAAcEQAAIgAAABsAAAAkEQAAIgAAAB0AAAAwEQAAIAAAAB0AAAA8EQAAIAAAAB0AAABIEQAA" +
+ "GQAAACAAAAAAAAAAGQAAACEAAAAAAAAARgAAACMAAAAAAAAARwAAACMAAABUEQAASAAAACMA" +
+ "AABcEQAASQAAACMAAABkEQAASgAAACMAAABsEQAASwAAACMAAADcEAAATAAAACMAAAB0EQAA" +
+ "TQAAACMAAAAEEQAATgAAACMAAAB8EQAATwAAACMAAAAMEQAAUAAAACMAAACEEQAATwAAACMA" +
+ "AAAUEQAAUAAAACMAAACMEQAATwAAACMAAACUEQAAUQAAACMAAACcEQAAUgAAACMAAAAcEQAA" +
+ "VQAAACQAAAAMEQAABwAEAEEAAAAHACkAbwAAAAcAKACEAAAABwAhAI8AAAAHACkAngAAAAcA" +
+ "GQChAAAACgAKABcAAAATABIAQwAAABcAEACSAAAABgAVAA4AAAAGAAMAggAAAAYABQCCAAAA" +
+ "BwAUAAsAAAAHABUADQAAAAcAFQAOAAAABwAWAF4AAAAHABcAXgAAAAcAGABeAAAABwAZAF4A" +
+ "AAAHABsAXgAAAAcAHQBeAAAABwAfAF4AAAAHACEAXgAAAAcAIwBeAAAABwAfAGcAAAAHACQA" +
+ "aQAAAAcAAAB9AAAABwAPAIsAAAAHAAEAkAAAAAcAFQCXAAAABwABAJsAAAAHABUAnwAAABAA" +
+ "AgB6AAAAEAAgAJUAAAARAB4ADgAAABMAAACFAAAAEwAEAKYAAAAUACUAdgAAABUAJQB2AAAA" +
+ "FgAVAA4AAAAWAAcAXAAAABYACABcAAAAFgAJAFwAAAAWAAoAXAAAABYACwBcAAAAFgAMAFwA" +
+ "AAAWAA0AXAAAABYADgBcAAAAFgAGAKMAAAAYABUADgAAABgAFQCHAAAAGAAcAJwAAAAYABUA" +
+ "nQAAABkAFQAOAAAAGQAFAHsAAAAcACIADgAAAB0AEwCkAAAAHgAQAHkAAAAfABEAcwAAAB8A" +
+ "EgCDAAAAIAAAAJQAAAAhABoADgAAACEAAAB7AAAAIQAAAHwAAAAhABoAmgAAAFMcAAAGAAAA" +
+ "EAAAABkAAAAAAAAARAAAAJwQAABaHAAAAAAAAAcAAAABAAAAGAAAAAAAAABEAAAArBAAAG0c" +
+ "AABQHAAABAAAABIAAAADAAAA9BsAAPsbAAAEHAAAAQAAABMcAAABAAAABBwAAAEAAAAcHAAA" +
+ "AQAAACUcAAABAAEAAQAAANUaAAAEAAAAcBAsAAAADgACAAEAAQAAAN0aAAANAAAAcQADAAAA" +
+ "DABuEDYAAAAKAHEQGwAAAAwAEQAAAAIAAQABAAAA4hoAAAUAAABuEAEAAQAMABEAAAABAAAA" +
+ "AAAAAAAAAAADAAAAYgADABEAAAADAAAAAgAAAOcaAAAeAAAAEwIQACIAIQASAXAgNAAQAGkA" +
+ "AwAiAAYAcBAAAAAAaQAFACMgKABpAAIAIyApAGkAAQAjICkAaQAEAA4AAQABAAEAAAD0GgAA" +
+ "BAAAAHAQKAAAAA4ABQACAAIAAAD5GgAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJfAAAA" +
+ "biAlACEADAFuICIAMQAMARsCAwAAAG4gJQAhAAwBbiAiAEEADAFuECcAAQAMAXAgGQAQACcA" +
+ "BQACAAIAAAACGwAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJgAAAAbiAlACEADAFuIB8A" +
+ "MQAMARsCBAAAAG4gJQAhAAwBbiAfAEEADAFuECcAAQAMAXAgGQAQACcACAAEAAMAAAALGwAA" +
+ "KgAAAC8ABAY5AAMADgAiABEAIgEWAHAQHgABABsCYQAAAG4gJQAhAAwBbjAgAEEFDAEbAgUA" +
+ "AABuICUAIQAMAW4wIABhBwwBbhAnAAEADAFwIBkAEAAnAAUAAgACAAAAFBsAACoAAAAtAAME" +
+ "OQADAA4AIgARACIBFgBwEB4AAQAbAmIAAABuICUAIQAMAW4gIQAxAAwBGwIGAAAAbiAlACEA" +
+ "DAFuICEAQQAMAW4QJwABAAwBcCAZABAAJwAFAAIAAgAAAB0bAAAoAAAAM0MDAA4AIgARACIB" +
+ "FgBwEB4AAQAbAmMAAABuICUAIQAMAW4gIgAxAAwBGwIHAAAAbiAlACEADAFuICIAQQAMAW4Q" +
+ "JwABAAwBcCAZABAAJwAIAAQAAwAAACgbAAAqAAAAMQAEBjkAAwAOACIAEQAiARYAcBAeAAEA" +
+ "GwJkAAAAbiAlACEADAFuMCMAQQUMARsCCAAAAG4gJQAhAAwBbjAjAGEHDAFuECcAAQAMAXAg" +
+ "GQAQACcABQACAAIAAAAzGwAAMwAAADNDAwAOADgDCwA4BAkAbiAcAEMACgA4AAMADgAiABEA" +
+ "IgEWAHAQHgABABsCZgAAAG4gJQAhAAwBbiAkADEADAEbAgkAAABuICUAIQAMAW4gJABBAAwB" +
+ "bhAnAAEADAFwIBkAEAAnAAAABQACAAIAAAA/GwAAMwAAADNDAwAOADgDCwA4BAkAbiAdAEMA" +
+ "CgA4AAMADgAiABEAIgEWAHAQHgABABsCZQAAAG4gJQAhAAwBbiAlADEADAEbAgoAAABuICUA" +
+ "IQAMAW4gJQBBAAwBbhAnAAEADAFwIBkAEAAnAAAABQACAAIAAABNGwAAKAAAADNDAwAOACIA" +
+ "EQAiARYAcBAeAAEAGwJlAAAAbiAlACEADAFuICIAMQAMARsCCgAAAG4gJQAhAAwBbiAiAEEA" +
+ "DAFuECcAAQAMAXAgGQAQACcABQACAAIAAABYGwAANQAAADJDAwAOADgDDQA4BAsAbiAcAEMA" +
+ "CgDfAAABOAADAA4AIgARACIBFgBwEB4AAQAbAmgAAABuICUAIQAMAW4gJAAxAAwBGwIJAAAA" +
+ "biAlACEADAFuICQAQQAMAW4QJwABAAwBcCAZABAAJwAAAAQAAQACAAAAZBsAAB0AAAA5AxwA" +
+ "IgARACIBFgBwEB4AAQAbAmoAAABuICUAIQAMAW4gJgAxAAwBbhAnAAEADAFwIBkAEAAnAA4A" +
+ "AAABAAAAAQAAAHAbAAANAAAAYgAFAG4QLQAAAAwAHwATAG4QGgAAAAoADwAAAAkAAwAEAAAA" +
+ "dRsAAGEAAAASFRIEcQARAAAACgEcAgcAbkAwACaHDABuEC8AAAAMAnEgDAAoAG4QLwAAAAwC" +
+ "bhAzAAIACgJxIAoAUgAjUiYAcRAbAAEADANNAwIEcTAyAEACDAAjUiUAYgMHAE0DAgRxMDEA" +
+ "QAIMAG4QLwAAAAwCbhAzAAIACgJxIAoAUgBuEC8AAAAMAnEgDAAoABYCfQBxICoAMgBiAgIA" +
+ "cQARAAAACgMiBBwAcCAuAAQATQQCA2ICAgBxABEAAAAKA0YCAgMRAgAAAQABAAAAAACTGwAA" +
+ "AQAAAA8AAAADAAEAAgAAAJobAAAUAAAAYgABAEYAAAJuEDYAAABiAAQAcQARAAAACgFGAAAB" +
+ "biA3ACAAEgAPAAwAAAADAAAAoxsAAOgAAAASKxIaEgkTCBAAEgA1gBcAYgQBACIFIQBwIDQA" +
+ "lQBNBQQAYgQEACIFIQBwIDQAlQBNBQQA2AAAASjqI4EnABIANYARACIEBwBwEAUABABNBAEA" +
+ "RgQBAG4QKwAEANgAAAEo8BIANYAKAEYEAQBuECkABADYAAABKPcSAxICEgA1gCIAYgQCAEYE" +
+ "BAASBXEgDwBUAGIEAQBGBAQAbhA1AAQACgQ4BA0A2AMDAWIEAQBGBAQAbhA1AAQACgSwQtgA" +
+ "AAEo32IECAAiBRYAcBAeAAUAGwZTAAAAbiAlAGUADAVuICIANQAMBRsGAgAAAG4gJQBlAAwF" +
+ "biAiACUADAVuECcABQAMBW4gGABUADKjLgBiBAgAGwVFAAAAbiAYAFQAEgA1gCMAYgQIABsF" +
+ "AQAAACO2JgBxEBsAAAAMB00HBgliBwQARgcHAG4QNQAHAAoHcRAbAAcADAdNBwYKbjAXAFQG" +
+ "2AAAASjeMoIuAGIECAAbBRIAAABuIBgAVAASADWAIwBiBAgAGwUAAAAAI7YmAHEQGwAAAAwH" +
+ "TQcGCWIHAQBGBwcAbhA1AAcACgdxEBsABwAMB00HBgpuMBcAVAbYAAABKN5xIAoAowBxIAoA" +
+ "ggAOAAMAAQABAAAA6BsAAAkAAAAS8fwQAAABAAoAcRATAAAADgAAANwHAAAAAAAAAAAAAAAA" +
+ "AADsBwAAAQAAAAMAAAAAAAAABQAAAPQHAAASAAAA/AcAABUAAAAECAAAFgAAAPwHAAABAAAA" +
+ "BAAAAAIAAAAVACYAAQAAAAEAAAABAAAAAgAAAAEAAAADAAAAAQAAAAUAAAABAAAAFAAAAAEA" +
+ "AAAVAAAAAQAAACQAAAADAAAAHgAVACAAAAADAAAAEgAVACAAAAADAAAAHQAEACUAAAADAAAA" +
+ "HQAEACYAAAACAAAAAAAAAAIAAAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAEAAQAAgAAAAUA" +
+ "BQACAAAAFAAUAAIAAAAVABUAAQAAAB0AAAACAAAAIgAiADEgQ2FsbCBzaXRlIGluc3RhbmNl" +
+ "ICMlMDJkIHdhcyBpbnZva2VkICUgMmQgdGltZXMKAC4gVGhyZWFkICUgMmQgaW52b2tlZCBj" +
+ "YWxsIHNpdGUgaW5zdGFuY2UgIyUwMmQKAAcgVm90ZXMgAAYsIGIyOiAABiwgYzI6IAAGLCBk" +
+ "MjogAAYsIGYyOiAABiwgaTI6IAAGLCBsMjogAAYsIG8yOiAABiwgczI6IAAFLWdldDAAATwA" +
+ "CDxjbGluaXQ+AAY8aW5pdD4AAj47AAFCAAFDABhDYWxsLXNpdGVzIGludm9jYXRpb25zIDoA" +
+ "AUQAAUYAAUkAAklJAA1JTlZPS0VfU1RBVElDAAFKAAFMAAJMQwACTEQAAkxGAAJMSQACTEoA" +
+ "AkxMAARMTElMAANMTEwABExMTEwAKUxUZXN0SW52b2tlQ3VzdG9tV2l0aENvbmN1cnJlbnRU" +
+ "aHJlYWRzJDE7ACdMVGVzdEludm9rZUN1c3RvbVdpdGhDb25jdXJyZW50VGhyZWFkczsAAkxa" +
+ "ADNMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsA" +
+ "MUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xpbmtlck1ldGhvZEhhbmRsZTsAL0xj" +
+ "b20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRsZUtpbmQ7ACJMZGFsdmlr" +
+ "L2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
+ "bGFzczsAIUxkYWx2aWsvYW5ub3RhdGlvbi9NZW1iZXJDbGFzc2VzOwAdTGRhbHZpay9hbm5v" +
+ "dGF0aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9p" +
+ "by9QcmludFN0cmVhbTsAGkxqYXZhL2xhbmcvQXNzZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5n" +
+ "L0NsYXNzOwATTGphdmEvbGFuZy9JbnRlZ2VyOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2" +
+ "YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcv" +
+ "U3lzdGVtOwASTGphdmEvbGFuZy9UaHJlYWQ7ABZMamF2YS9sYW5nL1RocmVhZExvY2FsABdM" +
+ "amF2YS9sYW5nL1RocmVhZExvY2FsOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" +
+ "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" +
+ "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" +
+ "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRs" +
+ "ZXM7AB1MamF2YS9sYW5nL2ludm9rZS9NZXRob2RUeXBlOwArTGphdmEvdXRpbC9jb25jdXJy" +
+ "ZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwARTlVNQkVSX09GX1RIUkVBRFMAAVMABFRZUEUA" +
+ "KlRlc3RJbnZva2VDdXN0b21XaXRoQ29uY3VycmVudFRocmVhZHMuamF2YQAkVGhyZWFkcyBk" +
+ "aWQgbm90IHRoZSBzYW1lIGNhbGwtc2l0ZXM6AAFWAANWQkIAA1ZDQwADVkREAANWRkYAAlZJ" +
+ "AANWSUkAAlZKAANWSkoAAlZMAANWTEwAA1ZTUwACVloACFdpbm5lcnMgAAFaAAJaTAASW0xq" +
+ "YXZhL2xhbmcvQ2xhc3M7ABNbTGphdmEvbGFuZy9PYmplY3Q7ABNbTGphdmEvbGFuZy9UaHJl" +
+ "YWQ7ABxbTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpdGU7ACxbTGphdmEvdXRpbC9jb25jdXJy" +
+ "ZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwALYWNjZXNzRmxhZ3MABmFwcGVuZAANYXJndW1l" +
+ "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6IAARYXNzZXJ0RXF1YWxz" +
+ "IGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMgZjE6IAARYXNzZXJ0RXF1" +
+ "YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVhbHMgczE6IAASYXNzZXJ0" +
+ "RXF1YWxzOiBvMTogAA9hc3NlcnROb3RFcXVhbHMAFWFzc2VydE5vdEVxdWFsczogbzE6IAAK" +
+ "YXNzZXJ0VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAJiMQACYjIAAmMxAAJjMgAGY2FsbGVk" +
+ "AAZjYWxsZXIAAmQxAAJkMgANZHJvcEFyZ3VtZW50cwASZW1pdHRlcjogamFjay00LjI1AA1l" +
+ "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAGZm9ybWF0AANnZXQA" +
+ "D2dldEFuZEluY3JlbWVudAAOZ2V0VGhyZWFkSW5kZXgAAWkAAmkxAAJpMgAFaW5kZXgADGlu" +
+ "aXRpYWxWYWx1ZQAPaW5zZXJ0QXJndW1lbnRzAAxpbnN0YW50aWF0ZWQACGludFZhbHVlABJp" +
+ "bnZva2VNZXRob2RIYW5kbGUABGpvaW4ABGtpbmQAAmwxAAJsMgAMbGlua2VyTWV0aG9kAApt" +
+ "ZXRob2RUeXBlAAJtaAAEbmFtZQAJbmV4dEluZGV4AAdub3RVc2VkAAFvAANvdXQAAXAADnBh" +
+ "cmFtZXRlckNvdW50AAdwcmludGxuAApyZXR1cm5UeXBlAANydW4AAnMxAAJzMgADc2V0AAlz" +
+ "ZXRDYWxsZWQABXNsZWVwAAVzdGFydAAJdGFyZ2V0dGVkAAR0ZXN0AAR0aGlzAAt0aHJlYWRJ" +
+ "bmRleAAHdGhyZWFkcwAIdG9TdHJpbmcABHR5cGUABXZhbHVlAAd2YWx1ZU9mAAV2b3RlcwAH" +
+ "d2lubmVycwABeAAmAAcOAls7ACkABw4AJwAHDgAjAAcsiVg1TU0CbEoANgAHDgCqAQJsbQcO" +
+ "PACvAQJubwcOPADIAQJycwcOWgDDAQJ4eQcOWgC5AQKAAYEBBw48AL4BAooBiwEHDloAzQEC" +
+ "kgGUAQcOPLQA2QECmQGaAQcOLSClIAC0AQKZAZoBBw48ANMBApIBlAEHDjzSAKQBAaYBBw4t" +
+ "ARoQADkABw4AWANxjwGNAQcsTAMBogEFaQMAjgEeeLTDpbR8W9IAPQGqAQcOAE8BggEHDni0" +
+ "AG0AB1kBAQMAfwUtlpNBLQMBowEoPHhXQTxYQB4DA6kBBR4DAqgBBTyHpS2RQwEkEi2HPHgB" +
+ "FA06Qy2HPHgBFA06QTw8AEIABw5aAwCqAQU8AAILAaUBGAcCDAJbBAiOAR4CDgGlARwEFzcX" +
+ "DBcxFw8CDQGlARwBGAYCDwGlARwBGBoACARdHAEYBIYBHAEdCQRdHAMYHhgVGCB1GAeIARsG" +
+ "jgEXi44BF5uWARgEAQQQAxYAF5sVAQAAAQIAgIAEjBABBKQQAcQg0BAGABMBABoBCgEKARoB" +
+ "CgEaA4gg7BABiIAEhBEBgoAE0BEBCegRAQnIEgEJqBMBCYwUAQnwFAEJ0BUBCbQWAQmsFwEJ" +
+ "pBgBCYQZAQmAGgEKzBoBCvgaAQnMHAIK4BwBCZgdFAH4IAAAEwAAAAAAAAABAAAAAAAAAAEA" +
+ "AACqAAAAcAAAAAIAAAAqAAAAGAMAAAMAAAAmAAAAwAMAAAQAAAAJAAAAiAUAAAUAAAA4AAAA" +
+ "0AUAAAcAAAABAAAAkAcAAAYAAAACAAAAlAcAAAgAAAABAAAA1AcAAAMQAAAFAAAA3AcAAAEg" +
+ "AAAXAAAADAgAAAYgAAACAAAAnBAAAAEQAAAXAAAA3BAAAAIgAACqAAAApBEAAAMgAAAWAAAA" +
+ "1RoAAAQgAAAGAAAA9BsAAAUgAAACAAAAUBwAAAAgAAACAAAAWhwAAAAQAAABAAAA1BwAAA==";
+}
diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000000..443a7af187
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java
@@ -0,0 +1,106 @@
+/* Generated by build-test.sh from TestLinkerMethodMinimalArguments.java */
+public class TestDataLinkerMethodMinimalArguments {
+ public static final String BASE64_DEX_FILE =
+ "ZGV4CjAzOADnZpVEc25JsNXLCW+vh64OuLf8RymAuINwFQAAcAAAAHhWNBIAAAAAAAAAAIgU" +
+ "AACBAAAAcAAAAB0AAAB0AgAAHAAAAOgCAAAHAAAAOAQAACIAAABwBAAAAQAAAIQFAADEDwAA" +
+ "rAUAAKAMAACjDAAApwwAAKoMAACyDAAAugwAAMIMAADKDAAA0gwAANoMAADiDAAA6gwAAPQM" +
+ "AAD8DAAA/wwAAAINAAAFDQAACA0AADENAABUDQAAZw0AAIoNAACbDQAAng0AAKMNAACyDQAA" +
+ "tQ0AALgNAAC8DQAAwA0AAMQNAADIDQAAzA0AANANAADWDQAA+g0AAP4NAAAzDgAAZg4AAJcO" +
+ "AACzDgAAyg4AAOsOAAAHDwAAGg8AAD4PAABSDwAAZg8AAIEPAACVDwAArA8AAMkPAADuDwAA" +
+ "DxAAADgQAABXEAAAgBAAAIMQAACqEAAA0RAAAAQRAAAHEQAADBEAABERAAAWEQAAGxEAACAR" +
+ "AAAmEQAAKxEAAC8RAAA0EQAAOREAAD0RAABAEQAARBEAAEcRAABMEQAAVBEAAGMRAABxEQAA" +
+ "hBEAAJcRAACqEQAAvREAANARAADjEQAA9hEAAAoSAAAWEgAAKhIAAC0SAAAxEgAANRIAADkS" +
+ "AAA9EgAARRIAAEkSAABNEgAAYRIAAHASAAB4EgAAfBIAAIASAACNEgAAmRIAAKsSAACvEgAA" +
+ "sxIAAMcSAADNEgAA0RIAANUSAADjEgAA/xIAAAsTAAATEwAAGRMAABwTAAAhEwAAJBMAAC0T" +
+ "AAA5EwAAPRMAAEETAABHEwAATRMAAFcTAABeEwAAYRMAAA0AAAAOAAAADwAAABAAAAAWAAAA" +
+ "GQAAACIAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8A" +
+ "AAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAAOAAAADwAAABIAAAAFwAAAAQAAADsCwAA" +
+ "GgAAABEAAAAAAAAAGwAAABIAAAD0CwAAHAAAABIAAAD8CwAAHQAAABIAAAAEDAAAHgAAABIA" +
+ "AAAMDAAAHwAAABIAAAAUDAAAIAAAABIAAAAcDAAAIAAAABIAAAAkDAAAIwAAABIAAAAsDAAA" +
+ "IQAAABUAAAA0DAAAIQAAABcAAABADAAAPAAAABsAAAAAAAAAPQAAABsAAABMDAAAPgAAABsA" +
+ "AABUDAAAPwAAABsAAABcDAAAQAAAABsAAABkDAAAQQAAABsAAADsCwAAQgAAABsAAABsDAAA" +
+ "QwAAABsAAAB4DAAARAAAABsAAAAcDAAARQAAABsAAACADAAARAAAABsAAAAkDAAARQAAABsA" +
+ "AACIDAAARAAAABsAAACQDAAARgAAABsAAACYDAAARwAAABsAAAAsDAAASQAAABwAAAAcDAAA" +
+ "BgAEABEAAAAGAAQAEgAAAAYABAATAAAABgAEABQAAAAGAAQAaAAAAAkACQAYAAAAEwALAHUA" +
+ "AAAGAAwACwAAAAYADAAMAAAABgAAAEsAAAAGAA0ATgAAAAYADgBOAAAABgAPAE4AAAAGABAA" +
+ "TgAAAAYAEQBOAAAABgATAE4AAAAGABUATgAAAAYAFwBOAAAABgAZAE4AAAAGABoAVwAAAAYA" +
+ "CgBvAAAABgASAHsAAAALABYAdwAAAAwAFgAMAAAADQAUAAwAAAAPABYADAAAABAADAAMAAAA" +
+ "EAAbAGMAAAARABsAYwAAABIADAAMAAAAEgACAEwAAAASAAMATAAAABIABABMAAAAEgAFAEwA" +
+ "AAASAAYATAAAABIABwBMAAAAEgAIAEwAAAASAAkATAAAABIAAQB9AAAAFgAYAAwAAAAYAAsA" +
+ "ZwAAADIUAAAGAAAAAQAAABAAAAAAAAAAOQAAAMQLAAA5FAAAAAAAAAQAAAANAAAAAQAAAAIU" +
+ "AAABAAAAKhQAAAEAAAAAAAAAZBMAAA8AAAASAGcABABnAAIAEhBnAAAAEiBnAAEAEjBnAAMA" +
+ "DgAAAAEAAQABAAAAcBMAAAQAAABwEBMAAAAOAAQAAgACAAAAdRMAABoAAABgAAQAYAEDADMQ" +
+ "EwBiAAYAGwE6AAAAbiAPABAAIgAMABsBSwAAAHAgEAAQACcAkAACAw8ABQACAAIAAAB/EwAA" +
+ "KAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJPAAAAbiAdACEADAFuIBoAMQAMARsCAwAAAG4g" +
+ "HQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACHEwAAKAAAADNDAwAOACIA" +
+ "DQAiARIAcBAWAAEAGwJQAAAAbiAdACEADAFuIBcAMQAMARsCBAAAAG4gHQAhAAwBbiAXAEEA" +
+ "DAFuEB8AAQAMAXAgEQAQACcACAAEAAMAAACPEwAAKgAAAC8ABAY5AAMADgAiAA0AIgESAHAQ" +
+ "FgABABsCUQAAAG4gHQAhAAwBbjAYAEEFDAEbAgUAAABuIB0AIQAMAW4wGABhBwwBbhAfAAEA" +
+ "DAFwIBEAEAAnAAUAAgACAAAAlxMAACoAAAAtAAMEOQADAA4AIgANACIBEgBwEBYAAQAbAlIA" +
+ "AABuIB0AIQAMAW4gGQAxAAwBGwIGAAAAbiAdACEADAFuIBkAQQAMAW4QHwABAAwBcCARABAA" +
+ "JwAFAAIAAgAAAJ8TAAAoAAAAM0MDAA4AIgANACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4g" +
+ "GgAxAAwBGwIHAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCARABAAJwAIAAQAAwAAAKcT" +
+ "AAAqAAAAMQAEBjkAAwAOACIADQAiARIAcBAWAAEAGwJUAAAAbiAdACEADAFuMBsAQQUMARsC" +
+ "CAAAAG4gHQAhAAwBbjAbAGEHDAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACvEwAAMwAAADND" +
+ "AwAOADgDCwA4BAkAbiAUAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsCVgAAAG4gHQAhAAwB" +
+ "biAcADEADAEbAgkAAABuIB0AIQAMAW4gHABBAAwBbhAfAAEADAFwIBEAEAAnAAAABQACAAIA" +
+ "AAC4EwAAMwAAADNDAwAOADgDCwA4BAkAbiAVAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsC" +
+ "VQAAAG4gHQAhAAwBbiAdADEADAEbAgoAAABuIB0AIQAMAW4gHQBBAAwBbhAfAAEADAFwIBEA" +
+ "EAAnAAAABQACAAIAAADDEwAAKAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJVAAAAbiAdACEA" +
+ "DAFuIBoAMQAMARsCCgAAAG4gHQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABAABAAIA" +
+ "AADLEwAAHQAAADkDHAAiAA0AIgESAHAQFgABABsCWAAAAG4gHQAhAAwBbiAeADEADAFuEB8A" +
+ "AQAMAXAgEQAQACcADgAAAAcAAwAEAAAA1RMAAGoAAABiAQYAIgISAHAQFgACABsDcAAAAG4g" +
+ "HQAyAAwCYAMEAG4gGgAyAAwCbhAfAAIADAJuIA8AIQAcAQYAbkAhABRlDABgAQQAYAIAADMh" +
+ "KABiAQYAIgISAHAQFgACABsDNwAAAG4gHQAyAAwCbiAdAFIADAIbAwAAAABuIB0AMgAMAm4g" +
+ "HABiAAwCbhAfAAIADAJuIA8AIQASAREBYAEEAGACAQAzIRMAYgEGABsCOwAAAG4gDwAhACIB" +
+ "DwAbAm8AAABwIBIAIQAnASIBFgBwICAAAQARAQYAAwACAAAA7RMAAFAAAAASERICYAACADQD" +
+ "SAABEHEQDAAAAGAAAwA2A0IAcRAMAAEAZwMEAJAABAX8IAAAVAAKAXEgBwAQAGIABgAiARIA" +
+ "cBAWAAEAGwIVAAAAbiAdACEADAFuIBoAMQAMARsCAQAAAG4gHQAhAAwBbiAaAEEADAFuIBoA" +
+ "UQAMARsCAgAAAG4gHQAhAAwBbhAfAAEADAFuIA8AEAAOAAEgKLoBISi/AAAAAAAAAAADAAAA" +
+ "AAAAAAIAAACsBQAADQAAALQFAAAOAAAAtAUAAAIAAAAEAAQAAQAAAAEAAAABAAAAAgAAAAEA" +
+ "AAADAAAAAQAAAAQAAAABAAAABQAAAAEAAAAQAAAAAQAAABEAAAABAAAAHAAAAAMAAAAYABEA" +
+ "GQAAAAMAAAAOABEAGQAAAAIAAAAAAAAAAgAAAAEAAQACAAAAAgACAAIAAAADAAMAAwAAAAQA" +
+ "BAAEAAAAAgAAAAUABQACAAAAEAAQAAIAAAARABEAAQAAABcAAAACAAAAGgAaAAEgAAIgKAAB" +
+ "KQAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAABiwgbDI6IAAGLCBv" +
+ "MjogAAYsIHMyOiAACDxjbGluaXQ+AAY8aW5pdD4AAUIAAUMAAUQAAUYAJ0ZBSUxVUkVfVFlQ" +
+ "RV9MSU5LRVJfTUVUSE9EX1JFVFVSTlNfTlVMTAAhRkFJTFVSRV9UWVBFX0xJTktFUl9NRVRI" +
+ "T0RfVEhST1dTABFGQUlMVVJFX1RZUEVfTk9ORQAhRkFJTFVSRV9UWVBFX1RBUkdFVF9NRVRI" +
+ "T0RfVEhST1dTAA9GYWlsdXJlIFR5cGUgKyAAAUkAA0lJSQANSU5WT0tFX1NUQVRJQwABSgAB" +
+ "TAACTEMAAkxEAAJMRgACTEkAAkxKAAJMTAAETExMTAAiTFRlc3RMaW5rZXJNZXRob2RNaW5p" +
+ "bWFsQXJndW1lbnRzOwACTFoAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NhbGxl" +
+ "ZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTGlua2Vy" +
+ "TWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0aG9kSGFu" +
+ "ZGxlS2luZDsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0" +
+ "cmVhbTsAH0xqYXZhL2xhbmcvQXJpdGhtZXRpY0V4Y2VwdGlvbjsAGkxqYXZhL2xhbmcvQXNz" +
+ "ZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5nL0NsYXNzOwAiTGphdmEvbGFuZy9JbnN0YW50aWF0" +
+ "aW9uRXhjZXB0aW9uOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" +
+ "GUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEv" +
+ "bGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xh" +
+ "bmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RI" +
+ "YW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZh" +
+ "L2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ACdSZXR1cm5pbmcgbnVsbCBpbnN0ZWFkIG9mIENh" +
+ "bGxTaXRlIGZvciAAAVMAJVRlc3RMaW5rZXJNZXRob2RNaW5pbWFsQXJndW1lbnRzLmphdmEA" +
+ "JVRocm93aW5nIEFyaXRobWV0aWNFeGNlcHRpb24gaW4gYWRkKCkAMVRocm93aW5nIEluc3Rh" +
+ "bnRpYXRpb25FeGNlcHRpb24gaW4gbGlua2VyTWV0aG9kKCkAAVYAA1ZCQgADVkNDAANWREQA" +
+ "A1ZGRgADVklJAARWSUlJAANWSkoAAlZMAANWTEwAA1ZTUwACVloAAVoAAlpMAAFhAANhZGQA" +
+ "BmFwcGVuZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6" +
+ "IAARYXNzZXJ0RXF1YWxzIGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMg" +
+ "ZjE6IAARYXNzZXJ0RXF1YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVh" +
+ "bHMgczE6IAASYXNzZXJ0RXF1YWxzOiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZh" +
+ "bHVlOiAAAWIAAmIxAAJiMgACYzEAAmMyAAZjYWxsZXIAAmQxAAJkMgASZW1pdHRlcjogamFj" +
+ "ay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgALZmFpbHVyZVR5cGUACmZp" +
+ "bmRTdGF0aWMAEGZvcmNlRmFpbHVyZVR5cGUAAmkxAAJpMgASaW52b2tlTWV0aG9kSGFuZGxl" +
+ "AARraW5kAAJsMQACbDIADGxpbmtlck1ldGhvZAAabGlua2VyTWV0aG9kIGZhaWx1cmUgdHlw" +
+ "ZSAACm1ldGhvZFR5cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1" +
+ "cm5UeXBlAAJzMQACczIABHRlc3QABHRoaXMACHRvU3RyaW5nAAV2YWx1ZQABeAABeQAeAAcd" +
+ "Li08PAJ5OwAcAAcOAC8CS1oHDmmHlwBWAltcBw48AFsCXV4HDjwAdAJgYQcOWgBvAmVmBw5a" +
+ "AGUCamsHDjwAagJubwcOWgB5AnV3Bw48tAB/Anp7Bw4tIKUgAGACensHDjwAUAF/Bw4tARoQ" +
+ "ADkDX3RyBw4BGxBpAwBzGGkBJA8taYeXAEgDZ4ABgQEHLId4LZYBLw8CeywtAAAHBE0cAhgE" +
+ "GARrHAEdCARNHAMYGBgRGBliGAZsGwVzF29zF0t4GAQCCgF+HAEYFAMWABdLFQAFAA8AAAoB" +
+ "CgEKAQoBCgCIgAS8CwGBgATsCwEKhAwBCcgMAQmoDQEJiA4BCewOAQnQDwEJsBABCZQRAQmM" +
+ "EgEJhBMBCeQTAQqwFAEJlBYAEwAAAAAAAAABAAAAAAAAAAEAAACBAAAAcAAAAAIAAAAdAAAA" +
+ "dAIAAAMAAAAcAAAA6AIAAAQAAAAHAAAAOAQAAAUAAAAiAAAAcAQAAAcAAAABAAAAgAUAAAYA" +
+ "AAABAAAAhAUAAAgAAAABAAAApAUAAAMQAAACAAAArAUAAAEgAAAPAAAAvAUAAAYgAAABAAAA" +
+ "xAsAAAEQAAAVAAAA7AsAAAIgAACBAAAAoAwAAAMgAAAPAAAAZBMAAAQgAAACAAAAAhQAAAUg" +
+ "AAABAAAAMhQAAAAgAAABAAAAORQAAAAQAAABAAAAiBQAAA==";
+}
diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000000..b96e18486f
--- /dev/null
+++ b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,108 @@
+/* Generated by build-test.sh from TestLinkerMethodMultipleArgumentTypes.java */
+public class TestDataLinkerMethodMultipleArgumentTypes {
+ public static final String BASE64_DEX_FILE =
+ "ZGV4CjAzOADmj8ccx56N3pWZ9IunuZvI0eWD+wmFmSnEFQAAcAAAAHhWNBIAAAAAAAAAANwU" +
+ "AACTAAAAcAAAAB0AAAC8AgAAHQAAADADAAADAAAAjAQAACIAAACkBAAAAQAAALgFAADkDwAA" +
+ "4AUAAEQMAABHDAAASgwAAFIMAABaDAAAYgwAAGoMAAByDAAAegwAAIIMAACKDAAAkgwAAJwM" +
+ "AACkDAAApwwAAKoMAACtDAAAsAwAAMYMAADNDAAA0AwAANUMAADkDAAA5wwAAOoMAADuDAAA" +
+ "8gwAAPYMAAD6DAAA/gwAAAINAAAIDQAAGA0AAEENAABFDQAAeg0AAKMNAADWDQAABw4AACYO" +
+ "AABCDgAATA4AAGMOAAB/DgAAkQ4AAKQOAAC6DgAAzg4AAOIOAAD9DgAAEQ8AACgPAABFDwAA" +
+ "ag8AAIsPAAC0DwAA0w8AANYPAAACEAAABRAAAAoQAAAPEAAAFBAAABkQAAAdEAAAIhAAACcQ" +
+ "AAArEAAAMBAAADUQAAA5EAAAPBAAAEUQAABJEAAATBAAAFEQAABZEAAAaBAAAHYQAACJEAAA" +
+ "nBAAAK8QAADCEAAA1RAAAOgQAAD7EAAADxEAABsRAAAvEQAAMhEAADYRAAA6EQAASBEAAFsR" +
+ "AABmEQAAahEAAG4RAAB2EQAAgREAAI0RAACREQAAlREAAKIRAAC2EQAAxREAAM0RAADREQAA" +
+ "1REAAOERAADtEQAA8REAAPURAAD/EQAAExIAABkSAAAdEgAAIRIAAC8SAAA6EgAAURIAAF0S" +
+ "AABlEgAAaxIAAG4SAABzEgAAdhIAAH8SAACLEgAAjxIAAJMSAACfEgAArBIAALISAAC4EgAA" +
+ "whIAAMYSAADLEgAAzxIAANMSAADXEgAA2xIAAN8SAADjEgAA5xIAAOsSAADyEgAA9RIAAA0A" +
+ "AAAOAAAADwAAABAAAAATAAAAFgAAACAAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAApAAAA" +
+ "KgAAACwAAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADoA" +
+ "AABGAAAAEwAAAAQAAAAAAAAAFAAAAAQAAACICwAAFwAAABEAAAAAAAAAGAAAABIAAACQCwAA" +
+ "GQAAABIAAACYCwAAGgAAABIAAACgCwAAGwAAABIAAACoCwAAHAAAABIAAACwCwAAHQAAABIA" +
+ "AAC4CwAAHQAAABIAAADACwAAIQAAABIAAADICwAAHwAAABUAAADQCwAAHgAAABcAAADwCwAA" +
+ "OgAAABsAAAAAAAAAOwAAABsAAAD8CwAAPAAAABsAAAAEDAAAPQAAABsAAAAMDAAAPgAAABsA" +
+ "AAAUDAAAPwAAABsAAACoCwAAQAAAABsAAACICwAAQQAAABsAAAAcDAAAQgAAABsAAAC4CwAA" +
+ "QwAAABsAAAAkDAAAQgAAABsAAADACwAAQwAAABsAAAAsDAAAQgAAABsAAAA0DAAARAAAABsA" +
+ "AAA8DAAARQAAABsAAADICwAASAAAABwAAAC4CwAABgAEAFwAAAAKAAoAFQAAABMADQB7AAAA" +
+ "BgANAAsAAAAGAA0ADAAAAAYAAAARAAAABgABAEoAAAAGAA4ATQAAAAYADwBNAAAABgAQAE0A" +
+ "AAAGABEATQAAAAYAEwBNAAAABgAUAE0AAAAGABYATQAAAAYAGABNAAAABgAaAE0AAAAGABsA" +
+ "VgAAAAYACwB0AAAABgATAIMAAAANABIAfQAAAA0AFwB9AAAADgAVAAwAAAAQAA0ADAAAABAA" +
+ "HABoAAAAEQAcAGgAAAASAA0ADAAAABIAAwBLAAAAEgAEAEsAAAASAAUASwAAABIABgBLAAAA" +
+ "EgAHAEsAAAASAAgASwAAABIACQBLAAAAEgAKAEsAAAASAAIAhQAAABYAGQAMAAAAGAAMAGsA" +
+ "AABpFAAABgAAAAEAAAAQAAAAAAAAADkAAABgCwAAkhQAAAAAAAAEAAAADgAAAAEAAACpEwAA" +
+ "AgAAAEcUAABgFAAAAQAAAGAUAAABAAAAAAAAAPgSAAAEAAAAEgBnAAAADgABAAEAAQAAAP4S" +
+ "AAAEAAAAcBATAAAADgADAAIAAAAAAAMTAAADAAAAkAABAg8AAAAFAAIAAgAAAAoTAAAoAAAA" +
+ "M0MDAA4AIgAOACIBEgBwEBYAAQAbAk4AAABuIB0AIQAMAW4gGgAxAAwBGwICAAAAbiAdACEA" +
+ "DAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAABITAAAoAAAAM0MDAA4AIgAOACIB" +
+ "EgBwEBYAAQAbAk8AAABuIB0AIQAMAW4gFwAxAAwBGwIDAAAAbiAdACEADAFuIBcAQQAMAW4Q" +
+ "HwABAAwBcCASABAAJwAIAAQAAwAAABoTAAAqAAAALwAEBjkAAwAOACIADgAiARIAcBAWAAEA" +
+ "GwJQAAAAbiAdACEADAFuMBgAQQUMARsCBAAAAG4gHQAhAAwBbjAYAGEHDAFuEB8AAQAMAXAg" +
+ "EgAQACcABQACAAIAAAAiEwAAKgAAAC0AAwQ5AAMADgAiAA4AIgESAHAQFgABABsCUQAAAG4g" +
+ "HQAhAAwBbiAZADEADAEbAgUAAABuIB0AIQAMAW4gGQBBAAwBbhAfAAEADAFwIBIAEAAnAAUA" +
+ "AgACAAAAKhMAACgAAAAzQwMADgAiAA4AIgESAHAQFgABABsCUgAAAG4gHQAhAAwBbiAaADEA" +
+ "DAEbAgYAAABuIB0AIQAMAW4gGgBBAAwBbhAfAAEADAFwIBIAEAAnAAgABAADAAAAMhMAACoA" +
+ "AAAxAAQGOQADAA4AIgAOACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4wGwBBBQwBGwIHAAAA" +
+ "biAdACEADAFuMBsAYQcMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAADoTAAAzAAAAM0MDAA4A" +
+ "OAMLADgECQBuIBQAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJVAAAAbiAdACEADAFuIBwA" +
+ "MQAMARsCCAAAAG4gHQAhAAwBbiAcAEEADAFuEB8AAQAMAXAgEgAQACcAAAAFAAIAAgAAAEMT" +
+ "AAAzAAAAM0MDAA4AOAMLADgECQBuIBUAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJUAAAA" +
+ "biAdACEADAFuIB0AMQAMARsCCQAAAG4gHQAhAAwBbiAdAEEADAFuEB8AAQAMAXAgEgAQACcA" +
+ "AAAFAAIAAgAAAFETAAAoAAAAM0MDAA4AIgAOACIBEgBwEBYAAQAbAlQAAABuIB0AIQAMAW4g" +
+ "GgAxAAwBGwIJAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAEAAEAAgAAAFsT" +
+ "AAAdAAAAOQMcACIADgAiARIAcBAWAAEAGwJXAAAAbiAdACEADAFuIB4AMQAMAW4QHwABAAwB" +
+ "cCASABAAJwAOAAAAFgAPAAQAAABmEwAAbAAAAGIDAgAiBBIAcBAWAAQAGwUoAAAAbiAdAFQA" +
+ "DARuIB0AhAAMBBsFAAAAAG4gHQBUAAwEbiAcAJQADARuEB8ABAAMBG4gEQBDAHEQDQAKABIT" +
+ "cSAIALMAEwNhAHEgBQDDABMDAARxIAgA0wASE3EgCADjABQDmpkxQXEgBwDzABgEmpmZmZmZ" +
+ "AUAFABAAcUAGAFQQGwMSAAAACAASAHEgCwADABwDBgAIABMAcSAKAAMAFwQVzVsHBQAUAHFA" +
+ "CQBUEBwDBgBuQCEAN5gMAiIDFgBwICAAIwARAwQAAgACAAAAmRMAABEAAACQAAID/CAAADIA" +
+ "CgFxIAgAEABiAAIAkAECA24gEAAQAA4AAAACAAEAAAAAAKQTAAADAAAAYAAAAA8AAAAAAAAA" +
+ "AAAAAAMAAAAAAAAAAwAAAOAFAAAOAAAA6AUAAA8AAAD0BQAAAgAAAAQABAABAAAAAQAAAAEA" +
+ "AAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAAFAAAAAQAAABAAAAABAAAAEQAAAAEAAAAcAAAA" +
+ "DQAAABgAEQAZABwAAAABABoABAADAAIAEQAPAAUAAAADAAAADwARABkAAAACAAAAAAAAAAIA" +
+ "AAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAFAAUAAgAAABAAEAACAAAAEQARAAEAAAAXAAAA" +
+ "AgAAABoAGgABIAABKAAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAA" +
+ "BiwgbDI6IAAGLCBvMjogAAYsIHMyOiAABjwqPjtKKQAIPGNsaW5pdD4ABjxpbml0PgABQgAB" +
+ "QwABRAABRgAUR2V0Qm9vdHN0cmFwUnVuQ291bnQABUhlbGxvAAFJAANJSUkADUlOVk9LRV9T" +
+ "VEFUSUMAAUoAAUwAAkxDAAJMRAACTEYAAkxJAAJMSgACTEwABExMTEwADkxMTExaQkNTSUZE" +
+ "TExKACdMVGVzdExpbmtlck1ldGhvZE11bHRpcGxlQXJndW1lbnRUeXBlczsAAkxaADNMY29t" +
+ "L2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsAJ0xjb20v" +
+ "YW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NvbnN0YW50OwAxTGNvbS9hbmRyb2lkL2phY2sv" +
+ "YW5ub3RhdGlvbnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5u" +
+ "b3RhdGlvbnMvTWV0aG9kSGFuZGxlS2luZDsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1" +
+ "cmU7ABpMZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAITGlua2luZyAAFUxqYXZhL2lvL1By" +
+ "aW50U3RyZWFtOwAaTGphdmEvbGFuZy9Bc3NlcnRpb25FcnJvcjsAEExqYXZhL2xhbmcvQ2xh" +
+ "c3MAEUxqYXZhL2xhbmcvQ2xhc3M7ABRMamF2YS9sYW5nL0NsYXNzPCo+OwASTGphdmEvbGFu" +
+ "Zy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRl" +
+ "cjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" +
+ "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" +
+ "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" +
+ "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7" +
+ "AAFTACpUZXN0TGlua2VyTWV0aG9kTXVsdGlwbGVBcmd1bWVudFR5cGVzLmphdmEAAVYAA1ZC" +
+ "QgADVkNDAANWREQAA1ZGRgACVkkAA1ZJSQADVkpKAAJWTAADVkxMAANWU1MAAlZaAAFaAAda" +
+ "QkNTSUZEAAJaTAABYQADYWRkAAZhcHBlbmQADWFyZ3VtZW50VHlwZXMADGFzc2VydEVxdWFs" +
+ "cwARYXNzZXJ0RXF1YWxzIGIxOiAAEWFzc2VydEVxdWFscyBjMTogABFhc3NlcnRFcXVhbHMg" +
+ "ZDE6IAARYXNzZXJ0RXF1YWxzIGYxOiAAEWFzc2VydEVxdWFscyBpMTogABFhc3NlcnRFcXVh" +
+ "bHMgbDE6IAARYXNzZXJ0RXF1YWxzIHMxOiAAEmFzc2VydEVxdWFsczogbzE6IAAKYXNzZXJ0" +
+ "VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAFiAAJiMQACYjIADGJvb2xlYW5WYWx1ZQARYm9v" +
+ "dHN0cmFwUnVuQ291bnQACWJ5dGVWYWx1ZQACYzEAAmMyAAZjYWxsZXIACWNoYXJWYWx1ZQAK" +
+ "Y2xhc3NWYWx1ZQACZDEAAmQyAAtkb3VibGVWYWx1ZQASZW1pdHRlcjogamFjay00LjI1AA1l" +
+ "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAKZmxvYXRWYWx1ZQAC" +
+ "aTEAAmkyAAhpbnRWYWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAJsMQACbDIADGxp" +
+ "bmtlck1ldGhvZAAJbG9uZ1ZhbHVlABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MACm1ldGhvZFR5" +
+ "cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1cm5UeXBlAAJzMQAC" +
+ "czIACnNob3J0VmFsdWUAC3N0cmluZ1ZhbHVlAAR0ZXN0AAR0aGlzAAh0b1N0cmluZwACdjEA" +
+ "A3YxMAACdjIAAnYzAAJ2NAACdjUAAnY2AAJ2NwACdjgAAnY5AAV2YWx1ZQABeAABeQAeAAcO" +
+ "OQAcAAcOADECSlkHDgBZAlpbBw48AF4CX2AHDjwAdwJkZQcOWgByAmprBw5aAGgCbm8HDjwA" +
+ "bQJzdAcOWgB8Ant9Bw48tACCAQKAAYEBBw4tIKUgAGMCgAGBAQcOPABTAZEBBw4tARoQADkN" +
+ "YXp4hwGJAYoBiwGMAY0BjgGPAQCIAQQTkAEQLgcOASQPPEtaWktppYd4iGkDAnkYAE4CkgGT" +
+ "AQcOlngASgAHDgAABwVMHAIYBBgEcBwBHQkETBwNGBgYERgZGBwYABgBGBoYBBgDGAIYERgP" +
+ "GAVnGAZxGwF5F3R2HAodCAFbHAE/HQgBXRwBAAEdCAFhHAEDYR0IAYEBHAEiAAQdCAFvHAEE" +
+ "AR0IAWwcAXCamTFBHQgBZRwB8ZqZmZmZmQFAHQgBggEcARcSHQgBYhwBGAYdCAF1HAFmFc1b" +
+ "B3kXSn4YBAILAZABHAkXARc2Fy8XNxdHFy8XKxcKFzMCDAGQARwBGBQNFgAXShUBBAEEAQRh" +
+ "JAAEBAFwmpkxQfGamZmZmZkBQBcSGAZmFc1bBwEADwEACgCIgAT8CwGBgASUDAIKrAwBCcQM" +
+ "AQmkDQEJhA4BCegOAQnMDwEJrBABCZARAQmIEgEJgBMBCeATAQqsFAEJlBYCAcgWEwAAAAAA" +
+ "AAABAAAAAAAAAAEAAACTAAAAcAAAAAIAAAAdAAAAvAIAAAMAAAAdAAAAMAMAAAQAAAADAAAA" +
+ "jAQAAAUAAAAiAAAApAQAAAcAAAABAAAAtAUAAAYAAAABAAAAuAUAAAgAAAABAAAA2AUAAAMQ" +
+ "AAADAAAA4AUAAAEgAAAQAAAA/AUAAAYgAAABAAAAYAsAAAEQAAAUAAAAiAsAAAIgAACTAAAA" +
+ "RAwAAAMgAAAQAAAA+BIAAAQgAAADAAAAqRMAAAUgAAABAAAAaRQAAAAgAAABAAAAkhQAAAAQ" +
+ "AAABAAAA3BQAAA==";
+}
diff --git a/test/Android.bp b/test/Android.bp
index d3244a683a..00c890a834 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -274,6 +274,7 @@ art_cc_defaults {
"933-misc-events/misc_events.cc",
"936-search-onload/search_onload.cc",
"944-transform-classloaders/classloader.cc",
+ "945-obsolete-native/obsolete_native.cc",
],
shared_libs: [
"libbase",
diff --git a/test/dexdump/invoke-custom.dex b/test/dexdump/invoke-custom.dex
new file mode 100644
index 0000000000..67261cad79
--- /dev/null
+++ b/test/dexdump/invoke-custom.dex
Binary files differ
diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst
new file mode 100644
index 0000000000..3540bd10d5
--- /dev/null
+++ b/test/dexdump/invoke-custom.lst
@@ -0,0 +1,6 @@
+#invoke-custom.dex
+0x000003fc 8 com.android.jack.java7.invokecustom.test004.Tests <init> ()V Tests.java 35
+0x00000414 6 com.android.jack.java7.invokecustom.test004.Tests add (II)I Tests.java 55
+0x0000042c 166 com.android.jack.java7.invokecustom.test004.Tests linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; Tests.java 62
+0x000004e4 24 com.android.jack.java7.invokecustom.test004.Tests main ([Ljava/lang/String;)V Tests.java 82
+0x0000050c 22 com.android.jack.java7.invokecustom.test004.Tests test ()V Tests.java 78
diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt
new file mode 100644
index 0000000000..e92549a086
--- /dev/null
+++ b/test/dexdump/invoke-custom.txt
@@ -0,0 +1,254 @@
+Processing 'invoke-custom.dex'...
+Opened 'invoke-custom.dex', DEX version '038'
+DEX file header:
+magic : 'dex\n038\0'
+checksum : db57516f
+signature : 57be...ffc4
+file_size : 3276
+header_size : 112
+link_size : 0
+link_off : 0 (0x000000)
+string_ids_size : 82
+string_ids_off : 112 (0x000070)
+type_ids_size : 31
+type_ids_off : 440 (0x0001b8)
+proto_ids_size : 16
+proto_ids_off : 564 (0x000234)
+field_ids_size : 3
+field_ids_off : 756 (0x0002f4)
+method_ids_size : 18
+method_ids_off : 780 (0x00030c)
+class_defs_size : 1
+class_defs_off : 932 (0x0003a4)
+data_size : 2304
+data_off : 972 (0x0003cc)
+
+Class #0 header:
+class_idx : 10
+access_flags : 1 (0x0001)
+superclass_idx : 15
+interfaces_off : 0 (0x000000)
+source_file_idx : 38
+annotations_off : 1316 (0x000524)
+class_data_off : 3014 (0x000bc6)
+static_fields_size : 1
+instance_fields_size: 0
+direct_methods_size : 4
+virtual_methods_size: 1
+
+Class #0 annotations:
+Annotations on method #1 'add'
+ VISIBILITY_BUILD Lcom/android/jack/annotations/CalledByInvokeCustom; argumentTypes={ I I } invokeMethodHandle={ Lcom/android/jack/annotations/LinkerMethodHandle; argumentTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Z B C S I F D Ljava/lang/String; Ljava/lang/Class; J } enclosingType=Lcom/android/jack/java7/invokecustom/test004/Tests; kind=INVOKE_STATIC name="linkerMethod" } methodHandleExtraArgs={ Lcom/android/jack/annotations/Constant; booleanValue={ true } Lcom/android/jack/annotations/Constant; byteValue={ 1 } Lcom/android/jack/annotations/Constant; charValue={ 97 } Lcom/android/jack/annotations/Constant; shortValue={ 1024 } Lcom/android/jack/annotations/Constant; intValue={ 1 } Lcom/android/jack/annotations/Constant; floatValue={ 11.1 } Lcom/android/jack/annotations/Constant; doubleValue={ 2.2 } Lcom/android/jack/annotations/Constant; stringValue={ "Hello" } Lcom/android/jack/annotations/Constant; classValue={ Lcom/android/jack/java7/invokecustom/test004/Tests; } Lcom/android/jack/annotations/Constant; longValue={ 123456789 } } name="add" returnType=I
+Annotations on method #2 'linkerMethod'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "ZBCSIFD" "Ljava/lang/String;" "Ljava/lang/Class" "<*>;J)" "Ljava/lang/invoke/CallSite;" }
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+Annotations on method #4 'test'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+ VISIBILITY_RUNTIME Lorg/junit/Test;
+
+Class #0 -
+ Class descriptor : 'Lcom/android/jack/java7/invokecustom/test004/Tests;'
+ Access flags : 0x0001 (PUBLIC)
+ Superclass : 'Ljava/lang/Object;'
+ Interfaces -
+ Static fields -
+ #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ name : 'fieldCallSite'
+ type : 'Ljava/lang/invoke/CallSite;'
+ access : 0x0009 (PUBLIC STATIC)
+ Instance fields -
+ Direct methods -
+ #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ name : '<init>'
+ type : '()V'
+ access : 0x10001 (PUBLIC CONSTRUCTOR)
+ code -
+ registers : 1
+ ins : 1
+ outs : 1
+ insns size : 4 16-bit code units
+0003ec: |[0003ec] com.android.jack.java7.invokecustom.test004.Tests.<init>:()V
+0003fc: 7010 0600 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0006
+000402: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=35
+ locals :
+ 0x0000 - 0x0004 reg=0 this Lcom/android/jack/java7/invokecustom/test004/Tests;
+
+ #1 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ name : 'add'
+ type : '(II)I'
+ access : 0x000a (PRIVATE STATIC)
+ code -
+ registers : 3
+ ins : 2
+ outs : 0
+ insns size : 3 16-bit code units
+000404: |[000404] com.android.jack.java7.invokecustom.test004.Tests.add:(II)I
+000414: 9000 0102 |0000: add-int v0, v1, v2
+000418: 0f00 |0002: return v0
+ catches : (none)
+ positions :
+ 0x0000 line=55
+ locals :
+ 0x0000 - 0x0003 reg=1 (null) I
+ 0x0000 - 0x0003 reg=2 (null) I
+
+ #2 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ name : 'linkerMethod'
+ type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;'
+ access : 0x000a (PRIVATE STATIC)
+ code -
+ registers : 24
+ ins : 15
+ outs : 6
+ insns size : 83 16-bit code units
+00041c: |[00041c] com.android.jack.java7.invokecustom.test004.Tests.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
+00042c: 7110 1100 0c00 |0000: invoke-static {v12}, Ljunit/framework/Assert;.assertTrue:(Z)V // method@0011
+000432: 1212 |0003: const/4 v2, #int 1 // #1
+000434: 7120 0d00 d200 |0004: invoke-static {v2, v13}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00043a: 1302 6100 |0007: const/16 v2, #int 97 // #61
+00043e: 7120 0a00 e200 |0009: invoke-static {v2, v14}, Ljunit/framework/Assert;.assertEquals:(CC)V // method@000a
+000444: 1302 0004 |000c: const/16 v2, #int 1024 // #400
+000448: 7120 0d00 f200 |000e: invoke-static {v2, v15}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00044e: 1212 |0011: const/4 v2, #int 1 // #1
+000450: 0200 1000 |0012: move/from16 v0, v16
+000454: 7120 0d00 0200 |0014: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+00045a: 1202 |0017: const/4 v2, #int 0 // #0
+00045c: 1403 9a99 3141 |0018: const v3, #float 11.1 // #4131999a
+000462: 0200 1100 |001b: move/from16 v0, v17
+000466: 7130 0c00 0302 |001d: invoke-static {v3, v0, v2}, Ljunit/framework/Assert;.assertEquals:(FFF)V // method@000c
+00046c: 1606 0000 |0020: const-wide/16 v6, #int 0 // #0
+000470: 1802 9a99 9999 9999 0140 |0022: const-wide v2, #double 2.2 // #400199999999999a
+00047a: 0504 1200 |0027: move-wide/from16 v4, v18
+00047e: 7706 0b00 0200 |0029: invoke-static/range {v2, v3, v4, v5, v6, v7}, Ljunit/framework/Assert;.assertEquals:(DDD)V // method@000b
+000484: 1b02 0700 0000 |002c: const-string/jumbo v2, "Hello" // string@00000007
+00048a: 0800 1400 |002f: move-object/from16 v0, v20
+00048e: 7120 1000 0200 |0031: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/String;Ljava/lang/String;)V // method@0010
+000494: 1c02 0a00 |0034: const-class v2, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
+000498: 0800 1500 |0036: move-object/from16 v0, v21
+00049c: 7120 0f00 0200 |0038: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000f
+0004a2: 1702 15cd 5b07 |003b: const-wide/32 v2, #float 1.6536e-34 // #075bcd15
+0004a8: 0500 1600 |003e: move-wide/from16 v0, v22
+0004ac: 7140 0e00 3210 |0040: invoke-static {v2, v3, v0, v1}, Ljunit/framework/Assert;.assertEquals:(JJ)V // method@000e
+0004b2: 7100 0900 0000 |0043: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0009
+0004b8: 0c02 |0046: move-result-object v2
+0004ba: 1c03 0a00 |0047: const-class v3, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
+0004be: 6e40 0800 32ba |0049: invoke-virtual {v2, v3, v10, v11}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0008
+0004c4: 0c02 |004c: move-result-object v2
+0004c6: 2203 1400 |004d: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0014
+0004ca: 7020 0700 2300 |004f: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0007
+0004d0: 1103 |0052: return-object v3
+ catches : (none)
+ positions :
+ 0x0000 line=62
+ 0x0003 line=63
+ 0x0007 line=64
+ 0x000c line=65
+ 0x0011 line=66
+ 0x0017 line=67
+ 0x0020 line=68
+ 0x002c line=69
+ 0x0034 line=70
+ 0x003b line=71
+ 0x0043 line=72
+ 0x004d line=73
+ locals :
+ 0x0000 - 0x0053 reg=9 (null) Ljava/lang/invoke/MethodHandles$Lookup;
+ 0x0000 - 0x0053 reg=10 (null) Ljava/lang/String;
+ 0x0000 - 0x0053 reg=11 (null) Ljava/lang/invoke/MethodType;
+ 0x0000 - 0x0053 reg=12 (null) Z
+ 0x0000 - 0x0053 reg=13 (null) B
+ 0x0000 - 0x0053 reg=14 (null) C
+ 0x0000 - 0x0053 reg=15 (null) S
+ 0x0000 - 0x0053 reg=16 (null) I
+ 0x0000 - 0x0053 reg=17 (null) F
+ 0x0000 - 0x0053 reg=18 (null) D
+ 0x0000 - 0x0053 reg=20 (null) Ljava/lang/String;
+ 0x0000 - 0x0053 reg=21 (null) Ljava/lang/Class;
+ 0x0000 - 0x0053 reg=22 (null) J
+
+ #3 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ name : 'main'
+ type : '([Ljava/lang/String;)V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 4
+ ins : 1
+ outs : 2
+ insns size : 12 16-bit code units
+0004d4: |[0004d4] com.android.jack.java7.invokecustom.test004.Tests.main:([Ljava/lang/String;)V
+0004e4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0004e8: 1221 |0002: const/4 v1, #int 2 // #2
+0004ea: 1232 |0003: const/4 v2, #int 3 // #3
+0004ec: fc20 0000 2100 |0004: invoke-custom {v1, v2}, call_site@0000
+0004f2: 0a01 |0007: move-result v1
+0004f4: 6e20 0500 1000 |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(I)V // method@0005
+0004fa: 0e00 |000b: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=82
+ 0x000b line=83
+ locals :
+ 0x0000 - 0x000c reg=3 (null) [Ljava/lang/String;
+
+ Virtual methods -
+ #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ name : 'test'
+ type : '()V'
+ access : 0x0001 (PUBLIC)
+ code -
+ registers : 3
+ ins : 1
+ outs : 2
+ insns size : 11 16-bit code units
+0004fc: |[0004fc] com.android.jack.java7.invokecustom.test004.Tests.test:()V
+00050c: 1220 |0000: const/4 v0, #int 2 // #2
+00050e: 1231 |0001: const/4 v1, #int 3 // #3
+000510: fc20 0100 1000 |0002: invoke-custom {v0, v1}, call_site@0001
+000516: 0a00 |0005: move-result v0
+000518: 1251 |0006: const/4 v1, #int 5 // #5
+00051a: 7120 0d00 0100 |0007: invoke-static {v1, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
+000520: 0e00 |000a: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=78
+ 0x000a line=79
+ locals :
+ 0x0000 - 0x000b reg=2 this Lcom/android/jack/java7/invokecustom/test004/Tests;
+
+ source_file_idx : 38 (Tests.java)
+
+Method handle #0:
+ type : invoke-static
+ target : Lcom/android/jack/java7/invokecustom/test004/Tests; linkerMethod
+ target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
+Call site #0:
+ link_argument[0] : 0 (MethodHandle)
+ link_argument[1] : add (String)
+ link_argument[2] : (II)I (MethodType)
+ link_argument[3] : 1 (int)
+ link_argument[4] : 1 (int)
+ link_argument[5] : 97 (int)
+ link_argument[6] : 1024 (int)
+ link_argument[7] : 1 (int)
+ link_argument[8] : 11.1 (float)
+ link_argument[9] : 2.2 (double)
+ link_argument[10] : Hello (String)
+ link_argument[11] : Tests (Class)
+ link_argument[12] : 123456789 (long)
+Call site #1:
+ link_argument[0] : 0 (MethodHandle)
+ link_argument[1] : add (String)
+ link_argument[2] : (II)I (MethodType)
+ link_argument[3] : 1 (int)
+ link_argument[4] : 1 (int)
+ link_argument[5] : 97 (int)
+ link_argument[6] : 1024 (int)
+ link_argument[7] : 1 (int)
+ link_argument[8] : 11.1 (float)
+ link_argument[9] : 2.2 (double)
+ link_argument[10] : Hello (String)
+ link_argument[11] : Tests (Class)
+ link_argument[12] : 123456789 (long)
diff --git a/test/dexdump/invoke-custom.xml b/test/dexdump/invoke-custom.xml
new file mode 100644
index 0000000000..2a2966738a
--- /dev/null
+++ b/test/dexdump/invoke-custom.xml
@@ -0,0 +1,89 @@
+<api>
+<package name="com.android.jack.java7.invokecustom.test004"
+>
+<class name="Tests"
+ extends="java.lang.Object"
+ interface="false"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<field name="fieldCallSite"
+ type="java.lang.invoke.CallSite"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</field>
+<constructor name="Tests"
+ type="com.android.jack.java7.invokecustom.test004.Tests"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+<method name="main"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="test"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+</class>
+<method_handle index="0"
+ type="invoke-static"
+ target_class="Lcom/android/jack/java7/invokecustom/test004/Tests;"
+ target_member="linkerMethod"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<call_site index="0">
+<link_argument index="0" type="MethodHandle" value="0"/>
+<link_argument index="1" type="String" values="add"/>
+<link_argument index="2" type="MethodType" value="(II)I"/>
+<link_argument index="3" type="int" value="1"/>
+<link_argument index="4" type="int" value="1"/>
+<link_argument index="5" type="int" value="97"/>
+<link_argument index="6" type="int" value="1024"/>
+<link_argument index="7" type="int" value="1"/>
+<link_argument index="8" type="float" value="11.1"/>
+<link_argument index="9" type="double" value="2.2"/>
+<link_argument index="10" type="String" value="Hello"/>
+<link_argument index="11" type="Class" value="Tests"/>
+<link_argument index="12" type="long" value="123456789"/>
+</call_site>
+<call_site index="1">
+<link_argument index="0" type="MethodHandle" value="0"/>
+<link_argument index="1" type="String" values="add"/>
+<link_argument index="2" type="MethodType" value="(II)I"/>
+<link_argument index="3" type="int" value="1"/>
+<link_argument index="4" type="int" value="1"/>
+<link_argument index="5" type="int" value="97"/>
+<link_argument index="6" type="int" value="1024"/>
+<link_argument index="7" type="int" value="1"/>
+<link_argument index="8" type="float" value="11.1"/>
+<link_argument index="9" type="double" value="2.2"/>
+<link_argument index="10" type="String" value="Hello"/>
+<link_argument index="11" type="Class" value="Tests"/>
+<link_argument index="12" type="long" value="123456789"/>
+</call_site>
+</package>
+</api>
diff --git a/test/etc/default-build b/test/etc/default-build
index e9e388646a..3d7b7dd046 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -60,6 +60,12 @@ else
HAS_SRC_DEX2OAT_UNRESOLVED=false
fi
+# Allow overriding ZIP_COMPRESSION_METHOD with e.g. 'store'
+ZIP_COMPRESSION_METHOD="deflate"
+# Align every ZIP file made by calling $ZIPALIGN command?
+WITH_ZIP_ALIGN=false
+ZIP_ALIGN_BYTES="-1"
+
DX_FLAGS=""
SKIP_DX_MERGER="false"
EXPERIMENTAL=""
@@ -73,6 +79,7 @@ JACK_EXPERIMENTAL_ARGS["agents"]="-D jack.java.source.version=1.8 -D jack.androi
JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1"
+JACK_EXPERIMENTAL_ARGS["java-8"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
declare -A SMALI_EXPERIMENTAL_ARGS
SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"
@@ -118,6 +125,17 @@ while true; do
DEFAULT_EXPERIMENT=""
EXPERIMENTAL="${EXPERIMENTAL} $1"
shift
+ elif [ "x$1" = "x--zip-compression-method" ]; then
+ # Allow using different zip compression method, e.g. 'store'
+ shift
+ ZIP_COMPRESSION_METHOD="$1"
+ shift
+ elif [ "x$1" = "x--zip-align" ]; then
+ # Align ZIP entries to some # of bytes.
+ shift
+ WITH_ZIP_ALIGN=true
+ ZIP_ALIGN_BYTES="$1"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -146,6 +164,26 @@ for experiment in ${EXPERIMENTAL}; do
JAVAC_ARGS="${JAVAC_ARGS} ${JAVAC_EXPERIMENTAL_ARGS[${experiment}]}"
done
+#########################################
+
+# Catch all commands to 'ZIP' and prepend extra flags.
+# Optionally, zipalign results to some alignment.
+function zip() {
+ local zip_target="$1"
+ local entry_src="$2"
+ shift 2
+
+ command zip --compression-method "$ZIP_COMPRESSION_METHOD" "$zip_target" "$entry_src" "$@"
+
+ if "$WITH_ZIP_ALIGN"; then
+ # zipalign does not operate in-place, so write results to a temp file.
+ local tmp_file="$(mktemp)"
+ "$ZIPALIGN" -f "$ZIP_ALIGN_BYTES" "$zip_target" "$tmp_file"
+ # replace original zip target with our temp file.
+ mv "$tmp_file" "$zip_target"
+ fi
+}
+
if [ -e classes.dex ]; then
zip $TEST_NAME.jar classes.dex
exit 0
@@ -234,7 +272,7 @@ else
fi
fi
-if [ "${HAS_SMALI}" = "true" ]; then
+if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then
# Compile Smali classes
${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
@@ -246,7 +284,7 @@ if [ "${HAS_SMALI}" = "true" ]; then
fi
fi
-if [ "${HAS_SMALI_MULTIDEX}" = "true" ]; then
+if [ "${HAS_SMALI_MULTIDEX}" = "true" -a ${NEED_DEX} = "true" ]; then
# Compile Smali classes
${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 186a1513ee..7d218f1d61 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -64,6 +64,10 @@ TEST_IS_NDEBUG="n"
APP_IMAGE="y"
VDEX_FILTER=""
+# if "y", run 'sync' before dalvikvm to make sure all files from
+# build step (e.g. dex2oat) were finished writing.
+SYNC_BEFORE_RUN="n"
+
while true; do
if [ "x$1" = "x--quiet" ]; then
QUIET="y"
@@ -262,6 +266,9 @@ while true; do
option="$1"
VDEX_FILTER="--compiler-filter=$option"
shift
+ elif [ "x$1" = "x--sync" ]; then
+ SYNC_BEFORE_RUN="y"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -491,6 +498,7 @@ dex2oat_cmdline="true"
vdex_cmdline="true"
mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
+sync_cmdline="true"
if [ "$PREBUILD" = "y" ]; then
@@ -530,6 +538,10 @@ if [ "$STRIP_DEX" = "y" ]; then
strip_cmdline="zip --quiet --delete $DEX_LOCATION/$TEST_NAME.jar classes.dex"
fi
+if [ "$SYNC_BEFORE_RUN" = "y" ]; then
+ sync_cmdline="sync"
+fi
+
DALVIKVM_ISA_FEATURES_ARGS=""
if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
@@ -589,7 +601,8 @@ if [ "$HOST" = "n" ]; then
LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH
fi
- PUBLIC_LIBS=libart.so:libartd.so
+ # System libraries needed by libarttestd.so
+ PUBLIC_LIBS=libart.so:libartd.so:libc++.so:libbacktrace.so:libbase.so:libnativehelper.so
# Create a script with the command. The command can get longer than the longest
# allowed adb command and there is no way to get the exit status from a adb shell
@@ -607,6 +620,7 @@ if [ "$HOST" = "n" ]; then
$dex2oat_cmdline && \
$vdex_cmdline && \
$strip_cmdline && \
+ $sync_cmdline && \
$dalvikvm_cmdline"
cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
@@ -679,7 +693,7 @@ else
fi
if [ "$DEV_MODE" = "y" ]; then
- echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline"
+ echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
@@ -689,6 +703,7 @@ else
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
+ $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
# the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 84df9241db..784f49c4b9 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -252,11 +252,6 @@
"variant": "jit"
},
{
- "test": "912-classes",
- "variant": "jit",
- "bug": "http://b/34655682"
- },
- {
"tests": ["570-checker-select",
"484-checker-register-hints"],
"description": ["These tests were based on the linear scan allocator,",
diff --git a/test/run-test b/test/run-test
index 27c700e89e..c926c115e2 100755
--- a/test/run-test
+++ b/test/run-test
@@ -90,6 +90,22 @@ fi
export JACK="$JACK -g -cp $JACK_CLASSPATH"
+# Zipalign is not on the PATH in some configs, auto-detect it.
+if [ -z "$ZIPALIGN" ]; then
+ if which zipalign >/dev/null; then
+ ZIPALIGN="zipalign";
+ else
+ # TODO: Add a dependency for zipalign in Android.run-test.mk
+ # once it doesn't depend on libandroidfw (b/35246701)
+ case "$OSTYPE" in
+ darwin*) ZIPALIGN="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/darwin/bin/zipalign" ;;
+ linux*) ZIPALIGN="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/linux/bin/zipalign" ;;
+ *) echo "Can't find zipalign: unknown: $OSTYPE" >&2;;
+ esac
+ fi
+fi
+export ZIPALIGN
+
info="info.txt"
build="build"
run="run"
@@ -495,7 +511,7 @@ elif [ "$runtime" = "art" ]; then
run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
else
guess_target_arch_name
- run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}:${android_root}/lib${suffix64}"
+ run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}"
run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
fi
if [ "$relocate" = "yes" ]; then
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 278980fd30..1dc8ce552c 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -15,6 +15,7 @@
# limitations under the License.
import os
+import re
import tempfile
import subprocess
@@ -29,14 +30,52 @@ def getEnvBoolean(var, default):
return False
return default
-def get_build_var(var_name):
+_DUMP_MANY_VARS_LIST = ['HOST_2ND_ARCH_PREFIX',
+ 'TARGET_2ND_ARCH',
+ 'TARGET_ARCH',
+ 'HOST_PREFER_32_BIT',
+ 'HOST_OUT_EXECUTABLES']
+_DUMP_MANY_VARS = None # To be set to a dictionary with above list being the keys,
+ # and the build variable being the value.
+def dump_many_vars(var_name):
+ """
+ Reach into the Android build system to dump many build vars simultaneously.
+ Since the make system is so slow, we want to avoid calling into build frequently.
+ """
+ global _DUMP_MANY_VARS
+ global _DUMP_MANY_VARS_LIST
+
+ # Look up var from cache.
+ if _DUMP_MANY_VARS:
+ return _DUMP_MANY_VARS[var_name]
+
+ all_vars=" ".join(_DUMP_MANY_VARS_LIST)
+
# The command is taken from build/envsetup.sh to fetch build variables.
- command = ("CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core "
+ command = ("CALLED_FROM_SETUP=true " # Enable the 'dump-many-vars' make target.
+ "BUILD_SYSTEM=build/core " # Set up lookup path for make includes.
"make --no-print-directory -C \"%s\" -f build/core/config.mk "
- "dumpvar-%s") % (ANDROID_BUILD_TOP, var_name)
+ "dump-many-vars DUMP_MANY_VARS=\"%s\"") % (ANDROID_BUILD_TOP, all_vars)
+
config = subprocess.Popen(command, stdout=subprocess.PIPE,
- shell=True).communicate()[0]
- return config.strip()
+ shell=True).communicate()[0] # read until EOF, select stdin
+ # Prints out something like:
+ # TARGET_ARCH='arm64'
+ # HOST_ARCH='x86_64'
+ _DUMP_MANY_VARS = {}
+ for line in config.split("\n"):
+ # Split out "$key='$value'" via regex.
+ match = re.search("([^=]+)='([^']*)", line)
+ if not match:
+ continue
+ key = match.group(1)
+ value = match.group(2)
+ _DUMP_MANY_VARS[key] = value
+
+ return _DUMP_MANY_VARS[var_name]
+
+def get_build_var(var_name):
+ return dump_many_vars(var_name)
def get_env(key):
return env.get(key)
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index f60a6c9f2a..81b7953f3b 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -767,7 +767,10 @@ def main():
build_targets += 'test-art-host-run-test-dependencies'
if 'target' in TARGET_TYPES:
build_targets += 'test-art-target-run-test-dependencies'
- build_command = 'make -j' + str(n_thread) + ' ' + build_targets
+ build_command = 'make'
+ build_command += ' -j' + str(n_thread)
+ build_command += ' -C ' + env.ANDROID_BUILD_TOP
+ build_command += ' ' + build_targets
if subprocess.call(build_command.split()):
sys.exit(1)
if user_requested_test:
@@ -786,7 +789,6 @@ def main():
except SystemExit:
pass
except:
- print "hello"
print_analysis()
sys.exit(1)
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index c5a93568c6..351857d1d9 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -122,6 +122,7 @@ static AgentLib agents[] = {
{ "942-private-recursive", common_redefine::OnLoad, nullptr },
{ "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
{ "944-transform-classloaders", common_redefine::OnLoad, nullptr },
+ { "945-obsolete-native", common_redefine::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 41faa69c31..729a3e5ac4 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -123,7 +123,7 @@ done
vogar_args="$vogar_args --timeout 480"
# Use Jack with "1.8" configuration.
-vogar_args="$vogar_args --toolchain jack --language JN"
+vogar_args="$vogar_args --toolchain jack --language JO"
# JIT settings.
if $use_jit; then