summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Android.bp95
-rw-r--r--runtime/arch/memcmp16_test.cc2
-rw-r--r--runtime/art_method-inl.h1
-rw-r--r--runtime/art_method.h2
-rw-r--r--runtime/base/bit_vector.cc2
-rw-r--r--runtime/base/stringpiece.cc7
-rw-r--r--runtime/base/stringpiece.h7
-rw-r--r--runtime/check_jni.cc31
-rw-r--r--runtime/class_linker-inl.h9
-rw-r--r--runtime/class_linker.cc61
-rw-r--r--runtime/class_linker.h7
-rw-r--r--runtime/common_throws.cc37
-rw-r--r--runtime/common_throws.h19
-rw-r--r--runtime/dex/art_dex_file_loader.cc8
-rw-r--r--runtime/dex/art_dex_file_loader.h2
-rw-r--r--runtime/dex/art_dex_file_loader_test.cc307
-rw-r--r--runtime/dex/base64_test_util.h99
-rw-r--r--runtime/dex/code_item_accessors-inl.h204
-rw-r--r--runtime/dex/code_item_accessors-no_art-inl.h23
-rw-r--r--runtime/dex/code_item_accessors.h167
-rw-r--r--runtime/dex/code_item_accessors_test.cc114
-rw-r--r--runtime/dex/compact_dex_debug_info.cc117
-rw-r--r--runtime/dex/compact_dex_debug_info.h65
-rw-r--r--runtime/dex/compact_dex_debug_info_test.cc84
-rw-r--r--runtime/dex/compact_dex_file.cc108
-rw-r--r--runtime/dex/compact_dex_file.h291
-rw-r--r--runtime/dex/compact_dex_file_test.cc101
-rw-r--r--runtime/dex/compact_dex_level.h50
-rw-r--r--runtime/dex/compact_dex_utils.h37
-rw-r--r--runtime/dex/descriptors_names.cc426
-rw-r--r--runtime/dex/descriptors_names.h63
-rw-r--r--runtime/dex/dex_file-inl.h521
-rw-r--r--runtime/dex/dex_file.cc795
-rw-r--r--runtime/dex/dex_file.h1443
-rw-r--r--runtime/dex/dex_file_annotations.cc2
-rw-r--r--runtime/dex/dex_file_annotations.h2
-rw-r--r--runtime/dex/dex_file_exception_helpers.cc104
-rw-r--r--runtime/dex/dex_file_exception_helpers.h68
-rw-r--r--runtime/dex/dex_file_layout.cc3
-rw-r--r--runtime/dex/dex_file_loader.cc501
-rw-r--r--runtime/dex/dex_file_loader.h198
-rw-r--r--runtime/dex/dex_file_reference.h52
-rw-r--r--runtime/dex/dex_file_test.cc749
-rw-r--r--runtime/dex/dex_file_tracking_registrar.cc272
-rw-r--r--runtime/dex/dex_file_tracking_registrar.h81
-rw-r--r--runtime/dex/dex_file_types.h117
-rw-r--r--runtime/dex/dex_file_verifier.cc3290
-rw-r--r--runtime/dex/dex_file_verifier.h257
-rw-r--r--runtime/dex/dex_file_verifier_test.cc2186
-rw-r--r--runtime/dex/dex_instruction-inl.h558
-rw-r--r--runtime/dex/dex_instruction.cc561
-rw-r--r--runtime/dex/dex_instruction.h742
-rw-r--r--runtime/dex/dex_instruction_iterator.h237
-rw-r--r--runtime/dex/dex_instruction_list.h308
-rw-r--r--runtime/dex/dex_instruction_test.cc172
-rw-r--r--runtime/dex/dex_instruction_utils.h219
-rw-r--r--runtime/dex/invoke_type.h38
-rw-r--r--runtime/dex/modifiers.cc58
-rw-r--r--runtime/dex/modifiers.h148
-rw-r--r--runtime/dex/standard_dex_file.cc81
-rw-r--r--runtime/dex/standard_dex_file.h118
-rw-r--r--runtime/dex/utf-inl.h99
-rw-r--r--runtime/dex/utf.cc321
-rw-r--r--runtime/dex/utf.h135
-rw-r--r--runtime/dex/utf_test.cc381
-rw-r--r--runtime/dexopt_test.cc13
-rw-r--r--runtime/dexopt_test.h6
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h3
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc10
-rw-r--r--runtime/gc/accounting/atomic_stack.h2
-rw-r--r--runtime/gc/accounting/space_bitmap-inl.h11
-rw-r--r--runtime/gc/accounting/space_bitmap.cc11
-rw-r--r--runtime/gc/accounting/space_bitmap.h21
-rw-r--r--runtime/gc/accounting/space_bitmap_test.cc2
-rw-r--r--runtime/gc/collector/concurrent_copying-inl.h9
-rw-r--r--runtime/gc/collector/concurrent_copying.cc219
-rw-r--r--runtime/gc/collector/concurrent_copying.h27
-rw-r--r--runtime/gc/collector/mark_sweep.cc30
-rw-r--r--runtime/gc/collector/sticky_mark_sweep.cc2
-rw-r--r--runtime/gc/heap.cc19
-rw-r--r--runtime/gc/heap.h8
-rw-r--r--runtime/gc/heap_test.cc1
-rw-r--r--runtime/gc/space/region_space-inl.h60
-rw-r--r--runtime/gc/space/region_space.cc83
-rw-r--r--runtime/gc/space/region_space.h120
-rw-r--r--runtime/gc/space/space.cc3
-rw-r--r--runtime/gc/space/space.h9
-rw-r--r--runtime/gc/space/zygote_space.h2
-rw-r--r--runtime/gc/verification.cc2
-rw-r--r--runtime/hidden_api.h74
-rw-r--r--runtime/hidden_api_access_flags.h18
-rw-r--r--runtime/image.cc2
-rw-r--r--runtime/instrumentation.cc15
-rw-r--r--runtime/interpreter/interpreter_common.cc380
-rw-r--r--runtime/interpreter/interpreter_common.h5
-rw-r--r--runtime/interpreter/mterp/arm/entry.S2
-rw-r--r--runtime/interpreter/mterp/arm/header.S2
-rw-r--r--runtime/interpreter/mterp/arm64/entry.S2
-rw-r--r--runtime/interpreter/mterp/arm64/header.S2
-rw-r--r--runtime/interpreter/mterp/cfi_asm_support.h30
-rwxr-xr-xruntime/interpreter/mterp/gen_mterp.py2
-rw-r--r--runtime/interpreter/mterp/mips/entry.S2
-rw-r--r--runtime/interpreter/mterp/mips/header.S2
-rw-r--r--runtime/interpreter/mterp/mips64/entry.S2
-rw-r--r--runtime/interpreter/mterp/mips64/header.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm.S4
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm64.S4
-rw-r--r--runtime/interpreter/mterp/out/mterp_mips.S4
-rw-r--r--runtime/interpreter/mterp/out/mterp_mips64.S4
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86.S4
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86_64.S4
-rw-r--r--runtime/interpreter/mterp/x86/entry.S2
-rw-r--r--runtime/interpreter/mterp/x86/header.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/entry.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/header.S2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc20
-rw-r--r--runtime/interpreter/unstarted_runtime_list.h2
-rw-r--r--runtime/java_vm_ext.h2
-rw-r--r--runtime/jdwp/jdwp_adb.cc13
-rw-r--r--runtime/jit/profile_compilation_info.cc55
-rw-r--r--runtime/jit/profile_compilation_info.h8
-rw-r--r--runtime/jit/profile_compilation_info_test.cc40
-rw-r--r--runtime/jit/profile_saver.cc2
-rw-r--r--runtime/jni_env_ext.h11
-rw-r--r--runtime/jni_internal.cc3
-rw-r--r--runtime/lock_word.h6
-rw-r--r--runtime/method_handles.cc2
-rw-r--r--runtime/method_handles.h18
-rw-r--r--runtime/mirror/class-inl.h2
-rw-r--r--runtime/mirror/class.cc48
-rw-r--r--runtime/mirror/class.h6
-rw-r--r--runtime/mirror/emulated_stack_frame.cc2
-rw-r--r--runtime/mirror/object-inl.h192
-rw-r--r--runtime/mirror/object-readbarrier-inl.h2
-rw-r--r--runtime/mirror/object.h61
-rw-r--r--runtime/mirror/var_handle.cc1592
-rw-r--r--runtime/mirror/var_handle.h68
-rw-r--r--runtime/mirror/var_handle_test.cc8
-rw-r--r--runtime/native/dalvik_system_DexFile.cc53
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc13
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc10
-rw-r--r--runtime/native/java_lang_Class.cc10
-rw-r--r--runtime/native/java_lang_Void.cc43
-rw-r--r--runtime/native/java_lang_Void.h28
-rw-r--r--runtime/native_stack_dump.cc3
-rw-r--r--runtime/oat.h1
-rw-r--r--runtime/oat_file.cc35
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/oat_file_assistant.cc23
-rw-r--r--runtime/oat_file_assistant.h14
-rw-r--r--runtime/oat_file_assistant_test.cc32
-rw-r--r--runtime/oat_file_test.cc42
-rw-r--r--runtime/parsed_options.cc6
-rw-r--r--runtime/runtime.cc28
-rw-r--r--runtime/runtime.h2
-rw-r--r--runtime/runtime_options.def3
-rw-r--r--runtime/subtype_check.h3
-rw-r--r--runtime/thread.cc31
-rw-r--r--runtime/verifier/method_verifier.cc18
-rw-r--r--runtime/verifier/reg_type_cache.cc2
-rw-r--r--runtime/well_known_classes.cc38
-rw-r--r--runtime/well_known_classes.h7
162 files changed, 3589 insertions, 17631 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp
index db9bceaf29..1ac770fd06 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -14,83 +14,6 @@
// limitations under the License.
//
-cc_defaults {
- name: "libdexfile_defaults",
- defaults: ["art_defaults"],
- host_supported: true,
- srcs: [
- "dex/compact_dex_debug_info.cc",
- "dex/compact_dex_file.cc",
- "dex/descriptors_names.cc",
- "dex/dex_file.cc",
- "dex/dex_file_exception_helpers.cc",
- "dex/dex_file_loader.cc",
- "dex/dex_file_tracking_registrar.cc",
- "dex/dex_file_verifier.cc",
- "dex/dex_instruction.cc",
- "dex/modifiers.cc",
- "dex/standard_dex_file.cc",
- "dex/utf.cc",
- ],
-
- target: {
- android: {
- static_libs: [
- "libziparchive",
- "libz",
- "libbase",
- ],
- shared_libs: [
- "libutils",
- ],
- },
- host: {
- shared_libs: [
- "libziparchive",
- "libz",
- ],
- },
- },
- generated_sources: ["dexfile_operator_srcs"],
- include_dirs: [
- "external/zlib",
- ],
- shared_libs: [
- "liblog",
- // For common macros.
- "libbase",
- "libz",
- ],
-
- // Exporting "." would shadow the system elf.h with our elf.h,
- // which in turn breaks any tools that reference this library.
- // export_include_dirs: ["."],
-}
-
-gensrcs {
- name: "dexfile_operator_srcs",
- cmd: "$(location generate-operator-out.py) art/runtime $(in) > $(out)",
- tool_files: ["generate-operator-out.py"],
- srcs: [
- "dex/dex_file.h",
- "dex/dex_file_layout.h",
- "dex/dex_instruction.h",
- "dex/dex_instruction_utils.h",
- "dex/invoke_type.h",
- ],
- output_extension: "operator_out.cc",
-}
-
-art_cc_library {
- name: "libdexfile",
- defaults: ["libdexfile_defaults"],
- // Leave the symbols in the shared library so that stack unwinders can
- // produce meaningful name resolution.
- strip: {
- keep_symbols: true,
- },
-}
-
// Keep the __jit_debug_register_code symbol as a unique symbol during ICF for architectures where
// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
// new jit code is generated. We don't want it to be called when a different function with the same
@@ -133,9 +56,9 @@ cc_defaults {
"common_throws.cc",
"compiler_filter.cc",
"debugger.cc",
+ "dex/art_dex_file_loader.cc",
"dex/dex_file_annotations.cc",
"dex/dex_file_layout.cc",
- "dex/art_dex_file_loader.cc",
"dex_to_dex_decompiler.cc",
"elf_file.cc",
"exec_utils.cc",
@@ -244,7 +167,6 @@ cc_defaults {
"native/java_lang_Thread.cc",
"native/java_lang_Throwable.cc",
"native/java_lang_VMClassLoader.cc",
- "native/java_lang_Void.cc",
"native/java_lang_invoke_MethodHandleImpl.cc",
"native/java_lang_ref_FinalizerReference.cc",
"native/java_lang_ref_Reference.cc",
@@ -487,7 +409,6 @@ cc_defaults {
"jni_platform_headers",
],
shared_libs: [
- "libdexfile",
"libnativebridge",
"libnativeloader",
"libbacktrace",
@@ -521,9 +442,9 @@ gensrcs {
"base/callee_save_type.h",
"base/enums.h",
"base/mutex.h",
- "debugger.h",
"base/unix_file/fd_file.h",
"class_status.h",
+ "debugger.h",
"dex/dex_file_layout.h",
"gc_root.h",
"gc/allocator_type.h",
@@ -565,6 +486,8 @@ art_cc_library {
strip: {
keep_symbols: true,
},
+ shared_libs: ["libdexfile"],
+ export_shared_lib_headers: ["libdexfile"],
}
art_cc_library {
@@ -573,6 +496,8 @@ art_cc_library {
"art_debug_defaults",
"libart_defaults",
],
+ shared_libs: ["libdexfiled"],
+ export_shared_lib_headers: ["libdexfiled"],
}
art_cc_library {
@@ -637,13 +562,7 @@ art_cc_test {
"class_loader_context_test.cc",
"class_table_test.cc",
"compiler_filter_test.cc",
- "dex/code_item_accessors_test.cc",
- "dex/compact_dex_debug_info_test.cc",
- "dex/compact_dex_file_test.cc",
- "dex/dex_file_test.cc",
- "dex/dex_file_verifier_test.cc",
- "dex/dex_instruction_test.cc",
- "dex/utf_test.cc",
+ "dex/art_dex_file_loader_test.cc",
"entrypoints/math_entrypoints_test.cc",
"entrypoints/quick/quick_trampoline_entrypoints_test.cc",
"entrypoints_order_test.cc",
diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc
index 2f3639c4b1..37aad21a40 100644
--- a/runtime/arch/memcmp16_test.cc
+++ b/runtime/arch/memcmp16_test.cc
@@ -100,7 +100,7 @@ static void CheckSeparate(size_t max_length, size_t min_length) {
}
size_t min = count1 < count2 ? count1 : count2;
- bool fill_same = r.next() % 1 == 1;
+ bool fill_same = r.next() % 2 == 1;
if (fill_same) {
for (size_t i = 0; i < min; ++i) {
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index e6e35c89c9..8b48aa27f9 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -328,6 +328,7 @@ inline mirror::DexCache* ArtMethod::GetDexCache() {
}
inline bool ArtMethod::IsProxyMethod() {
+ DCHECK(!IsRuntimeMethod()) << "ArtMethod::IsProxyMethod called on a runtime method";
// Avoid read barrier since the from-space version of the class will have the correct proxy class
// flags since they are constant for the lifetime of the class.
return GetDeclaringClass<kWithoutReadBarrier>()->IsProxyClass();
diff --git a/runtime/art_method.h b/runtime/art_method.h
index cec2ec4df2..21ee8f19e5 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -551,7 +551,7 @@ class ArtMethod FINAL {
// Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal
// conventions for a method of managed code. Returns false for Proxy methods.
ALWAYS_INLINE bool IsRuntimeMethod() {
- return dex_method_index_ == kRuntimeMethodDexMethodIndex;;
+ return dex_method_index_ == kRuntimeMethodDexMethodIndex;
}
// Is this a hand crafted method used for something like describing callee saves?
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 5e97a63c0d..c706c7ebf2 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -223,7 +223,7 @@ void BitVector::Subtract(const BitVector *src) {
// Difference until max, we know both accept it:
// There is no need to do more:
// If we are bigger than src, the upper bits are unchanged.
- // If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
+ // If we are smaller than src, the nonexistent upper bits are 0 and thus can't get subtracted.
for (uint32_t idx = 0; idx < min_size; idx++) {
storage_[idx] &= (~(src->GetRawStorageWord(idx)));
}
diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc
index 672431cf9d..aea4e74bb1 100644
--- a/runtime/base/stringpiece.cc
+++ b/runtime/base/stringpiece.cc
@@ -23,13 +23,6 @@
namespace art {
-#if !defined(NDEBUG)
-char StringPiece::operator[](size_type i) const {
- CHECK_LT(i, length_);
- return ptr_[i];
-}
-#endif
-
void StringPiece::CopyToString(std::string* target) const {
target->assign(ptr_, length_);
}
diff --git a/runtime/base/stringpiece.h b/runtime/base/stringpiece.h
index 46743e9643..e7109dc18a 100644
--- a/runtime/base/stringpiece.h
+++ b/runtime/base/stringpiece.h
@@ -20,6 +20,8 @@
#include <string.h>
#include <string>
+#include <android-base/logging.h>
+
namespace art {
// A string-like object that points to a sized piece of memory.
@@ -84,13 +86,10 @@ class StringPiece {
length_ = len;
}
-#if defined(NDEBUG)
char operator[](size_type i) const {
+ DCHECK_LT(i, length_);
return ptr_[i];
}
-#else
- char operator[](size_type i) const;
-#endif
void remove_prefix(size_type n) {
ptr_ += n;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 5549122c34..05f099f3b2 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -46,6 +46,7 @@
#include "well_known_classes.h"
namespace art {
+namespace {
using android::base::StringAppendF;
using android::base::StringPrintf;
@@ -1211,7 +1212,7 @@ class ScopedCheck {
// this particular instance of JNIEnv.
if (env != threadEnv) {
// Get the thread owning the JNIEnv that's being used.
- Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->self_;
+ Thread* envThread = reinterpret_cast<JNIEnvExt*>(env)->GetSelf();
AbortF("thread %s using JNIEnv* from thread %s",
ToStr<Thread>(*self).c_str(), ToStr<Thread>(*envThread).c_str());
return false;
@@ -1223,7 +1224,7 @@ class ScopedCheck {
case kFlag_CritOkay: // okay to call this method
break;
case kFlag_CritBad: // not okay to call
- if (threadEnv->critical_ > 0) {
+ if (threadEnv->GetCritical() > 0) {
AbortF("thread %s using JNI after critical get",
ToStr<Thread>(*self).c_str());
return false;
@@ -1231,25 +1232,25 @@ class ScopedCheck {
break;
case kFlag_CritGet: // this is a "get" call
// Don't check here; we allow nested gets.
- if (threadEnv->critical_ == 0) {
- threadEnv->critical_start_us_ = self->GetCpuMicroTime();
+ if (threadEnv->GetCritical() == 0) {
+ threadEnv->SetCriticalStartUs(self->GetCpuMicroTime());
}
- threadEnv->critical_++;
+ threadEnv->SetCritical(threadEnv->GetCritical() + 1);
break;
case kFlag_CritRelease: // this is a "release" call
- if (threadEnv->critical_ == 0) {
+ if (threadEnv->GetCritical() == 0) {
AbortF("thread %s called too many critical releases",
ToStr<Thread>(*self).c_str());
return false;
- } else if (threadEnv->critical_ == 1) {
+ } else if (threadEnv->GetCritical() == 1) {
// Leaving the critical region, possibly warn about long critical regions.
- uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->critical_start_us_;
+ uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->GetCriticalStartUs();
if (critical_duration_us > kCriticalWarnTimeUs) {
LOG(WARNING) << "JNI critical lock held for "
<< PrettyDuration(UsToNs(critical_duration_us)) << " on " << *self;
}
}
- threadEnv->critical_--;
+ threadEnv->SetCritical(threadEnv->GetCritical() - 1);
break;
default:
LOG(FATAL) << "Bad flags (internal error): " << flags_;
@@ -2621,7 +2622,7 @@ class CheckJNI {
}
static const JNINativeInterface* baseEnv(JNIEnv* env) {
- return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions_;
+ return reinterpret_cast<JNIEnvExt*>(env)->GetUncheckedFunctions();
}
static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
@@ -3847,10 +3848,6 @@ const JNINativeInterface gCheckNativeInterface = {
CheckJNI::GetObjectRefType,
};
-const JNINativeInterface* GetCheckJniNativeInterface() {
- return &gCheckNativeInterface;
-}
-
class CheckJII {
public:
static jint DestroyJavaVM(JavaVM* vm) {
@@ -3922,6 +3919,12 @@ const JNIInvokeInterface gCheckInvokeInterface = {
CheckJII::AttachCurrentThreadAsDaemon
};
+} // anonymous namespace
+
+const JNINativeInterface* GetCheckJniNativeInterface() {
+ return &gCheckNativeInterface;
+}
+
const JNIInvokeInterface* GetCheckJniInvokeInterface() {
return &gCheckInvokeInterface;
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index cd6e8d59e8..ae06f8f9bc 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -226,14 +226,7 @@ inline ArtMethod* ClassLinker::LookupResolvedMethod(uint32_t method_idx,
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
ObjPtr<mirror::Class> klass = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader);
if (klass != nullptr) {
- if (klass->IsInterface()) {
- resolved = klass->FindInterfaceMethod(dex_cache, method_idx, pointer_size);
- } else {
- resolved = klass->FindClassMethod(dex_cache, method_idx, pointer_size);
- }
- if (resolved != nullptr) {
- dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size);
- }
+ resolved = FindResolvedMethod(klass, dex_cache, class_loader, method_idx);
}
}
return resolved;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 800427d6ab..62627287b0 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -457,7 +457,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
VoidFunctor()));
// Initialize the SubtypeCheck bitstring for java.lang.Object and java.lang.Class.
- {
+ if (kBitstringSubtypeCheckEnabled) {
// It might seem the lock here is unnecessary, however all the SubtypeCheck
// functions are annotated to require locks all the way down.
//
@@ -1856,7 +1856,7 @@ bool ClassLinker::AddImageSpace(
visitor(root.Read());
}
- {
+ if (kBitstringSubtypeCheckEnabled) {
// Every class in the app image has initially SubtypeCheckInfo in the
// Uninitialized state.
//
@@ -4484,6 +4484,14 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(temp_klass, klass);
+ // SubtypeCheckInfo::Initialized must happen-before any new-instance for that type.
+ // See also ClassLinker::EnsureInitialized().
+ if (kBitstringSubtypeCheckEnabled) {
+ MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
+ SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(klass.Get());
+ // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck for j.l.r.Proxy is already assigned.
+ }
+
{
// Lock on klass is released. Lock new class object.
ObjectLock<mirror::Class> initialization_lock(self, klass);
@@ -5231,7 +5239,7 @@ bool ClassLinker::EnsureInitialized(Thread* self,
// can be used as a source for the IsSubClass check, and that all ancestors
// of the class are Assigned (can be used as a target for IsSubClass check)
// or Overflowed (can be used as a source for IsSubClass check).
- {
+ if (kBitstringSubtypeCheckEnabled) {
MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
SubtypeCheck<ObjPtr<mirror::Class>>::EnsureInitialized(c.Get());
// TODO: Avoid taking subtype_check_lock_ if SubtypeCheck is already initialized.
@@ -7931,6 +7939,38 @@ std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* clas
return oss.str();
}
+ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ uint32_t method_idx) {
+ // Search for the method using dex_cache and method_idx. The Class::Find*Method()
+ // functions can optimize the search if the dex_cache is the same as the DexCache
+ // of the class, with fall-back to name and signature search otherwise.
+ ArtMethod* resolved = nullptr;
+ if (klass->IsInterface()) {
+ resolved = klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_);
+ } else {
+ resolved = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_);
+ }
+ DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
+ if (resolved != nullptr) {
+ // In case of jmvti, the dex file gets verified before being registered, so first
+ // check if it's registered before checking class tables.
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ CHECK(!IsDexFileRegistered(Thread::Current(), dex_file) ||
+ FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader))
+ << "DexFile referrer: " << dex_file.GetLocation()
+ << " ClassLoader: " << DescribeLoaders(class_loader, "");
+ // Be a good citizen and update the dex cache to speed subsequent calls.
+ dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+ CHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr)
+ << "Class: " << klass->PrettyClass() << ", "
+ << "DexFile referrer: " << dex_file.GetLocation();
+ }
+ return resolved;
+}
+
template <ClassLinker::ResolveMode kResolveMode>
ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
@@ -7963,6 +8003,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
<< resolved->PrettyMethod() << ";" << resolved
<< "/0x" << std::hex << resolved->GetAccessFlags()
<< " ReferencedClass: " << descriptor
+ << " DexFile referrer: " << dex_file.GetLocation()
<< " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor);
}
} else {
@@ -7983,19 +8024,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx,
}
if (!valid_dex_cache_method) {
- // Search for the method using dex_cache and method_idx. The Class::Find*Method()
- // functions can optimize the search if the dex_cache is the same as the DexCache
- // of the class, with fall-back to name and signature search otherwise.
- if (klass->IsInterface()) {
- resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size);
- } else {
- resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size);
- }
- DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
- if (resolved != nullptr) {
- // Be a good citizen and update the dex cache to speed subsequent calls.
- dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size);
- }
+ resolved = FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
}
// Note: We can check for IllegalAccessError only if we have a referrer.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 16fa1ce801..6bb924fc7d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -312,6 +312,13 @@ class ClassLinker {
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find a method with the given index from class `klass`, and update the dex cache.
+ ArtMethod* FindResolvedMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader,
+ uint32_t method_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Resolve a method with a given ID from the DexFile associated with the given DexCache
// and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are
// used as in ResolveType. What is unique is the method type argument which is used to
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 1b6f317053..8f65c66441 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -53,6 +53,11 @@ static void AddReferrerLocation(std::ostream& os, ObjPtr<mirror::Class> referrer
}
}
+static void ThrowException(const char* exception_descriptor) REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
+ self->ThrowNewException(exception_descriptor, nullptr);
+}
+
static void ThrowException(const char* exception_descriptor,
ObjPtr<mirror::Class> referrer,
const char* fmt,
@@ -243,6 +248,11 @@ void ThrowIllegalArgumentException(const char* msg) {
ThrowException("Ljava/lang/IllegalArgumentException;", nullptr, msg);
}
+// IllegalStateException
+
+void ThrowIllegalStateException(const char* msg) {
+ ThrowException("Ljava/lang/IllegalStateException;", nullptr, msg);
+}
// IncompatibleClassChangeError
@@ -314,6 +324,13 @@ void ThrowIncompatibleClassChangeErrorForMethodConflict(ArtMethod* method) {
ArtMethod::PrettyMethod(method).c_str()).c_str());
}
+// IndexOutOfBoundsException
+
+void ThrowIndexOutOfBoundsException(int index, int length) {
+ ThrowException("Ljava/lang/IndexOutOfBoundsException;", nullptr,
+ StringPrintf("length=%d; index=%d", length, index).c_str());
+}
+
// InternalError
void ThrowInternalError(const char* fmt, ...) {
@@ -721,6 +738,12 @@ void ThrowNullPointerException(const char* msg) {
ThrowException("Ljava/lang/NullPointerException;", nullptr, msg);
}
+// ReadOnlyBufferException
+
+void ThrowReadOnlyBufferException() {
+ Thread::Current()->ThrowNewException("Ljava/nio/ReadOnlyBufferException;", nullptr);
+}
+
// RuntimeException
void ThrowRuntimeException(const char* fmt, ...) {
@@ -844,6 +867,12 @@ void ThrowStringIndexOutOfBoundsException(int index, int length) {
StringPrintf("length=%d; index=%d", length, index).c_str());
}
+// UnsupportedOperationException
+
+void ThrowUnsupportedOperationException() {
+ ThrowException("Ljava/lang/UnsupportedOperationException;");
+}
+
// VerifyError
void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) {
@@ -855,13 +884,13 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) {
// WrongMethodTypeException
-void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
- mirror::MethodType* callsite_type) {
+void ThrowWrongMethodTypeException(mirror::MethodType* expected_type,
+ mirror::MethodType* actual_type) {
ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",
nullptr,
StringPrintf("Expected %s but was %s",
- callee_type->PrettyDescriptor().c_str(),
- callsite_type->PrettyDescriptor().c_str()).c_str());
+ expected_type->PrettyDescriptor().c_str(),
+ actual_type->PrettyDescriptor().c_str()).c_str());
}
} // namespace art
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 3512b2b5bf..e9baa4fef0 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -120,6 +120,11 @@ void ThrowIllegalAccessException(const char* msg)
void ThrowIllegalArgumentException(const char* msg)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// IllegalAccessException
+
+void ThrowIllegalStateException(const char* msg)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// IncompatibleClassChangeError
void ThrowIncompatibleClassChangeError(InvokeType expected_type,
@@ -151,6 +156,11 @@ void ThrowIncompatibleClassChangeError(ObjPtr<mirror::Class> referrer, const cha
void ThrowIncompatibleClassChangeErrorForMethodConflict(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// IndexOutOfBoundsException
+
+void ThrowIndexOutOfBoundsException(int index, int length)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// InternalError
void ThrowInternalError(const char* fmt, ...)
@@ -223,6 +233,10 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address = false, uintptr_t ad
void ThrowNullPointerException(const char* msg)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// ReadOnlyBufferException
+
+void ThrowReadOnlyBufferException() REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// RuntimeException
void ThrowRuntimeException(const char* fmt, ...)
@@ -244,6 +258,10 @@ void ThrowStackOverflowError(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_)
void ThrowStringIndexOutOfBoundsException(int index, int length)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// UnsupportedOperationException
+
+void ThrowUnsupportedOperationException() REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// VerifyError
void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...)
@@ -251,6 +269,7 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
// WrongMethodTypeException
+
void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
mirror::MethodType* callsite_type)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc
index 08cf30d5bf..0817cb4b6a 100644
--- a/runtime/dex/art_dex_file_loader.cc
+++ b/runtime/dex/art_dex_file_loader.cc
@@ -25,10 +25,10 @@
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
-#include "compact_dex_file.h"
-#include "dex_file.h"
-#include "dex_file_verifier.h"
-#include "standard_dex_file.h"
+#include "dex/compact_dex_file.h"
+#include "dex/dex_file.h"
+#include "dex/dex_file_verifier.h"
+#include "dex/standard_dex_file.h"
#include "zip_archive.h"
namespace art {
diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h
index b31d1e94e0..3585381f9b 100644
--- a/runtime/dex/art_dex_file_loader.h
+++ b/runtime/dex/art_dex_file_loader.h
@@ -22,8 +22,8 @@
#include <string>
#include <vector>
-#include "dex_file_loader.h"
#include "base/macros.h"
+#include "dex/dex_file_loader.h"
namespace art {
diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc
new file mode 100644
index 0000000000..25d4dd0875
--- /dev/null
+++ b/runtime/dex/art_dex_file_loader_test.cc
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2011 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 <sys/mman.h>
+
+#include <memory>
+
+#include "art_dex_file_loader.h"
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "dex/base64_test_util.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/descriptors_names.h"
+#include "dex/dex_file.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_loader.h"
+#include "mem_map.h"
+#include "os.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+class ArtDexFileLoaderTest : public CommonRuntimeTest {};
+
+// TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and
+// the tests that depend upon them should be moved to dex_file_loader_test.cc
+
+TEST_F(ArtDexFileLoaderTest, Open) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested"));
+ ASSERT_TRUE(dex.get() != nullptr);
+}
+
+TEST_F(ArtDexFileLoaderTest, GetLocationChecksum) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main"));
+ EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum());
+}
+
+TEST_F(ArtDexFileLoaderTest, GetChecksum) {
+ std::vector<uint32_t> checksums;
+ ScopedObjectAccess soa(Thread::Current());
+ std::string error_msg;
+ const ArtDexFileLoader dex_file_loader;
+ EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(),
+ &checksums,
+ &error_msg))
+ << error_msg;
+ ASSERT_EQ(1U, checksums.size());
+ EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]);
+}
+
+TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksums) {
+ std::string error_msg;
+ std::vector<uint32_t> checksums;
+ std::string multidex_file = GetTestDexFileName("MultiDex");
+ const ArtDexFileLoader dex_file_loader;
+ EXPECT_TRUE(dex_file_loader.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(), DexFileLoader::GetMultiDexLocation(0, multidex_file.c_str()));
+ EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]);
+
+ EXPECT_EQ(dexes[1]->GetLocation(), DexFileLoader::GetMultiDexLocation(1, multidex_file.c_str()));
+ EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
+}
+
+TEST_F(ArtDexFileLoaderTest, ClassDefs) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
+ ASSERT_TRUE(raw.get() != nullptr);
+ EXPECT_EQ(3U, raw->NumClassDefs());
+
+ const DexFile::ClassDef& c0 = raw->GetClassDef(0);
+ EXPECT_STREQ("LNested$1;", raw->GetClassDescriptor(c0));
+
+ const DexFile::ClassDef& c1 = raw->GetClassDef(1);
+ EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c1));
+
+ const DexFile::ClassDef& c2 = raw->GetClassDef(2);
+ EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c2));
+}
+
+TEST_F(ArtDexFileLoaderTest, GetMethodSignature) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+ ASSERT_TRUE(raw.get() != nullptr);
+ EXPECT_EQ(1U, raw->NumClassDefs());
+
+ const DexFile::ClassDef& class_def = raw->GetClassDef(0);
+ ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def));
+
+ const uint8_t* class_data = raw->GetClassData(class_def);
+ ASSERT_TRUE(class_data != nullptr);
+ ClassDataItemIterator it(*raw, class_data);
+
+ EXPECT_EQ(1u, it.NumDirectMethods());
+
+ // Check the signature for the static initializer.
+ {
+ ASSERT_EQ(1U, it.NumDirectMethods());
+ const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
+ const char* name = raw->StringDataByIdx(method_id.name_idx_);
+ ASSERT_STREQ("<init>", name);
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
+ ASSERT_EQ("()V", signature);
+ }
+
+ // Check all virtual methods.
+ struct Result {
+ const char* name;
+ const char* signature;
+ const char* pretty_method;
+ };
+ static const Result results[] = {
+ {
+ "m1",
+ "(IDJLjava/lang/Object;)Ljava/lang/Float;",
+ "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)"
+ },
+ {
+ "m2",
+ "(ZSC)LGetMethodSignature;",
+ "GetMethodSignature GetMethodSignature.m2(boolean, short, char)"
+ },
+ {
+ "m3",
+ "()V",
+ "void GetMethodSignature.m3()"
+ },
+ {
+ "m4",
+ "(I)V",
+ "void GetMethodSignature.m4(int)"
+ },
+ {
+ "m5",
+ "(II)V",
+ "void GetMethodSignature.m5(int, int)"
+ },
+ {
+ "m6",
+ "(II[[I)V",
+ "void GetMethodSignature.m6(int, int, int[][])"
+ },
+ {
+ "m7",
+ "(II[[ILjava/lang/Object;)V",
+ "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)"
+ },
+ {
+ "m8",
+ "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V",
+ "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])"
+ },
+ {
+ "m9",
+ "()I",
+ "int GetMethodSignature.m9()"
+ },
+ {
+ "mA",
+ "()[[I",
+ "int[][] GetMethodSignature.mA()"
+ },
+ {
+ "mB",
+ "()[[Ljava/lang/Object;",
+ "java.lang.Object[][] GetMethodSignature.mB()"
+ },
+ };
+ ASSERT_EQ(arraysize(results), it.NumVirtualMethods());
+ for (const Result& r : results) {
+ it.Next();
+ const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
+
+ const char* name = raw->StringDataByIdx(method_id.name_idx_);
+ ASSERT_STREQ(r.name, name);
+
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
+ ASSERT_EQ(r.signature, signature);
+
+ std::string plain_method = std::string("GetMethodSignature.") + r.name;
+ ASSERT_EQ(plain_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ false));
+ ASSERT_EQ(r.pretty_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ true));
+ }
+}
+
+TEST_F(ArtDexFileLoaderTest, FindStringId) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+ ASSERT_TRUE(raw.get() != nullptr);
+ EXPECT_EQ(1U, raw->NumClassDefs());
+
+ const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
+ "D", "I", "J", nullptr };
+ for (size_t i = 0; strings[i] != nullptr; i++) {
+ const char* str = strings[i];
+ const DexFile::StringId* str_id = raw->FindStringId(str);
+ const char* dex_str = raw->GetStringData(*str_id);
+ EXPECT_STREQ(dex_str, str);
+ }
+}
+
+TEST_F(ArtDexFileLoaderTest, FindTypeId) {
+ for (size_t i = 0; i < java_lang_dex_file_->NumTypeIds(); i++) {
+ const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i));
+ const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str);
+ ASSERT_TRUE(type_str_id != nullptr);
+ dex::StringIndex type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id);
+ const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx);
+ ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str));
+ ASSERT_TRUE(type_id != nullptr);
+ EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id).index_, i);
+ }
+}
+
+TEST_F(ArtDexFileLoaderTest, FindProtoId) {
+ for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) {
+ const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i);
+ const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find);
+ std::vector<dex::TypeIndex> to_find_types;
+ if (to_find_tl != nullptr) {
+ for (size_t j = 0; j < to_find_tl->Size(); j++) {
+ to_find_types.push_back(to_find_tl->GetTypeItem(j).type_idx_);
+ }
+ }
+ const DexFile::ProtoId* found =
+ java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types);
+ ASSERT_TRUE(found != nullptr);
+ EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i);
+ }
+}
+
+TEST_F(ArtDexFileLoaderTest, FindMethodId) {
+ for (size_t i = 0; i < java_lang_dex_file_->NumMethodIds(); i++) {
+ const DexFile::MethodId& to_find = java_lang_dex_file_->GetMethodId(i);
+ const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_);
+ const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_);
+ const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_);
+ const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature);
+ ASSERT_TRUE(found != nullptr) << "Didn't find method " << i << ": "
+ << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "."
+ << java_lang_dex_file_->GetStringData(name)
+ << java_lang_dex_file_->GetMethodSignature(to_find);
+ EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i);
+ }
+}
+
+TEST_F(ArtDexFileLoaderTest, FindFieldId) {
+ for (size_t i = 0; i < java_lang_dex_file_->NumFieldIds(); i++) {
+ const DexFile::FieldId& to_find = java_lang_dex_file_->GetFieldId(i);
+ const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_);
+ const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_);
+ const DexFile::TypeId& type = java_lang_dex_file_->GetTypeId(to_find.type_idx_);
+ const DexFile::FieldId* found = java_lang_dex_file_->FindFieldId(klass, name, type);
+ ASSERT_TRUE(found != nullptr) << "Didn't find field " << i << ": "
+ << java_lang_dex_file_->StringByTypeIdx(to_find.type_idx_) << " "
+ << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "."
+ << java_lang_dex_file_->GetStringData(name);
+ EXPECT_EQ(java_lang_dex_file_->GetIndexForFieldId(*found), i);
+ }
+}
+
+TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) {
+ ScratchFile file;
+ UniqueCPtr<const char[]> dex_location_real(realpath(file.GetFilename().c_str(), nullptr));
+ std::string dex_location(dex_location_real.get());
+
+ ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location.c_str()));
+ std::string multidex_location = DexFileLoader::GetMultiDexLocation(1, dex_location.c_str());
+ ASSERT_EQ(multidex_location, DexFileLoader::GetDexCanonicalLocation(multidex_location.c_str()));
+
+ std::string dex_location_sym = dex_location + "symlink";
+ ASSERT_EQ(0, symlink(dex_location.c_str(), dex_location_sym.c_str()));
+
+ ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location_sym.c_str()));
+
+ std::string multidex_location_sym = DexFileLoader::GetMultiDexLocation(
+ 1, dex_location_sym.c_str());
+ ASSERT_EQ(multidex_location,
+ DexFileLoader::GetDexCanonicalLocation(multidex_location_sym.c_str()));
+
+ ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
+}
+
+} // namespace art
diff --git a/runtime/dex/base64_test_util.h b/runtime/dex/base64_test_util.h
deleted file mode 100644
index 0657f9fd01..0000000000
--- a/runtime/dex/base64_test_util.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_BASE64_TEST_UTIL_H_
-#define ART_RUNTIME_DEX_BASE64_TEST_UTIL_H_
-
-#include <base/logging.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <memory>
-#include <vector>
-
-namespace art {
-
-static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
- static const uint8_t kBase64Map[256] = {
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
- 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
- 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255
- };
-
- CHECK(dst_size != nullptr);
- std::vector<uint8_t> tmp;
- uint32_t t = 0, y = 0;
- int g = 3;
- for (size_t i = 0; src[i] != '\0'; ++i) {
- uint8_t c = kBase64Map[src[i] & 0xFF];
- if (c == 255) continue;
- // the final = symbols are read and used to trim the remaining bytes
- if (c == 254) {
- c = 0;
- // prevent g < 0 which would potentially allow an overflow later
- if (--g < 0) {
- *dst_size = 0;
- return nullptr;
- }
- } else if (g != 3) {
- // we only allow = to be at the end
- *dst_size = 0;
- return nullptr;
- }
- t = (t << 6) | c;
- if (++y == 4) {
- tmp.push_back((t >> 16) & 255);
- if (g > 1) {
- tmp.push_back((t >> 8) & 255);
- }
- if (g > 2) {
- tmp.push_back(t & 255);
- }
- y = t = 0;
- }
- }
- if (y != 0) {
- *dst_size = 0;
- return nullptr;
- }
- std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
- *dst_size = tmp.size();
- std::copy(tmp.begin(), tmp.end(), dst.get());
- return dst.release();
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_BASE64_TEST_UTIL_H_
diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h
deleted file mode 100644
index 9c39935d3b..0000000000
--- a/runtime/dex/code_item_accessors-inl.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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_DEX_CODE_ITEM_ACCESSORS_INL_H_
-#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_
-
-#include "code_item_accessors.h"
-
-#include "compact_dex_file.h"
-#include "dex_file-inl.h"
-#include "standard_dex_file.h"
-
-// The no ART version is used by binaries that don't include the whole runtime.
-namespace art {
-
-inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units,
- const uint16_t* insns) {
- insns_size_in_code_units_ = insns_size_in_code_units;
- insns_ = insns;
-}
-
-inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) {
- uint32_t insns_size_in_code_units;
- code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>(
- &insns_size_in_code_units,
- /*registers_size*/ nullptr,
- /*ins_size*/ nullptr,
- /*outs_size*/ nullptr,
- /*tries_size*/ nullptr);
- Init(insns_size_in_code_units, code_item.insns_);
-}
-
-inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) {
- Init(code_item.insns_size_in_code_units_, code_item.insns_);
-}
-
-inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- if (code_item != nullptr) {
- DCHECK(dex_file.IsInDataSection(code_item));
- if (dex_file.IsCompactDexFile()) {
- Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
- } else {
- DCHECK(dex_file.IsStandardDexFile());
- Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
- }
- }
-}
-
-inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(
- const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- Init(dex_file, code_item);
-}
-
-inline DexInstructionIterator CodeItemInstructionAccessor::begin() const {
- return DexInstructionIterator(insns_, 0u);
-}
-
-inline DexInstructionIterator CodeItemInstructionAccessor::end() const {
- return DexInstructionIterator(insns_, insns_size_in_code_units_);
-}
-
-inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom(
- uint32_t start_dex_pc) const {
- DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits());
- return {
- DexInstructionIterator(insns_, start_dex_pc),
- DexInstructionIterator(insns_, insns_size_in_code_units_) };
-}
-
-inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) {
- uint32_t insns_size_in_code_units;
- code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units,
- &registers_size_,
- &ins_size_,
- &outs_size_,
- &tries_size_);
- CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_);
-}
-
-inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) {
- CodeItemInstructionAccessor::Init(code_item);
- registers_size_ = code_item.registers_size_;
- ins_size_ = code_item.ins_size_;
- outs_size_ = code_item.outs_size_;
- tries_size_ = code_item.tries_size_;
-}
-
-inline void CodeItemDataAccessor::Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- if (code_item != nullptr) {
- if (dex_file.IsCompactDexFile()) {
- CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
- } else {
- DCHECK(dex_file.IsStandardDexFile());
- CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
- }
- }
-}
-
-inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- Init(dex_file, code_item);
-}
-
-inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const {
- const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u);
- return {
- try_items,
- try_items + TriesSize() };
-}
-
-inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const {
- return DexFile::GetCatchHandlerData(end(), TriesSize(), offset);
-}
-
-inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const {
- IterationRange<const DexFile::TryItem*> try_items(TryItems());
- int32_t index = DexFile::FindTryItem(try_items.begin(),
- try_items.end() - try_items.begin(),
- try_dex_pc);
- return index != -1 ? &try_items.begin()[index] : nullptr;
-}
-
-inline const void* CodeItemDataAccessor::CodeItemDataEnd() const {
- const uint8_t* handler_data = GetCatchHandlerData();
-
- if (TriesSize() == 0 || handler_data == nullptr) {
- return &end().Inst();
- }
- // Get the start of the handler data.
- const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data);
- // Manually read each handler.
- for (uint32_t i = 0; i < handlers_size; ++i) {
- int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2;
- if (uleb128_count <= 0) {
- uleb128_count = -uleb128_count + 1;
- }
- for (int32_t j = 0; j < uleb128_count; ++j) {
- DecodeUnsignedLeb128(&handler_data);
- }
- }
- return reinterpret_cast<const void*>(handler_data);
-}
-
-inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item,
- uint32_t dex_method_index) {
- if (code_item == nullptr) {
- return;
- }
- dex_file_ = &dex_file;
- if (dex_file.IsCompactDexFile()) {
- Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index);
- } else {
- DCHECK(dex_file.IsStandardDexFile());
- Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
- }
-}
-
-inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item,
- uint32_t dex_method_index) {
- debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset(
- dex_method_index);
- CodeItemDataAccessor::Init(code_item);
-}
-
-inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) {
- debug_info_offset_ = code_item.debug_info_off_;
- CodeItemDataAccessor::Init(code_item);
-}
-
-template<typename NewLocalCallback>
-inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static,
- uint32_t method_idx,
- NewLocalCallback new_local,
- void* context) const {
- return dex_file_->DecodeDebugLocalInfo(RegistersSize(),
- InsSize(),
- InsnsSizeInCodeUnits(),
- DebugInfoOffset(),
- is_static,
- method_idx,
- new_local,
- context);
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_
diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h
deleted file mode 100644
index 8082be3818..0000000000
--- a/runtime/dex/code_item_accessors-no_art-inl.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
-#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
-
-// TODO: delete this file once system/core is updated.
-#include "code_item_accessors-inl.h"
-
-#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
diff --git a/runtime/dex/code_item_accessors.h b/runtime/dex/code_item_accessors.h
deleted file mode 100644
index beb78f6e4f..0000000000
--- a/runtime/dex/code_item_accessors.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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.
- */
-
-// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump.
-
-#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_
-#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_
-
-#include "base/mutex.h"
-#include "compact_dex_file.h"
-#include "dex_file.h"
-#include "dex_instruction_iterator.h"
-#include "standard_dex_file.h"
-
-namespace art {
-
-class ArtMethod;
-
-// Abstracts accesses to the instruction fields of code items for CompactDexFile and
-// StandardDexFile.
-class CodeItemInstructionAccessor {
- public:
- ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile& dex_file,
- const DexFile::CodeItem* code_item);
-
- ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method);
-
- ALWAYS_INLINE DexInstructionIterator begin() const;
-
- ALWAYS_INLINE DexInstructionIterator end() const;
-
- IterationRange<DexInstructionIterator> InstructionsFrom(uint32_t start_dex_pc) const;
-
- uint32_t InsnsSizeInCodeUnits() const {
- return insns_size_in_code_units_;
- }
-
- const uint16_t* Insns() const {
- return insns_;
- }
-
- // Return the instruction for a dex pc.
- const Instruction& InstructionAt(uint32_t dex_pc) const {
- DCHECK_LT(dex_pc, InsnsSizeInCodeUnits());
- return *Instruction::At(insns_ + dex_pc);
- }
-
- // Return true if the accessor has a code item.
- bool HasCodeItem() const {
- return Insns() != nullptr;
- }
-
- protected:
- CodeItemInstructionAccessor() = default;
-
- ALWAYS_INLINE void Init(uint32_t insns_size_in_code_units, const uint16_t* insns);
- ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
- ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
- ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item);
-
- private:
- // size of the insns array, in 2 byte code units. 0 if there is no code item.
- uint32_t insns_size_in_code_units_ = 0;
-
- // Pointer to the instructions, null if there is no code item.
- const uint16_t* insns_ = 0;
-};
-
-// Abstracts accesses to code item fields other than debug info for CompactDexFile and
-// StandardDexFile.
-class CodeItemDataAccessor : public CodeItemInstructionAccessor {
- public:
- ALWAYS_INLINE CodeItemDataAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item);
-
- uint16_t RegistersSize() const {
- return registers_size_;
- }
-
- uint16_t InsSize() const {
- return ins_size_;
- }
-
- uint16_t OutsSize() const {
- return outs_size_;
- }
-
- uint16_t TriesSize() const {
- return tries_size_;
- }
-
- IterationRange<const DexFile::TryItem*> TryItems() const;
-
- const uint8_t* GetCatchHandlerData(size_t offset = 0) const;
-
- const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const;
-
- inline const void* CodeItemDataEnd() const;
-
- protected:
- CodeItemDataAccessor() = default;
-
- ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
- ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
- ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item);
-
- private:
- // Fields mirrored from the dex/cdex code item.
- uint16_t registers_size_;
- uint16_t ins_size_;
- uint16_t outs_size_;
- uint16_t tries_size_;
-};
-
-// Abstract accesses to code item data including debug info offset. More heavy weight than the other
-// helpers.
-class CodeItemDebugInfoAccessor : public CodeItemDataAccessor {
- public:
- CodeItemDebugInfoAccessor() = default;
-
- // Initialize with an existing offset.
- ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file,
- const DexFile::CodeItem* code_item,
- uint32_t dex_method_index) {
- Init(dex_file, code_item, dex_method_index);
- }
-
- ALWAYS_INLINE void Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item,
- uint32_t dex_method_index);
-
- ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method);
-
- uint32_t DebugInfoOffset() const {
- return debug_info_offset_;
- }
-
- template<typename NewLocalCallback>
- bool DecodeDebugLocalInfo(bool is_static,
- uint32_t method_idx,
- NewLocalCallback new_local,
- void* context) const;
-
- protected:
- ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index);
- ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
-
- private:
- const DexFile* dex_file_ = nullptr;
- uint32_t debug_info_offset_ = 0u;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_H_
diff --git a/runtime/dex/code_item_accessors_test.cc b/runtime/dex/code_item_accessors_test.cc
deleted file mode 100644
index 2bb4dde649..0000000000
--- a/runtime/dex/code_item_accessors_test.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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 "code_item_accessors-inl.h"
-
-#include <sys/mman.h>
-#include <memory>
-#include <vector>
-
-#include "dex_file_loader.h"
-#include "gtest/gtest.h"
-
-namespace art {
-
-class CodeItemAccessorsTest : public testing::Test {};
-
-std::unique_ptr<const DexFile> CreateFakeDex(bool compact_dex, std::vector<uint8_t>* data) {
- data->resize(kPageSize);
- if (compact_dex) {
- CompactDexFile::Header* header =
- const_cast<CompactDexFile::Header*>(CompactDexFile::Header::At(data->data()));
- CompactDexFile::WriteMagic(header->magic_);
- CompactDexFile::WriteCurrentVersion(header->magic_);
- header->data_off_ = 0;
- header->data_size_ = data->size();
- } else {
- StandardDexFile::WriteMagic(data->data());
- StandardDexFile::WriteCurrentVersion(data->data());
- }
- const DexFileLoader dex_file_loader;
- std::string error_msg;
- std::unique_ptr<const DexFile> dex(dex_file_loader.Open(data->data(),
- data->size(),
- "location",
- /*location_checksum*/ 123,
- /*oat_dex_file*/nullptr,
- /*verify*/false,
- /*verify_checksum*/false,
- &error_msg));
- CHECK(dex != nullptr) << error_msg;
- return dex;
-}
-
-TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) {
- std::vector<uint8_t> standard_dex_data;
- std::unique_ptr<const DexFile> standard_dex(CreateFakeDex(/*compact_dex*/false,
- &standard_dex_data));
- ASSERT_TRUE(standard_dex != nullptr);
- std::vector<uint8_t> compact_dex_data;
- std::unique_ptr<const DexFile> compact_dex(CreateFakeDex(/*compact_dex*/true,
- &compact_dex_data));
- ASSERT_TRUE(compact_dex != nullptr);
- static constexpr uint16_t kRegisterSize = 2;
- static constexpr uint16_t kInsSize = 1;
- static constexpr uint16_t kOutsSize = 3;
- static constexpr uint16_t kTriesSize = 4;
- // debug_info_off_ is not accessible from the helpers yet.
- static constexpr size_t kInsnsSizeInCodeUnits = 5;
-
- auto verify_code_item = [&](const DexFile* dex,
- const DexFile::CodeItem* item,
- const uint16_t* insns) {
- CodeItemInstructionAccessor insns_accessor(*dex, item);
- EXPECT_TRUE(insns_accessor.HasCodeItem());
- ASSERT_EQ(insns_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits);
- EXPECT_EQ(insns_accessor.Insns(), insns);
-
- CodeItemDataAccessor data_accessor(*dex, item);
- EXPECT_TRUE(data_accessor.HasCodeItem());
- EXPECT_EQ(data_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits);
- EXPECT_EQ(data_accessor.Insns(), insns);
- EXPECT_EQ(data_accessor.RegistersSize(), kRegisterSize);
- EXPECT_EQ(data_accessor.InsSize(), kInsSize);
- EXPECT_EQ(data_accessor.OutsSize(), kOutsSize);
- EXPECT_EQ(data_accessor.TriesSize(), kTriesSize);
- };
-
- StandardDexFile::CodeItem* dex_code_item =
- reinterpret_cast<StandardDexFile::CodeItem*>(const_cast<uint8_t*>(standard_dex->Begin()));
- dex_code_item->registers_size_ = kRegisterSize;
- dex_code_item->ins_size_ = kInsSize;
- dex_code_item->outs_size_ = kOutsSize;
- dex_code_item->tries_size_ = kTriesSize;
- dex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits;
- verify_code_item(standard_dex.get(), dex_code_item, dex_code_item->insns_);
-
- CompactDexFile::CodeItem* cdex_code_item =
- reinterpret_cast<CompactDexFile::CodeItem*>(const_cast<uint8_t*>(compact_dex->Begin() +
- CompactDexFile::CodeItem::kMaxPreHeaderSize * sizeof(uint16_t)));
- std::vector<uint16_t> preheader;
- cdex_code_item->Create(kRegisterSize,
- kInsSize,
- kOutsSize,
- kTriesSize,
- kInsnsSizeInCodeUnits,
- cdex_code_item->GetPreHeader());
-
- verify_code_item(compact_dex.get(), cdex_code_item, cdex_code_item->insns_);
-}
-
-} // namespace art
diff --git a/runtime/dex/compact_dex_debug_info.cc b/runtime/dex/compact_dex_debug_info.cc
deleted file mode 100644
index 19495ca92c..0000000000
--- a/runtime/dex/compact_dex_debug_info.cc
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 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 "compact_dex_debug_info.h"
-
-#include "compact_dex_utils.h"
-#include "leb128.h"
-
-namespace art {
-
-constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex;
-
-CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
- uint32_t debug_info_base,
- uint32_t debug_info_table_offset)
- : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)),
- debug_info_base_(debug_info_base),
- data_begin_(data_begin) {}
-
-uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const {
- const uint32_t offset = table_[method_idx / kElementsPerIndex];
- const size_t bit_index = method_idx % kElementsPerIndex;
-
- const uint8_t* block = data_begin_ + offset;
- uint16_t bit_mask = *block;
- ++block;
- bit_mask = (bit_mask << kBitsPerByte) | *block;
- ++block;
- if ((bit_mask & (1 << bit_index)) == 0) {
- // Bit is not set means the offset is 0 for the debug info.
- return 0u;
- }
- // Trim off the bits above the index we want and count how many bits are set. This is how many
- // lebs we need to decode.
- size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index));
- DCHECK_GT(count, 0u);
- uint32_t current_offset = debug_info_base_;
- do {
- current_offset += DecodeUnsignedLeb128(&block);
- --count;
- } while (count > 0);
- return current_offset;
-}
-
-void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets,
- std::vector<uint8_t>* out_data,
- uint32_t* out_min_offset,
- uint32_t* out_table_offset) {
- DCHECK(out_data != nullptr);
- DCHECK(out_data->empty());
- // Calculate the base offset and return it.
- *out_min_offset = std::numeric_limits<uint32_t>::max();
- for (const uint32_t offset : debug_info_offsets) {
- if (offset != 0u) {
- *out_min_offset = std::min(*out_min_offset, offset);
- }
- }
- // Write the leb blocks and store the important offsets (each kElementsPerIndex elements).
- size_t block_start = 0;
-
- std::vector<uint32_t> offset_table;
-
- // Write data first then the table.
- while (block_start < debug_info_offsets.size()) {
- // Write the offset of the block for each block.
- offset_table.push_back(out_data->size());
-
- // Block size of up to kElementsPerIndex
- const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex);
-
- // Calculate bit mask since need to write that first.
- uint16_t bit_mask = 0u;
- for (size_t i = 0; i < block_size; ++i) {
- if (debug_info_offsets[block_start + i] != 0u) {
- bit_mask |= 1 << i;
- }
- }
- // Write bit mask.
- out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte));
- out_data->push_back(static_cast<uint8_t>(bit_mask));
-
- // Write debug info offsets relative to the current offset.
- uint32_t current_offset = *out_min_offset;
- for (size_t i = 0; i < block_size; ++i) {
- const uint32_t debug_info_offset = debug_info_offsets[block_start + i];
- if (debug_info_offset != 0u) {
- uint32_t delta = debug_info_offset - current_offset;
- EncodeUnsignedLeb128(out_data, delta);
- current_offset = debug_info_offset;
- }
- }
-
- block_start += block_size;
- }
-
- // Write the offset table.
- AlignmentPadVector(out_data, alignof(uint32_t));
- *out_table_offset = out_data->size();
- out_data->insert(out_data->end(),
- reinterpret_cast<const uint8_t*>(&offset_table[0]),
- reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size()));
-}
-
-} // namespace art
diff --git a/runtime/dex/compact_dex_debug_info.h b/runtime/dex/compact_dex_debug_info.h
deleted file mode 100644
index 1aff75879e..0000000000
--- a/runtime/dex/compact_dex_debug_info.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2018 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_DEX_COMPACT_DEX_DEBUG_INFO_H_
-#define ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_
-
-#include <cstdint>
-#include <vector>
-
-namespace art {
-
-// Debug offset table for compact dex, aims to minimize size while still providing reasonable
-// speed (10-20ns average time per lookup on host).
-class CompactDexDebugInfoOffsetTable {
- public:
- // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the
- // integer is modified.
- static constexpr size_t kElementsPerIndex = 16;
-
- // Leb block format:
- // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk.
- // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset
- // changes compared to the previous index.
-
- class Accessor {
- public:
- Accessor(const uint8_t* data_begin,
- uint32_t debug_info_base,
- uint32_t debug_info_table_offset);
-
- // Return the debug info for a method index (or 0 if it doesn't have one).
- uint32_t GetDebugInfoOffset(uint32_t method_idx) const;
-
- private:
- const uint32_t* const table_;
- const uint32_t debug_info_base_;
- const uint8_t* const data_begin_;
- };
-
- // Returned offsets are all relative to debug_info_offsets.
- static void Build(const std::vector<uint32_t>& debug_info_offsets,
- std::vector<uint8_t>* out_data,
- uint32_t* out_min_offset,
- uint32_t* out_table_offset);
-
- // 32 bit aligned for the offset table.
- static constexpr size_t kAlignment = sizeof(uint32_t);
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_
diff --git a/runtime/dex/compact_dex_debug_info_test.cc b/runtime/dex/compact_dex_debug_info_test.cc
deleted file mode 100644
index 3267612443..0000000000
--- a/runtime/dex/compact_dex_debug_info_test.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2018 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 <vector>
-
-#include "base/logging.h"
-#include "dex/compact_dex_debug_info.h"
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(CompactDexDebugInfoTest, TestBuildAndAccess) {
- const size_t kDebugInfoMinOffset = 1234567;
- std::vector<uint32_t> offsets = {
- 0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0,
- std::numeric_limits<uint32_t>::max() - kDebugInfoMinOffset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
- };
- // Add some large offset since the debug info section will never be that close to the beginning
- // of the file.
- for (uint32_t& offset : offsets) {
- if (offset != 0u) {
- offset += kDebugInfoMinOffset;
- }
- }
-
- std::vector<uint8_t> data;
- uint32_t base_offset = 0;
- uint32_t table_offset = 0;
- CompactDexDebugInfoOffsetTable::Build(offsets,
- /*out*/ &data,
- /*out*/ &base_offset,
- /*out*/ &table_offset);
- EXPECT_GE(base_offset, kDebugInfoMinOffset);
- EXPECT_LT(table_offset, data.size());
- ASSERT_GT(data.size(), 0u);
- const size_t before_size = offsets.size() * sizeof(offsets.front());
- EXPECT_LT(data.size(), before_size);
-
- // Note that the accessor requires the data to be aligned. Use memmap to accomplish this.
- std::string error_msg;
- // Leave some extra room since we don't copy the table at the start (for testing).
- constexpr size_t kExtraOffset = 4 * 128;
- std::vector<uint8_t> fake_dex(data.size() + kExtraOffset);
- std::copy(data.begin(), data.end(), fake_dex.data() + kExtraOffset);
-
- CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset,
- base_offset,
- table_offset);
- for (size_t i = 0; i < offsets.size(); ++i) {
- EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i));
- }
-
- // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller
- // for sorted increasing order.
- std::sort(offsets.begin(), offsets.end());
- std::vector<uint8_t> sorted_data;
- CompactDexDebugInfoOffsetTable::Build(offsets,
- /*out*/ &sorted_data,
- /*out*/ &base_offset,
- /*out*/ &table_offset);
- EXPECT_LT(sorted_data.size(), data.size());
- {
- ScopedLogSeverity sls(LogSeverity::INFO);
- LOG(INFO) << "raw size " << before_size
- << " table size " << data.size()
- << " sorted table size " << sorted_data.size();
- }
-}
-
-} // namespace art
diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc
deleted file mode 100644
index ce289d4d7b..0000000000
--- a/runtime/dex/compact_dex_file.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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 "compact_dex_file.h"
-
-#include "code_item_accessors-inl.h"
-#include "dex_file-inl.h"
-#include "leb128.h"
-
-namespace art {
-
-constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize];
-constexpr uint8_t CompactDexFile::kDexMagicVersion[];
-
-void CompactDexFile::WriteMagic(uint8_t* magic) {
- std::copy_n(kDexMagic, kDexMagicSize, magic);
-}
-
-void CompactDexFile::WriteCurrentVersion(uint8_t* magic) {
- std::copy_n(kDexMagicVersion, kDexVersionLen, magic + kDexMagicSize);
-}
-
-bool CompactDexFile::IsMagicValid(const uint8_t* magic) {
- return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
-}
-
-bool CompactDexFile::IsVersionValid(const uint8_t* magic) {
- const uint8_t* version = &magic[sizeof(kDexMagic)];
- return memcmp(version, kDexMagicVersion, kDexVersionLen) == 0;
-}
-
-bool CompactDexFile::IsMagicValid() const {
- return IsMagicValid(header_->magic_);
-}
-
-bool CompactDexFile::IsVersionValid() const {
- return IsVersionValid(header_->magic_);
-}
-
-bool CompactDexFile::SupportsDefaultMethods() const {
- return (GetHeader().GetFeatureFlags() &
- static_cast<uint32_t>(FeatureFlags::kDefaultMethods)) != 0;
-}
-
-uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const {
- DCHECK(IsInDataSection(&item));
- return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) -
- reinterpret_cast<uintptr_t>(&item);
-}
-
-
-uint32_t CompactDexFile::CalculateChecksum(const uint8_t* base_begin,
- size_t base_size,
- const uint8_t* data_begin,
- size_t data_size) {
- Header temp_header(*Header::At(base_begin));
- // Zero out fields that are not included in the sum.
- temp_header.checksum_ = 0u;
- temp_header.data_off_ = 0u;
- temp_header.data_size_ = 0u;
- uint32_t checksum = ChecksumMemoryRange(reinterpret_cast<const uint8_t*>(&temp_header),
- sizeof(temp_header));
- // Exclude the header since we already computed it's checksum.
- checksum = (checksum * 31) ^ ChecksumMemoryRange(base_begin + sizeof(temp_header),
- base_size - sizeof(temp_header));
- checksum = (checksum * 31) ^ ChecksumMemoryRange(data_begin, data_size);
- return checksum;
-}
-
-uint32_t CompactDexFile::CalculateChecksum() const {
- return CalculateChecksum(Begin(), Size(), DataBegin(), DataSize());
-}
-
-CompactDexFile::CompactDexFile(const uint8_t* base,
- size_t size,
- const uint8_t* data_begin,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- DexFileContainer* container)
- : DexFile(base,
- size,
- data_begin,
- data_size,
- location,
- location_checksum,
- oat_dex_file,
- container,
- /*is_compact_dex*/ true),
- debug_info_offsets_(DataBegin() + GetHeader().debug_info_offsets_pos_,
- GetHeader().debug_info_base_,
- GetHeader().debug_info_offsets_table_offset_) {}
-
-} // namespace art
diff --git a/runtime/dex/compact_dex_file.h b/runtime/dex/compact_dex_file.h
deleted file mode 100644
index 31aeb27872..0000000000
--- a/runtime/dex/compact_dex_file.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * 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_DEX_COMPACT_DEX_FILE_H_
-#define ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_
-
-#include "base/casts.h"
-#include "dex_file.h"
-#include "dex/compact_dex_debug_info.h"
-
-namespace art {
-
-// CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage.
-class CompactDexFile : public DexFile {
- public:
- static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' };
- static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'};
-
- enum class FeatureFlags : uint32_t {
- kDefaultMethods = 0x1,
- };
-
- class Header : public DexFile::Header {
- public:
- static const Header* At(const void* at) {
- return reinterpret_cast<const Header*>(at);
- }
-
- uint32_t GetFeatureFlags() const {
- return feature_flags_;
- }
-
- uint32_t GetDataOffset() const {
- return data_off_;
- }
-
- uint32_t GetDataSize() const {
- return data_size_;
- }
-
- private:
- uint32_t feature_flags_ = 0u;
-
- // Position in the compact dex file for the debug info table data starts.
- uint32_t debug_info_offsets_pos_ = 0u;
-
- // Offset into the debug info table data where the lookup table is.
- uint32_t debug_info_offsets_table_offset_ = 0u;
-
- // Base offset of where debug info starts in the dex file.
- uint32_t debug_info_base_ = 0u;
-
- friend class CompactDexFile;
- friend class CompactDexWriter;
- };
-
- // Like the standard code item except without a debug info offset. Each code item may have a
- // preheader to encode large methods. In 99% of cases, the preheader is not used. This enables
- // smaller size with a good fast path case in the accessors.
- struct CodeItem : public DexFile::CodeItem {
- static constexpr size_t kAlignment = sizeof(uint16_t);
- // Max preheader size in uint16_ts.
- static constexpr size_t kMaxPreHeaderSize = 6;
-
- private:
- CodeItem() = default;
-
- static constexpr size_t kRegistersSizeShift = 12;
- static constexpr size_t kInsSizeShift = 8;
- static constexpr size_t kOutsSizeShift = 4;
- static constexpr size_t kTriesSizeSizeShift = 0;
- static constexpr uint16_t kFlagPreHeaderRegisterSize = 0x1 << 0;
- static constexpr uint16_t kFlagPreHeaderInsSize = 0x1 << 1;
- static constexpr uint16_t kFlagPreHeaderOutsSize = 0x1 << 2;
- static constexpr uint16_t kFlagPreHeaderTriesSize = 0x1 << 3;
- static constexpr uint16_t kFlagPreHeaderInsnsSize = 0x1 << 4;
- static constexpr size_t kInsnsSizeShift = 5;
- static constexpr size_t kInsnsSizeBits = sizeof(uint16_t) * kBitsPerByte - kInsnsSizeShift;
-
- // Combined preheader flags for fast testing if we need to go slow path.
- static constexpr uint16_t kFlagPreHeaderCombined =
- kFlagPreHeaderRegisterSize |
- kFlagPreHeaderInsSize |
- kFlagPreHeaderOutsSize |
- kFlagPreHeaderTriesSize |
- kFlagPreHeaderInsnsSize;
-
- // Create a code item and associated preheader if required based on field values.
- // Returns the start of the preheader. The preheader buffer must be at least as large as
- // kMaxPreHeaderSize;
- uint16_t* Create(uint16_t registers_size,
- uint16_t ins_size,
- uint16_t outs_size,
- uint16_t tries_size,
- uint32_t insns_size_in_code_units,
- uint16_t* out_preheader) {
- // Dex verification ensures that registers size > ins_size, so we can subtract the registers
- // size accordingly to reduce how often we need to use the preheader.
- DCHECK_GE(registers_size, ins_size);
- registers_size -= ins_size;
- fields_ = (registers_size & 0xF) << kRegistersSizeShift;
- fields_ |= (ins_size & 0xF) << kInsSizeShift;
- fields_ |= (outs_size & 0xF) << kOutsSizeShift;
- fields_ |= (tries_size & 0xF) << kTriesSizeSizeShift;
- registers_size &= ~0xF;
- ins_size &= ~0xF;
- outs_size &= ~0xF;
- tries_size &= ~0xF;
- insns_count_and_flags_ = 0;
- const size_t masked_count = insns_size_in_code_units & ((1 << kInsnsSizeBits) - 1);
- insns_count_and_flags_ |= masked_count << kInsnsSizeShift;
- insns_size_in_code_units -= masked_count;
-
- // Since the preheader case is rare (1% of code items), use a suboptimally large but fast
- // decoding format.
- if (insns_size_in_code_units != 0) {
- insns_count_and_flags_ |= kFlagPreHeaderInsnsSize;
- --out_preheader;
- *out_preheader = static_cast<uint16_t>(insns_size_in_code_units);
- --out_preheader;
- *out_preheader = static_cast<uint16_t>(insns_size_in_code_units >> 16);
- }
- auto preheader_encode = [&](uint16_t size, uint16_t flag) {
- if (size != 0) {
- insns_count_and_flags_ |= flag;
- --out_preheader;
- *out_preheader = size;
- }
- };
- preheader_encode(registers_size, kFlagPreHeaderRegisterSize);
- preheader_encode(ins_size, kFlagPreHeaderInsSize);
- preheader_encode(outs_size, kFlagPreHeaderOutsSize);
- preheader_encode(tries_size, kFlagPreHeaderTriesSize);
- return out_preheader;
- }
-
- ALWAYS_INLINE bool HasPreHeader(uint16_t flag) const {
- return (insns_count_and_flags_ & flag) != 0;
- }
-
- // Return true if the code item has any preheaders.
- ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) {
- return (insns_count_and_flags & kFlagPreHeaderCombined) != 0;
- }
-
- ALWAYS_INLINE uint16_t* GetPreHeader() {
- return reinterpret_cast<uint16_t*>(this);
- }
-
- ALWAYS_INLINE const uint16_t* GetPreHeader() const {
- return reinterpret_cast<const uint16_t*>(this);
- }
-
- // Decode fields and read the preheader if necessary. If kDecodeOnlyInstructionCount is
- // specified then only the instruction count is decoded.
- template <bool kDecodeOnlyInstructionCount>
- ALWAYS_INLINE void DecodeFields(uint32_t* insns_count,
- uint16_t* registers_size,
- uint16_t* ins_size,
- uint16_t* outs_size,
- uint16_t* tries_size) const {
- *insns_count = insns_count_and_flags_ >> kInsnsSizeShift;
- if (!kDecodeOnlyInstructionCount) {
- const uint16_t fields = fields_;
- *registers_size = (fields >> kRegistersSizeShift) & 0xF;
- *ins_size = (fields >> kInsSizeShift) & 0xF;
- *outs_size = (fields >> kOutsSizeShift) & 0xF;
- *tries_size = (fields >> kTriesSizeSizeShift) & 0xF;
- }
- if (UNLIKELY(HasAnyPreHeader(insns_count_and_flags_))) {
- const uint16_t* preheader = GetPreHeader();
- if (HasPreHeader(kFlagPreHeaderInsnsSize)) {
- --preheader;
- *insns_count += static_cast<uint32_t>(*preheader);
- --preheader;
- *insns_count += static_cast<uint32_t>(*preheader) << 16;
- }
- if (!kDecodeOnlyInstructionCount) {
- if (HasPreHeader(kFlagPreHeaderRegisterSize)) {
- --preheader;
- *registers_size += preheader[0];
- }
- if (HasPreHeader(kFlagPreHeaderInsSize)) {
- --preheader;
- *ins_size += preheader[0];
- }
- if (HasPreHeader(kFlagPreHeaderOutsSize)) {
- --preheader;
- *outs_size += preheader[0];
- }
- if (HasPreHeader(kFlagPreHeaderTriesSize)) {
- --preheader;
- *tries_size += preheader[0];
- }
- }
- }
- if (!kDecodeOnlyInstructionCount) {
- *registers_size += *ins_size;
- }
- }
-
- // Packed code item data, 4 bits each: [registers_size, ins_size, outs_size, tries_size]
- uint16_t fields_;
-
- // 5 bits for if either of the fields required preheader extension, 11 bits for the number of
- // instruction code units.
- uint16_t insns_count_and_flags_;
-
- uint16_t insns_[1]; // actual array of bytecode.
-
- ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
- ART_FRIEND_TEST(CompactDexFileTest, CodeItemFields);
- friend class CodeItemDataAccessor;
- friend class CodeItemDebugInfoAccessor;
- friend class CodeItemInstructionAccessor;
- friend class CompactDexFile;
- friend class CompactDexWriter;
- DISALLOW_COPY_AND_ASSIGN(CodeItem);
- };
-
- // Write the compact dex specific magic.
- static void WriteMagic(uint8_t* magic);
-
- // Write the current version, note that the input is the address of the magic.
- static void WriteCurrentVersion(uint8_t* magic);
-
- // Returns true if the byte string points to the magic value.
- static bool IsMagicValid(const uint8_t* magic);
- virtual bool IsMagicValid() const OVERRIDE;
-
- // Returns true if the byte string after the magic is the correct value.
- static bool IsVersionValid(const uint8_t* magic);
- virtual bool IsVersionValid() const OVERRIDE;
-
- // TODO This is completely a guess. We really need to do better. b/72402467
- // We ask for 64 megabytes which should be big enough for any realistic dex file.
- virtual size_t GetDequickenedSize() const OVERRIDE {
- return 64 * MB;
- }
-
- const Header& GetHeader() const {
- return down_cast<const Header&>(DexFile::GetHeader());
- }
-
- virtual bool SupportsDefaultMethods() const OVERRIDE;
-
- uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
-
- uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const {
- return debug_info_offsets_.GetDebugInfoOffset(dex_method_index);
- }
-
- static uint32_t CalculateChecksum(const uint8_t* base_begin,
- size_t base_size,
- const uint8_t* data_begin,
- size_t data_size);
- virtual uint32_t CalculateChecksum() const OVERRIDE;
-
- private:
- CompactDexFile(const uint8_t* base,
- size_t size,
- const uint8_t* data_begin,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- DexFileContainer* container);
-
- CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_;
-
- friend class DexFile;
- friend class DexFileLoader;
- DISALLOW_COPY_AND_ASSIGN(CompactDexFile);
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_COMPACT_DEX_FILE_H_
diff --git a/runtime/dex/compact_dex_file_test.cc b/runtime/dex/compact_dex_file_test.cc
deleted file mode 100644
index 517c5873ed..0000000000
--- a/runtime/dex/compact_dex_file_test.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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 "compact_dex_file.h"
-#include "dex_file_loader.h"
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(CompactDexFileTest, MagicAndVersion) {
- // Test permutations of valid/invalid headers.
- for (size_t i = 0; i < 2; ++i) {
- for (size_t j = 0; j < 2; ++j) {
- static const size_t len = CompactDexFile::kDexVersionLen + CompactDexFile::kDexMagicSize;
- uint8_t header[len] = {};
- std::fill_n(header, len, 0x99);
- const bool valid_magic = (i & 1) == 0;
- const bool valid_version = (j & 1) == 0;
- if (valid_magic) {
- CompactDexFile::WriteMagic(header);
- }
- if (valid_version) {
- CompactDexFile::WriteCurrentVersion(header);
- }
- EXPECT_EQ(valid_magic, CompactDexFile::IsMagicValid(header));
- EXPECT_EQ(valid_version, CompactDexFile::IsVersionValid(header));
- EXPECT_EQ(valid_magic, DexFileLoader::IsMagicValid(header));
- EXPECT_EQ(valid_magic && valid_version, DexFileLoader::IsVersionAndMagicValid(header));
- }
- }
-}
-
-TEST(CompactDexFileTest, CodeItemFields) {
- auto test_and_write = [&] (uint16_t registers_size,
- uint16_t ins_size,
- uint16_t outs_size,
- uint16_t tries_size,
- uint32_t insns_size_in_code_units) {
- ASSERT_GE(registers_size, ins_size);
- uint16_t buffer[sizeof(CompactDexFile::CodeItem) +
- CompactDexFile::CodeItem::kMaxPreHeaderSize] = {};
- CompactDexFile::CodeItem* code_item = reinterpret_cast<CompactDexFile::CodeItem*>(
- &buffer[CompactDexFile::CodeItem::kMaxPreHeaderSize]);
- const uint16_t* preheader_ptr = code_item->Create(registers_size,
- ins_size,
- outs_size,
- tries_size,
- insns_size_in_code_units,
- code_item->GetPreHeader());
- ASSERT_GT(preheader_ptr, buffer);
-
- uint16_t out_registers_size;
- uint16_t out_ins_size;
- uint16_t out_outs_size;
- uint16_t out_tries_size;
- uint32_t out_insns_size_in_code_units;
- code_item->DecodeFields</*kDecodeOnlyInstructionCount*/false>(&out_insns_size_in_code_units,
- &out_registers_size,
- &out_ins_size,
- &out_outs_size,
- &out_tries_size);
- ASSERT_EQ(registers_size, out_registers_size);
- ASSERT_EQ(ins_size, out_ins_size);
- ASSERT_EQ(outs_size, out_outs_size);
- ASSERT_EQ(tries_size, out_tries_size);
- ASSERT_EQ(insns_size_in_code_units, out_insns_size_in_code_units);
-
- ++out_insns_size_in_code_units; // Force value to change.
- code_item->DecodeFields</*kDecodeOnlyInstructionCount*/true>(&out_insns_size_in_code_units,
- /*registers_size*/ nullptr,
- /*ins_size*/ nullptr,
- /*outs_size*/ nullptr,
- /*tries_size*/ nullptr);
- ASSERT_EQ(insns_size_in_code_units, out_insns_size_in_code_units);
- };
- static constexpr uint32_t kMax32 = std::numeric_limits<uint32_t>::max();
- static constexpr uint16_t kMax16 = std::numeric_limits<uint16_t>::max();
- test_and_write(0, 0, 0, 0, 0);
- test_and_write(kMax16, kMax16, kMax16, kMax16, kMax32);
- test_and_write(kMax16 - 1, kMax16 - 2, kMax16 - 3, kMax16 - 4, kMax32 - 5);
- test_and_write(kMax16 - 4, kMax16 - 5, kMax16 - 3, kMax16 - 2, kMax32 - 1);
- test_and_write(5, 4, 3, 2, 1);
- test_and_write(5, 0, 3, 2, 1);
- test_and_write(kMax16, 0, kMax16 / 2, 1234, kMax32 / 4);
-}
-
-} // namespace art
diff --git a/runtime/dex/compact_dex_level.h b/runtime/dex/compact_dex_level.h
deleted file mode 100644
index de9ca3c783..0000000000
--- a/runtime/dex/compact_dex_level.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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_DEX_COMPACT_DEX_LEVEL_H_
-#define ART_RUNTIME_DEX_COMPACT_DEX_LEVEL_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "dex_file.h"
-
-namespace art {
-
-// Optimization level for compact dex generation.
-enum class CompactDexLevel {
- // Level none means not generated.
- kCompactDexLevelNone,
- // Level fast means optimizations that don't take many resources to perform.
- kCompactDexLevelFast,
-};
-
-#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL
-#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified.
-#else
-#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast
-#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone
-
-#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \
- ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \
- ART_DEFAULT_COMPACT_DEX_LEVEL)
-
-static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT;
-#endif
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_COMPACT_DEX_LEVEL_H_
diff --git a/runtime/dex/compact_dex_utils.h b/runtime/dex/compact_dex_utils.h
deleted file mode 100644
index 1c7e9514fd..0000000000
--- a/runtime/dex/compact_dex_utils.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 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_DEX_COMPACT_DEX_UTILS_H_
-#define ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_
-
-#include <vector>
-
-#include "base/bit_utils.h"
-
-namespace art {
-
-// Add padding to the end of the array until the size is aligned.
-template <typename T, template<typename> class Allocator>
-static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest,
- size_t alignment) {
- while (!IsAlignedParam(dest->size(), alignment)) {
- dest->push_back(T());
- }
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_
diff --git a/runtime/dex/descriptors_names.cc b/runtime/dex/descriptors_names.cc
deleted file mode 100644
index 8124e7256f..0000000000
--- a/runtime/dex/descriptors_names.cc
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * Copyright (C) 2011 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 "descriptors_names.h"
-
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
-
-#include "dex/utf-inl.h"
-
-namespace art {
-
-using android::base::StringAppendF;
-using android::base::StringPrintf;
-
-void AppendPrettyDescriptor(const char* descriptor, std::string* result) {
- // Count the number of '['s to get the dimensionality.
- const char* c = descriptor;
- size_t dim = 0;
- while (*c == '[') {
- dim++;
- c++;
- }
-
- // Reference or primitive?
- if (*c == 'L') {
- // "[[La/b/C;" -> "a.b.C[][]".
- c++; // Skip the 'L'.
- } else {
- // "[[B" -> "byte[][]".
- // To make life easier, we make primitives look like unqualified
- // reference types.
- switch (*c) {
- case 'B': c = "byte;"; break;
- case 'C': c = "char;"; break;
- case 'D': c = "double;"; break;
- case 'F': c = "float;"; break;
- case 'I': c = "int;"; break;
- case 'J': c = "long;"; break;
- case 'S': c = "short;"; break;
- case 'Z': c = "boolean;"; break;
- case 'V': c = "void;"; break; // Used when decoding return types.
- default: result->append(descriptor); return;
- }
- }
-
- // At this point, 'c' is a string of the form "fully/qualified/Type;"
- // or "primitive;". Rewrite the type with '.' instead of '/':
- const char* p = c;
- while (*p != ';') {
- char ch = *p++;
- if (ch == '/') {
- ch = '.';
- }
- result->push_back(ch);
- }
- // ...and replace the semicolon with 'dim' "[]" pairs:
- for (size_t i = 0; i < dim; ++i) {
- result->append("[]");
- }
-}
-
-std::string PrettyDescriptor(const char* descriptor) {
- std::string result;
- AppendPrettyDescriptor(descriptor, &result);
- return result;
-}
-
-std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
- // Remove the leading 'L' and trailing ';'...
- std::string class_name(class_descriptor);
- CHECK_EQ(class_name[0], 'L') << class_name;
- CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
- class_name.erase(0, 1);
- class_name.erase(class_name.size() - 1, 1);
-
- std::string short_name;
- short_name += "Java_";
- short_name += MangleForJni(class_name);
- short_name += "_";
- short_name += MangleForJni(method);
- return short_name;
-}
-
-// See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules.
-std::string MangleForJni(const std::string& s) {
- std::string result;
- size_t char_count = CountModifiedUtf8Chars(s.c_str());
- const char* cp = &s[0];
- for (size_t i = 0; i < char_count; ++i) {
- uint32_t ch = GetUtf16FromUtf8(&cp);
- if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
- result.push_back(ch);
- } else if (ch == '.' || ch == '/') {
- result += "_";
- } else if (ch == '_') {
- result += "_1";
- } else if (ch == ';') {
- result += "_2";
- } else if (ch == '[') {
- result += "_3";
- } else {
- const uint16_t leading = GetLeadingUtf16Char(ch);
- const uint32_t trailing = GetTrailingUtf16Char(ch);
-
- StringAppendF(&result, "_0%04x", leading);
- if (trailing != 0) {
- StringAppendF(&result, "_0%04x", trailing);
- }
- }
- }
- return result;
-}
-
-std::string DotToDescriptor(const char* class_name) {
- std::string descriptor(class_name);
- std::replace(descriptor.begin(), descriptor.end(), '.', '/');
- if (descriptor.length() > 0 && descriptor[0] != '[') {
- descriptor = "L" + descriptor + ";";
- }
- return descriptor;
-}
-
-std::string DescriptorToDot(const char* descriptor) {
- size_t length = strlen(descriptor);
- if (length > 1) {
- if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
- // Descriptors have the leading 'L' and trailing ';' stripped.
- std::string result(descriptor + 1, length - 2);
- std::replace(result.begin(), result.end(), '/', '.');
- return result;
- } else {
- // For arrays the 'L' and ';' remain intact.
- std::string result(descriptor);
- std::replace(result.begin(), result.end(), '/', '.');
- return result;
- }
- }
- // Do nothing for non-class/array descriptors.
- return descriptor;
-}
-
-std::string DescriptorToName(const char* descriptor) {
- size_t length = strlen(descriptor);
- if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
- std::string result(descriptor + 1, length - 2);
- return result;
- }
- return descriptor;
-}
-
-// Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii.
-static uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = {
- 0x00000000, // 00..1f low control characters; nothing valid
- 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
- 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
- 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z'
-};
-
-// Helper for IsValidPartOfMemberNameUtf8(); do not call directly.
-static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {
- /*
- * It's a multibyte encoded character. Decode it and analyze. We
- * accept anything that isn't (a) an improperly encoded low value,
- * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
- * control character, or (e) a high space, layout, or special
- * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
- * U+fff0..U+ffff). This is all specified in the dex format
- * document.
- */
-
- const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr);
- const uint16_t leading = GetLeadingUtf16Char(pair);
-
- // We have a surrogate pair resulting from a valid 4 byte UTF sequence.
- // No further checks are necessary because 4 byte sequences span code
- // points [U+10000, U+1FFFFF], which are valid codepoints in a dex
- // identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of
- // the surrogate halves are valid and well formed in this instance.
- if (GetTrailingUtf16Char(pair) != 0) {
- return true;
- }
-
-
- // We've encountered a one, two or three byte UTF-8 sequence. The
- // three byte UTF-8 sequence could be one half of a surrogate pair.
- switch (leading >> 8) {
- case 0x00:
- // It's only valid if it's above the ISO-8859-1 high space (0xa0).
- return (leading > 0x00a0);
- case 0xd8:
- case 0xd9:
- case 0xda:
- case 0xdb:
- {
- // We found a three byte sequence encoding one half of a surrogate.
- // Look for the other half.
- const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr);
- const uint16_t trailing = GetLeadingUtf16Char(pair2);
-
- return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff);
- }
- case 0xdc:
- case 0xdd:
- case 0xde:
- case 0xdf:
- // It's a trailing surrogate, which is not valid at this point.
- return false;
- case 0x20:
- case 0xff:
- // It's in the range that has spaces, controls, and specials.
- switch (leading & 0xfff8) {
- case 0x2000:
- case 0x2008:
- case 0x2028:
- case 0xfff0:
- case 0xfff8:
- return false;
- }
- return true;
- default:
- return true;
- }
-
- UNREACHABLE();
-}
-
-/* Return whether the pointed-at modified-UTF-8 encoded character is
- * valid as part of a member name, updating the pointer to point past
- * the consumed character. This will consume two encoded UTF-16 code
- * points if the character is encoded as a surrogate pair. Also, if
- * this function returns false, then the given pointer may only have
- * been partially advanced.
- */
-static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
- uint8_t c = (uint8_t) **pUtf8Ptr;
- if (LIKELY(c <= 0x7f)) {
- // It's low-ascii, so check the table.
- uint32_t wordIdx = c >> 5;
- uint32_t bitIdx = c & 0x1f;
- (*pUtf8Ptr)++;
- return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0;
- }
-
- // It's a multibyte encoded character. Call a non-inline function
- // for the heavy lifting.
- return IsValidPartOfMemberNameUtf8Slow(pUtf8Ptr);
-}
-
-bool IsValidMemberName(const char* s) {
- bool angle_name = false;
-
- switch (*s) {
- case '\0':
- // The empty string is not a valid name.
- return false;
- case '<':
- angle_name = true;
- s++;
- break;
- }
-
- while (true) {
- switch (*s) {
- case '\0':
- return !angle_name;
- case '>':
- return angle_name && s[1] == '\0';
- }
-
- if (!IsValidPartOfMemberNameUtf8(&s)) {
- return false;
- }
- }
-}
-
-enum ClassNameType { kName, kDescriptor };
-template<ClassNameType kType, char kSeparator>
-static bool IsValidClassName(const char* s) {
- int arrayCount = 0;
- while (*s == '[') {
- arrayCount++;
- s++;
- }
-
- if (arrayCount > 255) {
- // Arrays may have no more than 255 dimensions.
- return false;
- }
-
- ClassNameType type = kType;
- if (type != kDescriptor && arrayCount != 0) {
- /*
- * If we're looking at an array of some sort, then it doesn't
- * matter if what is being asked for is a class name; the
- * format looks the same as a type descriptor in that case, so
- * treat it as such.
- */
- type = kDescriptor;
- }
-
- if (type == kDescriptor) {
- /*
- * We are looking for a descriptor. Either validate it as a
- * single-character primitive type, or continue on to check the
- * embedded class name (bracketed by "L" and ";").
- */
- switch (*(s++)) {
- case 'B':
- case 'C':
- case 'D':
- case 'F':
- case 'I':
- case 'J':
- case 'S':
- case 'Z':
- // These are all single-character descriptors for primitive types.
- return (*s == '\0');
- case 'V':
- // Non-array void is valid, but you can't have an array of void.
- return (arrayCount == 0) && (*s == '\0');
- case 'L':
- // Class name: Break out and continue below.
- break;
- default:
- // Oddball descriptor character.
- return false;
- }
- }
-
- /*
- * We just consumed the 'L' that introduces a class name as part
- * of a type descriptor, or we are looking for an unadorned class
- * name.
- */
-
- bool sepOrFirst = true; // first character or just encountered a separator.
- for (;;) {
- uint8_t c = (uint8_t) *s;
- switch (c) {
- case '\0':
- /*
- * Premature end for a type descriptor, but valid for
- * a class name as long as we haven't encountered an
- * empty component (including the degenerate case of
- * the empty string "").
- */
- return (type == kName) && !sepOrFirst;
- case ';':
- /*
- * Invalid character for a class name, but the
- * legitimate end of a type descriptor. In the latter
- * case, make sure that this is the end of the string
- * and that it doesn't end with an empty component
- * (including the degenerate case of "L;").
- */
- return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0');
- case '/':
- case '.':
- if (c != kSeparator) {
- // The wrong separator character.
- return false;
- }
- if (sepOrFirst) {
- // Separator at start or two separators in a row.
- return false;
- }
- sepOrFirst = true;
- s++;
- break;
- default:
- if (!IsValidPartOfMemberNameUtf8(&s)) {
- return false;
- }
- sepOrFirst = false;
- break;
- }
- }
-}
-
-bool IsValidBinaryClassName(const char* s) {
- return IsValidClassName<kName, '.'>(s);
-}
-
-bool IsValidJniClassName(const char* s) {
- return IsValidClassName<kName, '/'>(s);
-}
-
-bool IsValidDescriptor(const char* s) {
- return IsValidClassName<kDescriptor, '/'>(s);
-}
-
-void Split(const std::string& s, char separator, std::vector<std::string>* result) {
- const char* p = s.data();
- const char* end = p + s.size();
- while (p != end) {
- if (*p == separator) {
- ++p;
- } else {
- const char* start = p;
- while (++p != end && *p != separator) {
- // Skip to the next occurrence of the separator.
- }
- result->push_back(std::string(start, p - start));
- }
- }
-}
-
-std::string PrettyDescriptor(Primitive::Type type) {
- return PrettyDescriptor(Primitive::Descriptor(type));
-}
-
-} // namespace art
diff --git a/runtime/dex/descriptors_names.h b/runtime/dex/descriptors_names.h
deleted file mode 100644
index 22e9573556..0000000000
--- a/runtime/dex/descriptors_names.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DESCRIPTORS_NAMES_H_
-#define ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_
-
-#include <string>
-
-#include "primitive.h"
-
-namespace art {
-
-// Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf,
-// one of which is probably more useful to you.
-// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
-// "[[I" would be "int[][]", "[Ljava/lang/String;" would be
-// "java.lang.String[]", and so forth.
-void AppendPrettyDescriptor(const char* descriptor, std::string* result);
-std::string PrettyDescriptor(const char* descriptor);
-std::string PrettyDescriptor(Primitive::Type type);
-
-// Performs JNI name mangling as described in section 11.3 "Linking Native Methods"
-// of the JNI spec.
-std::string MangleForJni(const std::string& s);
-
-std::string GetJniShortName(const std::string& class_name, const std::string& method_name);
-
-// Turn "java.lang.String" into "Ljava/lang/String;".
-std::string DotToDescriptor(const char* class_name);
-
-// Turn "Ljava/lang/String;" into "java.lang.String" using the conventions of
-// java.lang.Class.getName().
-std::string DescriptorToDot(const char* descriptor);
-
-// Turn "Ljava/lang/String;" into "java/lang/String" using the opposite conventions of
-// java.lang.Class.getName().
-std::string DescriptorToName(const char* descriptor);
-
-// Tests for whether 's' is a valid class name in the three common forms:
-bool IsValidBinaryClassName(const char* s); // "java.lang.String"
-bool IsValidJniClassName(const char* s); // "java/lang/String"
-bool IsValidDescriptor(const char* s); // "Ljava/lang/String;"
-
-// Returns whether the given string is a valid field or method name,
-// additionally allowing names that begin with '<' and end with '>'.
-bool IsValidMemberName(const char* s);
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_
diff --git a/runtime/dex/dex_file-inl.h b/runtime/dex/dex_file-inl.h
deleted file mode 100644
index aa53daac35..0000000000
--- a/runtime/dex/dex_file-inl.h
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DEX_FILE_INL_H_
-#define ART_RUNTIME_DEX_DEX_FILE_INL_H_
-
-#include "base/bit_utils.h"
-#include "base/casts.h"
-#include "base/stringpiece.h"
-#include "compact_dex_file.h"
-#include "dex_file.h"
-#include "invoke_type.h"
-#include "leb128.h"
-#include "standard_dex_file.h"
-
-namespace art {
-
-inline int32_t DexFile::GetStringLength(const StringId& string_id) const {
- const uint8_t* ptr = DataBegin() + string_id.string_data_off_;
- return DecodeUnsignedLeb128(&ptr);
-}
-
-inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id,
- uint32_t* utf16_length) const {
- DCHECK(utf16_length != nullptr) << GetLocation();
- const uint8_t* ptr = DataBegin() + string_id.string_data_off_;
- *utf16_length = DecodeUnsignedLeb128(&ptr);
- return reinterpret_cast<const char*>(ptr);
-}
-
-inline const char* DexFile::GetStringData(const StringId& string_id) const {
- uint32_t ignored;
- return GetStringDataAndUtf16Length(string_id, &ignored);
-}
-
-inline const char* DexFile::StringDataAndUtf16LengthByIdx(dex::StringIndex idx,
- uint32_t* utf16_length) const {
- if (!idx.IsValid()) {
- *utf16_length = 0;
- return nullptr;
- }
- const StringId& string_id = GetStringId(idx);
- return GetStringDataAndUtf16Length(string_id, utf16_length);
-}
-
-inline const char* DexFile::StringDataByIdx(dex::StringIndex idx) const {
- uint32_t unicode_length;
- return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
-}
-
-inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const {
- if (!idx.IsValid()) {
- return nullptr;
- }
- const TypeId& type_id = GetTypeId(idx);
- return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
-}
-
-inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const {
- if (!idx.IsValid()) {
- return nullptr;
- }
- const TypeId& type_id = GetTypeId(idx);
- return StringDataByIdx(type_id.descriptor_idx_);
-}
-
-inline const char* DexFile::GetTypeDescriptor(const TypeId& type_id) const {
- return StringDataByIdx(type_id.descriptor_idx_);
-}
-
-inline const char* DexFile::GetFieldTypeDescriptor(const FieldId& field_id) const {
- const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_);
- return GetTypeDescriptor(type_id);
-}
-
-inline const char* DexFile::GetFieldName(const FieldId& field_id) const {
- return StringDataByIdx(field_id.name_idx_);
-}
-
-inline const char* DexFile::GetMethodDeclaringClassDescriptor(const MethodId& method_id) const {
- const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_);
- return GetTypeDescriptor(type_id);
-}
-
-inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const {
- return Signature(this, GetProtoId(method_id.proto_idx_));
-}
-
-inline const Signature DexFile::GetProtoSignature(const ProtoId& proto_id) const {
- return Signature(this, proto_id);
-}
-
-inline const char* DexFile::GetMethodName(const MethodId& method_id) const {
- return StringDataByIdx(method_id.name_idx_);
-}
-
-inline const char* DexFile::GetMethodShorty(uint32_t idx) const {
- return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_);
-}
-
-inline const char* DexFile::GetMethodShorty(const MethodId& method_id) const {
- return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_);
-}
-
-inline const char* DexFile::GetMethodShorty(const MethodId& method_id, uint32_t* length) const {
- // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
- return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
-}
-
-inline const char* DexFile::GetClassDescriptor(const ClassDef& class_def) const {
- return StringByTypeIdx(class_def.class_idx_);
-}
-
-inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) const {
- return StringByTypeIdx(proto_id.return_type_idx_);
-}
-
-inline const char* DexFile::GetShorty(uint32_t proto_idx) const {
- const ProtoId& proto_id = GetProtoId(proto_idx);
- return StringDataByIdx(proto_id.shorty_idx_);
-}
-
-inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end,
- uint32_t offset) {
- return reinterpret_cast<const TryItem*>
- (RoundUp(reinterpret_cast<uintptr_t>(&code_item_end.Inst()), TryItem::kAlignment)) + offset;
-}
-
-static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1,
- const DexFile* df2, dex::StringIndex sidx2) {
- uint32_t s1_len; // Note: utf16 length != mutf8 length.
- const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len);
- uint32_t s2_len;
- const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len);
- return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0);
-}
-
-inline bool Signature::operator==(const Signature& rhs) const {
- if (dex_file_ == nullptr) {
- return rhs.dex_file_ == nullptr;
- }
- if (rhs.dex_file_ == nullptr) {
- return false;
- }
- if (dex_file_ == rhs.dex_file_) {
- return proto_id_ == rhs.proto_id_;
- }
- uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length.
- const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_,
- &lhs_shorty_len);
- StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len);
- {
- uint32_t rhs_shorty_len;
- const char* rhs_shorty_data =
- rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_,
- &rhs_shorty_len);
- StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len);
- if (lhs_shorty != rhs_shorty) {
- return false; // Shorty mismatch.
- }
- }
- if (lhs_shorty[0] == 'L') {
- const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_);
- const DexFile::TypeId& rhs_return_type_id =
- rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_);
- if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_,
- rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) {
- return false; // Return type mismatch.
- }
- }
- if (lhs_shorty.find('L', 1) != StringPiece::npos) {
- const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
- const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
- // We found a reference parameter in the matching shorty, so both lists must be non-empty.
- DCHECK(params != nullptr);
- DCHECK(rhs_params != nullptr);
- uint32_t params_size = params->Size();
- DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
- for (uint32_t i = 0; i < params_size; ++i) {
- const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_);
- const DexFile::TypeId& rhs_param_id =
- rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_);
- if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_,
- rhs.dex_file_, rhs_param_id.descriptor_idx_)) {
- return false; // Parameter type mismatch.
- }
- }
- }
- return true;
-}
-
-inline
-InvokeType ClassDataItemIterator::GetMethodInvokeType(const DexFile::ClassDef& class_def) const {
- if (HasNextDirectMethod()) {
- if ((GetRawMemberAccessFlags() & kAccStatic) != 0) {
- return kStatic;
- } else {
- return kDirect;
- }
- } else {
- DCHECK_EQ(GetRawMemberAccessFlags() & kAccStatic, 0U);
- if ((class_def.access_flags_ & kAccInterface) != 0) {
- return kInterface;
- } else if ((GetRawMemberAccessFlags() & kAccConstructor) != 0) {
- return kSuper;
- } else {
- return kVirtual;
- }
- }
-}
-
-template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
-bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream,
- const std::string& location,
- const char* declaring_class_descriptor,
- const std::vector<const char*>& arg_descriptors,
- const std::string& method_name,
- bool is_static,
- uint16_t registers_size,
- uint16_t ins_size,
- uint16_t insns_size_in_code_units,
- IndexToStringData index_to_string_data,
- TypeIndexToStringData type_index_to_string_data,
- NewLocalCallback new_local_callback,
- void* context) {
- if (stream == nullptr) {
- return false;
- }
- std::vector<LocalInfo> local_in_reg(registers_size);
-
- uint16_t arg_reg = registers_size - ins_size;
- if (!is_static) {
- const char* descriptor = declaring_class_descriptor;
- local_in_reg[arg_reg].name_ = "this";
- local_in_reg[arg_reg].descriptor_ = descriptor;
- local_in_reg[arg_reg].signature_ = nullptr;
- local_in_reg[arg_reg].start_address_ = 0;
- local_in_reg[arg_reg].reg_ = arg_reg;
- local_in_reg[arg_reg].is_live_ = true;
- arg_reg++;
- }
-
- DecodeUnsignedLeb128(&stream); // Line.
- uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
- uint32_t i;
- if (parameters_size != arg_descriptors.size()) {
- LOG(ERROR) << "invalid stream - problem with parameter iterator in " << location
- << " for method " << method_name;
- return false;
- }
- for (i = 0; i < parameters_size && i < arg_descriptors.size(); ++i) {
- if (arg_reg >= registers_size) {
- LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg
- << " >= " << registers_size << ") in " << location;
- return false;
- }
- uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
- const char* descriptor = arg_descriptors[i];
- local_in_reg[arg_reg].name_ = index_to_string_data(name_idx);
- local_in_reg[arg_reg].descriptor_ = descriptor;
- local_in_reg[arg_reg].signature_ = nullptr;
- local_in_reg[arg_reg].start_address_ = 0;
- local_in_reg[arg_reg].reg_ = arg_reg;
- local_in_reg[arg_reg].is_live_ = true;
- switch (*descriptor) {
- case 'D':
- case 'J':
- arg_reg += 2;
- break;
- default:
- arg_reg += 1;
- break;
- }
- }
-
- uint32_t address = 0;
- for (;;) {
- uint8_t opcode = *stream++;
- switch (opcode) {
- case DBG_END_SEQUENCE:
- // Emit all variables which are still alive at the end of the method.
- for (uint16_t reg = 0; reg < registers_size; reg++) {
- if (local_in_reg[reg].is_live_) {
- local_in_reg[reg].end_address_ = insns_size_in_code_units;
- new_local_callback(context, local_in_reg[reg]);
- }
- }
- return true;
- case DBG_ADVANCE_PC:
- address += DecodeUnsignedLeb128(&stream);
- break;
- case DBG_ADVANCE_LINE:
- DecodeSignedLeb128(&stream); // Line.
- break;
- case DBG_START_LOCAL:
- case DBG_START_LOCAL_EXTENDED: {
- uint16_t reg = DecodeUnsignedLeb128(&stream);
- if (reg >= registers_size) {
- LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
- << registers_size << ") in " << location;
- return false;
- }
-
- uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
- uint16_t descriptor_idx = DecodeUnsignedLeb128P1(&stream);
- uint32_t signature_idx = dex::kDexNoIndex;
- if (opcode == DBG_START_LOCAL_EXTENDED) {
- signature_idx = DecodeUnsignedLeb128P1(&stream);
- }
-
- // Emit what was previously there, if anything
- if (local_in_reg[reg].is_live_) {
- local_in_reg[reg].end_address_ = address;
- new_local_callback(context, local_in_reg[reg]);
- }
-
- local_in_reg[reg].name_ = index_to_string_data(name_idx);
- local_in_reg[reg].descriptor_ = type_index_to_string_data(descriptor_idx);;
- local_in_reg[reg].signature_ = index_to_string_data(signature_idx);
- local_in_reg[reg].start_address_ = address;
- local_in_reg[reg].reg_ = reg;
- local_in_reg[reg].is_live_ = true;
- break;
- }
- case DBG_END_LOCAL: {
- uint16_t reg = DecodeUnsignedLeb128(&stream);
- if (reg >= registers_size) {
- LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
- << registers_size << ") in " << location;
- return false;
- }
- // If the register is live, close it properly. Otherwise, closing an already
- // closed register is sloppy, but harmless if no further action is taken.
- if (local_in_reg[reg].is_live_) {
- local_in_reg[reg].end_address_ = address;
- new_local_callback(context, local_in_reg[reg]);
- local_in_reg[reg].is_live_ = false;
- }
- break;
- }
- case DBG_RESTART_LOCAL: {
- uint16_t reg = DecodeUnsignedLeb128(&stream);
- if (reg >= registers_size) {
- LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
- << registers_size << ") in " << location;
- return false;
- }
- // If the register is live, the "restart" is superfluous,
- // and we don't want to mess with the existing start address.
- if (!local_in_reg[reg].is_live_) {
- local_in_reg[reg].start_address_ = address;
- local_in_reg[reg].is_live_ = true;
- }
- break;
- }
- case DBG_SET_PROLOGUE_END:
- case DBG_SET_EPILOGUE_BEGIN:
- break;
- case DBG_SET_FILE:
- DecodeUnsignedLeb128P1(&stream); // name.
- break;
- default:
- address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE;
- break;
- }
- }
-}
-
-template<typename NewLocalCallback>
-bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size,
- uint32_t ins_size,
- uint32_t insns_size_in_code_units,
- uint32_t debug_info_offset,
- bool is_static,
- uint32_t method_idx,
- NewLocalCallback new_local_callback,
- void* context) const {
- const uint8_t* const stream = GetDebugInfoStream(debug_info_offset);
- if (stream == nullptr) {
- return false;
- }
- std::vector<const char*> arg_descriptors;
- DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx)));
- for (; it.HasNext(); it.Next()) {
- arg_descriptors.push_back(it.GetDescriptor());
- }
- return DecodeDebugLocalInfo(stream,
- GetLocation(),
- GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)),
- arg_descriptors,
- this->PrettyMethod(method_idx),
- is_static,
- registers_size,
- ins_size,
- insns_size_in_code_units,
- [this](uint32_t idx) {
- return StringDataByIdx(dex::StringIndex(idx));
- },
- [this](uint32_t idx) {
- return StringByTypeIdx(dex::TypeIndex(
- dchecked_integral_cast<uint16_t>(idx)));
- },
- new_local_callback,
- context);
-}
-
-template<typename DexDebugNewPosition, typename IndexToStringData>
-bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream,
- IndexToStringData index_to_string_data,
- DexDebugNewPosition position_functor,
- void* context) {
- if (stream == nullptr) {
- return false;
- }
-
- PositionInfo entry = PositionInfo();
- entry.line_ = DecodeUnsignedLeb128(&stream);
- uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
- for (uint32_t i = 0; i < parameters_size; ++i) {
- DecodeUnsignedLeb128P1(&stream); // Parameter name.
- }
-
- for (;;) {
- uint8_t opcode = *stream++;
- switch (opcode) {
- case DBG_END_SEQUENCE:
- return true; // end of stream.
- case DBG_ADVANCE_PC:
- entry.address_ += DecodeUnsignedLeb128(&stream);
- break;
- case DBG_ADVANCE_LINE:
- entry.line_ += DecodeSignedLeb128(&stream);
- break;
- case DBG_START_LOCAL:
- DecodeUnsignedLeb128(&stream); // reg.
- DecodeUnsignedLeb128P1(&stream); // name.
- DecodeUnsignedLeb128P1(&stream); // descriptor.
- break;
- case DBG_START_LOCAL_EXTENDED:
- DecodeUnsignedLeb128(&stream); // reg.
- DecodeUnsignedLeb128P1(&stream); // name.
- DecodeUnsignedLeb128P1(&stream); // descriptor.
- DecodeUnsignedLeb128P1(&stream); // signature.
- break;
- case DBG_END_LOCAL:
- case DBG_RESTART_LOCAL:
- DecodeUnsignedLeb128(&stream); // reg.
- break;
- case DBG_SET_PROLOGUE_END:
- entry.prologue_end_ = true;
- break;
- case DBG_SET_EPILOGUE_BEGIN:
- entry.epilogue_begin_ = true;
- break;
- case DBG_SET_FILE: {
- uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
- entry.source_file_ = index_to_string_data(name_idx);
- break;
- }
- default: {
- int adjopcode = opcode - DBG_FIRST_SPECIAL;
- entry.address_ += adjopcode / DBG_LINE_RANGE;
- entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
- if (position_functor(context, entry)) {
- return true; // early exit.
- }
- entry.prologue_end_ = false;
- entry.epilogue_begin_ = false;
- break;
- }
- }
- }
-}
-
-template<typename DexDebugNewPosition>
-bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset,
- DexDebugNewPosition position_functor,
- void* context) const {
- return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset),
- [this](uint32_t idx) {
- return StringDataByIdx(dex::StringIndex(idx));
- },
- position_functor,
- context);
-}
-
-inline const CompactDexFile* DexFile::AsCompactDexFile() const {
- DCHECK(IsCompactDexFile());
- return down_cast<const CompactDexFile*>(this);
-}
-
-inline const StandardDexFile* DexFile::AsStandardDexFile() const {
- DCHECK(IsStandardDexFile());
- return down_cast<const StandardDexFile*>(this);
-}
-
-// Get the base of the encoded data for the given DexCode.
-inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& code_item_end,
- uint32_t tries_size,
- uint32_t offset) {
- const uint8_t* handler_data =
- reinterpret_cast<const uint8_t*>(GetTryItems(code_item_end, tries_size));
- return handler_data + offset;
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_INL_H_
diff --git a/runtime/dex/dex_file.cc b/runtime/dex/dex_file.cc
deleted file mode 100644
index 18eb903551..0000000000
--- a/runtime/dex/dex_file.cc
+++ /dev/null
@@ -1,795 +0,0 @@
-/*
- * Copyright (C) 2011 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 "dex_file.h"
-
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <zlib.h>
-
-#include <memory>
-#include <sstream>
-#include <type_traits>
-
-#include "android-base/stringprintf.h"
-
-#include "base/enums.h"
-#include "base/stl_util.h"
-#include "descriptors_names.h"
-#include "dex_file-inl.h"
-#include "leb128.h"
-#include "standard_dex_file.h"
-#include "utf-inl.h"
-
-namespace art {
-
-using android::base::StringPrintf;
-
-static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong");
-static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial");
-static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
-static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial");
-
-uint32_t DexFile::CalculateChecksum() const {
- return CalculateChecksum(Begin(), Size());
-}
-
-uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) {
- const uint32_t non_sum_bytes = OFFSETOF_MEMBER(DexFile::Header, signature_);
- return ChecksumMemoryRange(begin + non_sum_bytes, size - non_sum_bytes);
-}
-
-uint32_t DexFile::ChecksumMemoryRange(const uint8_t* begin, size_t size) {
- return adler32(adler32(0L, Z_NULL, 0), begin, size);
-}
-
-int DexFile::GetPermissions() const {
- CHECK(container_.get() != nullptr);
- return container_->GetPermissions();
-}
-
-bool DexFile::IsReadOnly() const {
- CHECK(container_.get() != nullptr);
- return container_->IsReadOnly();
-}
-
-bool DexFile::EnableWrite() const {
- CHECK(container_.get() != nullptr);
- return container_->EnableWrite();
-}
-
-bool DexFile::DisableWrite() const {
- CHECK(container_.get() != nullptr);
- return container_->DisableWrite();
-}
-
-DexFile::DexFile(const uint8_t* base,
- size_t size,
- const uint8_t* data_begin,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- DexFileContainer* container,
- bool is_compact_dex)
- : begin_(base),
- size_(size),
- data_begin_(data_begin),
- data_size_(data_size),
- location_(location),
- location_checksum_(location_checksum),
- header_(reinterpret_cast<const Header*>(base)),
- string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
- type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
- field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
- 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),
- container_(container),
- is_compact_dex_(is_compact_dex) {
- 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() {
- // We don't call DeleteGlobalRef on dex_object_ because we're only called by DestroyJavaVM, and
- // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
- // re-attach, but cleaning up these global references is not obviously useful. It's not as if
- // the global reference table is otherwise empty!
-}
-
-bool DexFile::Init(std::string* error_msg) {
- if (!CheckMagicAndVersion(error_msg)) {
- return false;
- }
- return true;
-}
-
-bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
- if (!IsMagicValid()) {
- std::ostringstream oss;
- oss << "Unrecognized magic number in " << GetLocation() << ":"
- << " " << header_->magic_[0]
- << " " << header_->magic_[1]
- << " " << header_->magic_[2]
- << " " << header_->magic_[3];
- *error_msg = oss.str();
- return false;
- }
- if (!IsVersionValid()) {
- std::ostringstream oss;
- oss << "Unrecognized version number in " << GetLocation() << ":"
- << " " << header_->magic_[4]
- << " " << header_->magic_[5]
- << " " << header_->magic_[6]
- << " " << header_->magic_[7];
- *error_msg = oss.str();
- return false;
- }
- return true;
-}
-
-void DexFile::InitializeSectionsFromMapList() {
- const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
- if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) {
- // Bad offset. The dex file verifier runs after this method and will reject the file.
- return;
- }
- 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 > DataSize()) {
- // 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_;
- }
- }
-}
-
-uint32_t DexFile::Header::GetVersion() const {
- const char* version = reinterpret_cast<const char*>(&magic_[kDexMagicSize]);
- return atoi(version);
-}
-
-const DexFile::ClassDef* DexFile::FindClassDef(dex::TypeIndex type_idx) const {
- size_t num_class_defs = NumClassDefs();
- // Fast path for rare no class defs case.
- if (num_class_defs == 0) {
- return nullptr;
- }
- for (size_t i = 0; i < num_class_defs; ++i) {
- const ClassDef& class_def = GetClassDef(i);
- if (class_def.class_idx_ == type_idx) {
- return &class_def;
- }
- }
- return nullptr;
-}
-
-uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def,
- uint32_t method_idx) const {
- const uint8_t* class_data = GetClassData(class_def);
- CHECK(class_data != nullptr);
- ClassDataItemIterator it(*this, class_data);
- it.SkipAllFields();
- while (it.HasNextDirectMethod()) {
- if (it.GetMemberIndex() == method_idx) {
- return it.GetMethodCodeItemOffset();
- }
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- if (it.GetMemberIndex() == method_idx) {
- return it.GetMethodCodeItemOffset();
- }
- it.Next();
- }
- LOG(FATAL) << "Unable to find method " << method_idx;
- UNREACHABLE();
-}
-
-const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass,
- const DexFile::StringId& name,
- const DexFile::TypeId& type) const {
- // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
- const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
- const dex::StringIndex name_idx = GetIndexForStringId(name);
- const dex::TypeIndex type_idx = GetIndexForTypeId(type);
- int32_t lo = 0;
- int32_t hi = NumFieldIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const DexFile::FieldId& field = GetFieldId(mid);
- if (class_idx > field.class_idx_) {
- lo = mid + 1;
- } else if (class_idx < field.class_idx_) {
- hi = mid - 1;
- } else {
- if (name_idx > field.name_idx_) {
- lo = mid + 1;
- } else if (name_idx < field.name_idx_) {
- hi = mid - 1;
- } else {
- if (type_idx > field.type_idx_) {
- lo = mid + 1;
- } else if (type_idx < field.type_idx_) {
- hi = mid - 1;
- } else {
- return &field;
- }
- }
- }
- }
- return nullptr;
-}
-
-const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_klass,
- const DexFile::StringId& name,
- const DexFile::ProtoId& signature) const {
- // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
- const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
- const dex::StringIndex name_idx = GetIndexForStringId(name);
- const uint16_t proto_idx = GetIndexForProtoId(signature);
- int32_t lo = 0;
- int32_t hi = NumMethodIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const DexFile::MethodId& method = GetMethodId(mid);
- if (class_idx > method.class_idx_) {
- lo = mid + 1;
- } else if (class_idx < method.class_idx_) {
- hi = mid - 1;
- } else {
- if (name_idx > method.name_idx_) {
- lo = mid + 1;
- } else if (name_idx < method.name_idx_) {
- hi = mid - 1;
- } else {
- if (proto_idx > method.proto_idx_) {
- lo = mid + 1;
- } else if (proto_idx < method.proto_idx_) {
- hi = mid - 1;
- } else {
- return &method;
- }
- }
- }
- }
- return nullptr;
-}
-
-const DexFile::StringId* DexFile::FindStringId(const char* string) const {
- int32_t lo = 0;
- int32_t hi = NumStringIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid));
- const char* str = GetStringData(str_id);
- int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
- if (compare > 0) {
- lo = mid + 1;
- } else if (compare < 0) {
- hi = mid - 1;
- } else {
- return &str_id;
- }
- }
- return nullptr;
-}
-
-const DexFile::TypeId* DexFile::FindTypeId(const char* string) const {
- int32_t lo = 0;
- int32_t hi = NumTypeIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const TypeId& type_id = GetTypeId(dex::TypeIndex(mid));
- const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_);
- const char* str = GetStringData(str_id);
- int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
- if (compare > 0) {
- lo = mid + 1;
- } else if (compare < 0) {
- hi = mid - 1;
- } else {
- return &type_id;
- }
- }
- return nullptr;
-}
-
-const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const {
- int32_t lo = 0;
- int32_t hi = NumStringIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid));
- const char* str = GetStringData(str_id);
- int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length);
- if (compare > 0) {
- lo = mid + 1;
- } else if (compare < 0) {
- hi = mid - 1;
- } else {
- return &str_id;
- }
- }
- return nullptr;
-}
-
-const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const {
- int32_t lo = 0;
- int32_t hi = NumTypeIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const TypeId& type_id = GetTypeId(dex::TypeIndex(mid));
- if (string_idx > type_id.descriptor_idx_) {
- lo = mid + 1;
- } else if (string_idx < type_id.descriptor_idx_) {
- hi = mid - 1;
- } else {
- return &type_id;
- }
- }
- return nullptr;
-}
-
-const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx,
- const dex::TypeIndex* signature_type_idxs,
- uint32_t signature_length) const {
- int32_t lo = 0;
- int32_t hi = NumProtoIds() - 1;
- while (hi >= lo) {
- int32_t mid = (hi + lo) / 2;
- const DexFile::ProtoId& proto = GetProtoId(mid);
- int compare = return_type_idx.index_ - proto.return_type_idx_.index_;
- if (compare == 0) {
- DexFileParameterIterator it(*this, proto);
- size_t i = 0;
- while (it.HasNext() && i < signature_length && compare == 0) {
- compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_;
- it.Next();
- i++;
- }
- if (compare == 0) {
- if (it.HasNext()) {
- compare = -1;
- } else if (i < signature_length) {
- compare = 1;
- }
- }
- }
- if (compare > 0) {
- lo = mid + 1;
- } else if (compare < 0) {
- hi = mid - 1;
- } else {
- return &proto;
- }
- }
- return nullptr;
-}
-
-// Given a signature place the type ids into the given vector
-bool DexFile::CreateTypeList(const StringPiece& signature,
- dex::TypeIndex* return_type_idx,
- std::vector<dex::TypeIndex>* param_type_idxs) const {
- if (signature[0] != '(') {
- return false;
- }
- size_t offset = 1;
- size_t end = signature.size();
- bool process_return = false;
- while (offset < end) {
- size_t start_offset = offset;
- char c = signature[offset];
- offset++;
- if (c == ')') {
- process_return = true;
- continue;
- }
- while (c == '[') { // process array prefix
- if (offset >= end) { // expect some descriptor following [
- return false;
- }
- c = signature[offset];
- offset++;
- }
- if (c == 'L') { // process type descriptors
- do {
- if (offset >= end) { // unexpected early termination of descriptor
- return false;
- }
- c = signature[offset];
- offset++;
- } while (c != ';');
- }
- // TODO: avoid creating a std::string just to get a 0-terminated char array
- std::string descriptor(signature.data() + start_offset, offset - start_offset);
- const DexFile::TypeId* type_id = FindTypeId(descriptor.c_str());
- if (type_id == nullptr) {
- return false;
- }
- dex::TypeIndex type_idx = GetIndexForTypeId(*type_id);
- if (!process_return) {
- param_type_idxs->push_back(type_idx);
- } else {
- *return_type_idx = type_idx;
- return offset == end; // return true if the signature had reached a sensible end
- }
- }
- return false; // failed to correctly parse return type
-}
-
-const Signature DexFile::CreateSignature(const StringPiece& signature) const {
- dex::TypeIndex return_type_idx;
- std::vector<dex::TypeIndex> param_type_indices;
- bool success = CreateTypeList(signature, &return_type_idx, &param_type_indices);
- if (!success) {
- return Signature::NoSignature();
- }
- const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices);
- if (proto_id == nullptr) {
- return Signature::NoSignature();
- }
- return Signature(this, *proto_id);
-}
-
-int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) {
- uint32_t min = 0;
- uint32_t max = tries_size;
- while (min < max) {
- const uint32_t mid = (min + max) / 2;
-
- const art::DexFile::TryItem& ti = try_items[mid];
- const uint32_t start = ti.start_addr_;
- const uint32_t end = start + ti.insn_count_;
-
- if (address < start) {
- max = mid;
- } else if (address >= end) {
- min = mid + 1;
- } else { // We have a winner!
- return mid;
- }
- }
- // No match.
- return -1;
-}
-
-bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) {
- LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context);
-
- // We know that this callback will be called in
- // ascending address order, so keep going until we find
- // a match or we've just gone past it.
- if (entry.address_ > context->address_) {
- // The line number from the previous positions callback
- // wil be the final result.
- return true;
- } else {
- context->line_num_ = entry.line_;
- return entry.address_ == context->address_;
- }
-}
-
-// Read a signed integer. "zwidth" is the zero-based byte count.
-int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) {
- int32_t val = 0;
- for (int i = zwidth; i >= 0; --i) {
- val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24);
- }
- val >>= (3 - zwidth) * 8;
- return val;
-}
-
-// Read an unsigned integer. "zwidth" is the zero-based byte count,
-// "fill_on_right" indicates which side we want to zero-fill from.
-uint32_t DexFile::ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) {
- uint32_t val = 0;
- for (int i = zwidth; i >= 0; --i) {
- val = (val >> 8) | (((uint32_t)*ptr++) << 24);
- }
- if (!fill_on_right) {
- val >>= (3 - zwidth) * 8;
- }
- return val;
-}
-
-// Read a signed long. "zwidth" is the zero-based byte count.
-int64_t DexFile::ReadSignedLong(const uint8_t* ptr, int zwidth) {
- int64_t val = 0;
- for (int i = zwidth; i >= 0; --i) {
- val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56);
- }
- val >>= (7 - zwidth) * 8;
- return val;
-}
-
-// Read an unsigned long. "zwidth" is the zero-based byte count,
-// "fill_on_right" indicates which side we want to zero-fill from.
-uint64_t DexFile::ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) {
- uint64_t val = 0;
- for (int i = zwidth; i >= 0; --i) {
- val = (val >> 8) | (((uint64_t)*ptr++) << 56);
- }
- if (!fill_on_right) {
- val >>= (7 - zwidth) * 8;
- }
- return val;
-}
-
-std::string DexFile::PrettyMethod(uint32_t method_idx, bool with_signature) const {
- if (method_idx >= NumMethodIds()) {
- return StringPrintf("<<invalid-method-idx-%d>>", method_idx);
- }
- const DexFile::MethodId& method_id = GetMethodId(method_idx);
- std::string result;
- const DexFile::ProtoId* proto_id = with_signature ? &GetProtoId(method_id.proto_idx_) : nullptr;
- if (with_signature) {
- AppendPrettyDescriptor(StringByTypeIdx(proto_id->return_type_idx_), &result);
- result += ' ';
- }
- AppendPrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id), &result);
- result += '.';
- result += GetMethodName(method_id);
- if (with_signature) {
- result += '(';
- const DexFile::TypeList* params = GetProtoParameters(*proto_id);
- if (params != nullptr) {
- const char* separator = "";
- for (uint32_t i = 0u, size = params->Size(); i != size; ++i) {
- result += separator;
- separator = ", ";
- AppendPrettyDescriptor(StringByTypeIdx(params->GetTypeItem(i).type_idx_), &result);
- }
- }
- result += ')';
- }
- return result;
-}
-
-std::string DexFile::PrettyField(uint32_t field_idx, bool with_type) const {
- if (field_idx >= NumFieldIds()) {
- return StringPrintf("<<invalid-field-idx-%d>>", field_idx);
- }
- const DexFile::FieldId& field_id = GetFieldId(field_idx);
- std::string result;
- if (with_type) {
- result += GetFieldTypeDescriptor(field_id);
- result += ' ';
- }
- AppendPrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id), &result);
- result += '.';
- result += GetFieldName(field_id);
- return result;
-}
-
-std::string DexFile::PrettyType(dex::TypeIndex type_idx) const {
- if (type_idx.index_ >= NumTypeIds()) {
- return StringPrintf("<<invalid-type-idx-%d>>", type_idx.index_);
- }
- const DexFile::TypeId& type_id = GetTypeId(type_idx);
- return PrettyDescriptor(GetTypeDescriptor(type_id));
-}
-
-// Checks that visibility is as expected. Includes special behavior for M and
-// before to allow runtime and build visibility when expecting runtime.
-std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
- os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]",
- dex_file.GetLocation().c_str(),
- dex_file.GetHeader().checksum_, dex_file.GetLocationChecksum(),
- dex_file.Begin(), dex_file.Begin() + dex_file.Size());
- return os;
-}
-
-std::string Signature::ToString() const {
- if (dex_file_ == nullptr) {
- CHECK(proto_id_ == nullptr);
- return "<no signature>";
- }
- const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
- std::string result;
- if (params == nullptr) {
- result += "()";
- } else {
- result += "(";
- for (uint32_t i = 0; i < params->Size(); ++i) {
- result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_);
- }
- result += ")";
- }
- result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_);
- return result;
-}
-
-uint32_t Signature::GetNumberOfParameters() const {
- const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
- return (params != nullptr) ? params->Size() : 0;
-}
-
-bool Signature::IsVoid() const {
- const char* return_type = dex_file_->GetReturnTypeDescriptor(*proto_id_);
- return strcmp(return_type, "V") == 0;
-}
-
-bool Signature::operator==(const StringPiece& rhs) const {
- if (dex_file_ == nullptr) {
- return false;
- }
- StringPiece tail(rhs);
- if (!tail.starts_with("(")) {
- return false; // Invalid signature
- }
- tail.remove_prefix(1); // "(";
- const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
- if (params != nullptr) {
- for (uint32_t i = 0; i < params->Size(); ++i) {
- StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_));
- if (!tail.starts_with(param)) {
- return false;
- }
- tail.remove_prefix(param.length());
- }
- }
- if (!tail.starts_with(")")) {
- return false;
- }
- tail.remove_prefix(1); // ")";
- return tail == dex_file_->StringByTypeIdx(proto_id_->return_type_idx_);
-}
-
-std::ostream& operator<<(std::ostream& os, const Signature& sig) {
- return os << sig.ToString();
-}
-
-// Decodes the header section from the class data bytes.
-void ClassDataItemIterator::ReadClassDataHeader() {
- CHECK(ptr_pos_ != nullptr);
- header_.static_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_);
- header_.instance_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_);
- header_.direct_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_);
- header_.virtual_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_);
-}
-
-void ClassDataItemIterator::ReadClassDataField() {
- field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
- field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
- // The user of the iterator is responsible for checking if there
- // are unordered or duplicate indexes.
-}
-
-void ClassDataItemIterator::ReadClassDataMethod() {
- method_.method_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
- method_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
- method_.code_off_ = DecodeUnsignedLeb128(&ptr_pos_);
- if (last_idx_ != 0 && method_.method_idx_delta_ == 0) {
- LOG(WARNING) << "Duplicate method in " << dex_file_.GetLocation();
- }
-}
-
-EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file,
- const uint8_t* array_data)
- : dex_file_(dex_file),
- array_size_(),
- pos_(-1),
- ptr_(array_data),
- type_(kByte) {
- array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0;
- if (array_size_ > 0) {
- Next();
- }
-}
-
-void EncodedArrayValueIterator::Next() {
- pos_++;
- if (pos_ >= array_size_) {
- return;
- }
- uint8_t value_type = *ptr_++;
- uint8_t value_arg = value_type >> kEncodedValueArgShift;
- size_t width = value_arg + 1; // assume and correct later
- type_ = static_cast<ValueType>(value_type & kEncodedValueTypeMask);
- switch (type_) {
- case kBoolean:
- jval_.i = (value_arg != 0) ? 1 : 0;
- width = 0;
- break;
- case kByte:
- jval_.i = DexFile::ReadSignedInt(ptr_, value_arg);
- CHECK(IsInt<8>(jval_.i));
- break;
- case kShort:
- jval_.i = DexFile::ReadSignedInt(ptr_, value_arg);
- CHECK(IsInt<16>(jval_.i));
- break;
- case kChar:
- jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false);
- CHECK(IsUint<16>(jval_.i));
- break;
- case kInt:
- jval_.i = DexFile::ReadSignedInt(ptr_, value_arg);
- break;
- case kLong:
- jval_.j = DexFile::ReadSignedLong(ptr_, value_arg);
- break;
- case kFloat:
- jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, true);
- break;
- case kDouble:
- jval_.j = DexFile::ReadUnsignedLong(ptr_, value_arg, true);
- break;
- case kString:
- case kType:
- case kMethodType:
- case kMethodHandle:
- jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false);
- break;
- case kField:
- case kMethod:
- case kEnum:
- case kArray:
- case kAnnotation:
- UNIMPLEMENTED(FATAL) << ": type " << type_;
- UNREACHABLE();
- case kNull:
- jval_.l = nullptr;
- width = 0;
- break;
- default:
- LOG(FATAL) << "Unreached";
- UNREACHABLE();
- }
- ptr_ += width;
-}
-
-namespace dex {
-
-std::ostream& operator<<(std::ostream& os, const StringIndex& index) {
- os << "StringIndex[" << index.index_ << "]";
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const TypeIndex& index) {
- os << "TypeIndex[" << index.index_ << "]";
- return os;
-}
-
-} // namespace dex
-
-} // namespace art
diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h
deleted file mode 100644
index cf8c840b59..0000000000
--- a/runtime/dex/dex_file.h
+++ /dev/null
@@ -1,1443 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DEX_FILE_H_
-#define ART_RUNTIME_DEX_DEX_FILE_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-
-#include "base/iteration_range.h"
-#include "base/macros.h"
-#include "base/value_object.h"
-#include "dex_file_types.h"
-#include "dex_instruction_iterator.h"
-#include "globals.h"
-#include "hidden_api_access_flags.h"
-#include "jni.h"
-#include "modifiers.h"
-
-namespace art {
-
-class CompactDexFile;
-enum InvokeType : uint32_t;
-class MemMap;
-class OatDexFile;
-class Signature;
-class StandardDexFile;
-class StringPiece;
-class ZipArchive;
-
-// Some instances of DexFile own the storage referred to by DexFile. Clients who create
-// such management do so by subclassing Container.
-class DexFileContainer {
- public:
- DexFileContainer() { }
- virtual ~DexFileContainer() { }
- virtual int GetPermissions() = 0;
- virtual bool IsReadOnly() = 0;
- virtual bool EnableWrite() = 0;
- virtual bool DisableWrite() = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DexFileContainer);
-};
-
-// Dex file is the API that exposes native dex files (ordinary dex files) and CompactDex.
-// Originally, the dex file format used by ART was mostly the same as APKs. The only change was
-// quickened opcodes and layout optimizations.
-// Since ART needs to support both native dex files and CompactDex files, the DexFile interface
-// provides an abstraction to facilitate this.
-class DexFile {
- public:
- // Number of bytes in the dex file magic.
- static constexpr size_t kDexMagicSize = 4;
- static constexpr size_t kDexVersionLen = 4;
-
- // First Dex format version enforcing class definition ordering rules.
- static const uint32_t kClassDefinitionOrderEnforcedVersion = 37;
-
- static constexpr size_t kSha1DigestSize = 20;
- static constexpr uint32_t kDexEndianConstant = 0x12345678;
-
- // The value of an invalid index.
- static const uint16_t kDexNoIndex16 = 0xFFFF;
-
- // Raw header_item.
- struct Header {
- uint8_t magic_[8] = {};
- uint32_t checksum_ = 0; // See also location_checksum_
- uint8_t signature_[kSha1DigestSize] = {};
- uint32_t file_size_ = 0; // size of entire file
- uint32_t header_size_ = 0; // offset to start of next section
- uint32_t endian_tag_ = 0;
- uint32_t link_size_ = 0; // unused
- uint32_t link_off_ = 0; // unused
- uint32_t map_off_ = 0; // unused
- uint32_t string_ids_size_ = 0; // number of StringIds
- uint32_t string_ids_off_ = 0; // file offset of StringIds array
- uint32_t type_ids_size_ = 0; // number of TypeIds, we don't support more than 65535
- uint32_t type_ids_off_ = 0; // file offset of TypeIds array
- uint32_t proto_ids_size_ = 0; // number of ProtoIds, we don't support more than 65535
- uint32_t proto_ids_off_ = 0; // file offset of ProtoIds array
- uint32_t field_ids_size_ = 0; // number of FieldIds
- uint32_t field_ids_off_ = 0; // file offset of FieldIds array
- uint32_t method_ids_size_ = 0; // number of MethodIds
- uint32_t method_ids_off_ = 0; // file offset of MethodIds array
- uint32_t class_defs_size_ = 0; // number of ClassDefs
- uint32_t class_defs_off_ = 0; // file offset of ClassDef array
- uint32_t data_size_ = 0; // size of data section
- uint32_t data_off_ = 0; // file offset of data section
-
- // Decode the dex magic version
- uint32_t GetVersion() const;
- };
-
- // Map item type codes.
- enum MapItemType : uint16_t { // private
- kDexTypeHeaderItem = 0x0000,
- kDexTypeStringIdItem = 0x0001,
- kDexTypeTypeIdItem = 0x0002,
- kDexTypeProtoIdItem = 0x0003,
- kDexTypeFieldIdItem = 0x0004,
- kDexTypeMethodIdItem = 0x0005,
- kDexTypeClassDefItem = 0x0006,
- kDexTypeCallSiteIdItem = 0x0007,
- kDexTypeMethodHandleItem = 0x0008,
- kDexTypeMapList = 0x1000,
- kDexTypeTypeList = 0x1001,
- kDexTypeAnnotationSetRefList = 0x1002,
- kDexTypeAnnotationSetItem = 0x1003,
- kDexTypeClassDataItem = 0x2000,
- kDexTypeCodeItem = 0x2001,
- kDexTypeStringDataItem = 0x2002,
- kDexTypeDebugInfoItem = 0x2003,
- kDexTypeAnnotationItem = 0x2004,
- kDexTypeEncodedArrayItem = 0x2005,
- kDexTypeAnnotationsDirectoryItem = 0x2006,
- };
-
- struct MapItem {
- uint16_t type_;
- uint16_t unused_;
- uint32_t size_;
- uint32_t offset_;
- };
-
- struct MapList {
- uint32_t size_;
- MapItem list_[1];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MapList);
- };
-
- // Raw string_id_item.
- struct StringId {
- uint32_t string_data_off_; // offset in bytes from the base address
-
- private:
- DISALLOW_COPY_AND_ASSIGN(StringId);
- };
-
- // Raw type_id_item.
- struct TypeId {
- dex::StringIndex descriptor_idx_; // index into string_ids
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TypeId);
- };
-
- // Raw field_id_item.
- struct FieldId {
- dex::TypeIndex class_idx_; // index into type_ids_ array for defining class
- dex::TypeIndex type_idx_; // index into type_ids_ array for field type
- dex::StringIndex name_idx_; // index into string_ids_ array for field name
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FieldId);
- };
-
- // Raw proto_id_item.
- struct ProtoId {
- dex::StringIndex shorty_idx_; // index into string_ids array for shorty descriptor
- dex::TypeIndex return_type_idx_; // index into type_ids array for return type
- uint16_t pad_; // padding = 0
- uint32_t parameters_off_; // file offset to type_list for parameter types
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ProtoId);
- };
-
- // Raw method_id_item.
- struct MethodId {
- dex::TypeIndex class_idx_; // index into type_ids_ array for defining class
- uint16_t proto_idx_; // index into proto_ids_ array for method prototype
- dex::StringIndex name_idx_; // index into string_ids_ array for method name
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MethodId);
- };
-
- // Raw class_def_item.
- struct ClassDef {
- dex::TypeIndex class_idx_; // index into type_ids_ array for this class
- uint16_t pad1_; // padding = 0
- uint32_t access_flags_;
- dex::TypeIndex superclass_idx_; // index into type_ids_ array for superclass
- uint16_t pad2_; // padding = 0
- uint32_t interfaces_off_; // file offset to TypeList
- dex::StringIndex source_file_idx_; // index into string_ids_ for source file name
- uint32_t annotations_off_; // file offset to annotations_directory_item
- uint32_t class_data_off_; // file offset to class_data_item
- uint32_t static_values_off_; // file offset to EncodedArray
-
- // Returns the valid access flags, that is, Java modifier bits relevant to the ClassDef type
- // (class or interface). These are all in the lower 16b and do not contain runtime flags.
- uint32_t GetJavaAccessFlags() const {
- // Make sure that none of our runtime-only flags are set.
- static_assert((kAccValidClassFlags & kAccJavaFlagsMask) == kAccValidClassFlags,
- "Valid class flags not a subset of Java flags");
- static_assert((kAccValidInterfaceFlags & kAccJavaFlagsMask) == kAccValidInterfaceFlags,
- "Valid interface flags not a subset of Java flags");
-
- if ((access_flags_ & kAccInterface) != 0) {
- // Interface.
- return access_flags_ & kAccValidInterfaceFlags;
- } else {
- // Class.
- return access_flags_ & kAccValidClassFlags;
- }
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ClassDef);
- };
-
- // Raw type_item.
- struct TypeItem {
- dex::TypeIndex type_idx_; // index into type_ids section
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TypeItem);
- };
-
- // Raw type_list.
- class TypeList {
- public:
- uint32_t Size() const {
- return size_;
- }
-
- const TypeItem& GetTypeItem(uint32_t idx) const {
- DCHECK_LT(idx, this->size_);
- return this->list_[idx];
- }
-
- // Size in bytes of the part of the list that is common.
- static constexpr size_t GetHeaderSize() {
- return 4U;
- }
-
- // Size in bytes of the whole type list including all the stored elements.
- static constexpr size_t GetListSize(size_t count) {
- return GetHeaderSize() + sizeof(TypeItem) * count;
- }
-
- private:
- uint32_t size_; // size of the list, in entries
- TypeItem list_[1]; // elements of the list
- 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.
- kInvokeDirect = 0x0007, // an invoker for a direct (special) method.
- kInvokeInterface = 0x0008, // an invoker for an interface method.
- kLast = kInvokeInterface
- };
-
- // 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);
- };
-
- // Base code_item, compact dex and standard dex have different code item layouts.
- struct CodeItem {
- protected:
- CodeItem() = default;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CodeItem);
- };
-
- // Raw try_item.
- struct TryItem {
- static constexpr size_t kAlignment = sizeof(uint32_t);
-
- uint32_t start_addr_;
- uint16_t insn_count_;
- uint16_t handler_off_;
-
- private:
- TryItem() = default;
- friend class DexWriter;
- DISALLOW_COPY_AND_ASSIGN(TryItem);
- };
-
- // Annotation constants.
- enum {
- kDexVisibilityBuild = 0x00, /* annotation visibility */
- kDexVisibilityRuntime = 0x01,
- kDexVisibilitySystem = 0x02,
-
- kDexAnnotationByte = 0x00,
- kDexAnnotationShort = 0x02,
- kDexAnnotationChar = 0x03,
- kDexAnnotationInt = 0x04,
- kDexAnnotationLong = 0x06,
- kDexAnnotationFloat = 0x10,
- kDexAnnotationDouble = 0x11,
- kDexAnnotationMethodType = 0x15,
- kDexAnnotationMethodHandle = 0x16,
- kDexAnnotationString = 0x17,
- kDexAnnotationType = 0x18,
- kDexAnnotationField = 0x19,
- kDexAnnotationMethod = 0x1a,
- kDexAnnotationEnum = 0x1b,
- kDexAnnotationArray = 0x1c,
- kDexAnnotationAnnotation = 0x1d,
- kDexAnnotationNull = 0x1e,
- kDexAnnotationBoolean = 0x1f,
-
- kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */
- kDexAnnotationValueArgShift = 5,
- };
-
- struct AnnotationsDirectoryItem {
- uint32_t class_annotations_off_;
- uint32_t fields_size_;
- uint32_t methods_size_;
- uint32_t parameters_size_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem);
- };
-
- struct FieldAnnotationsItem {
- uint32_t field_idx_;
- uint32_t annotations_off_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FieldAnnotationsItem);
- };
-
- struct MethodAnnotationsItem {
- uint32_t method_idx_;
- uint32_t annotations_off_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MethodAnnotationsItem);
- };
-
- struct ParameterAnnotationsItem {
- uint32_t method_idx_;
- uint32_t annotations_off_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ParameterAnnotationsItem);
- };
-
- struct AnnotationSetRefItem {
- uint32_t annotations_off_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefItem);
- };
-
- struct AnnotationSetRefList {
- uint32_t size_;
- AnnotationSetRefItem list_[1];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefList);
- };
-
- struct AnnotationSetItem {
- uint32_t size_;
- uint32_t entries_[1];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AnnotationSetItem);
- };
-
- struct AnnotationItem {
- uint8_t visibility_;
- uint8_t annotation_[1];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
- };
-
- enum AnnotationResultStyle { // private
- kAllObjects,
- kPrimitivesOrObjects,
- kAllRaw
- };
-
- struct AnnotationValue;
-
- // Closes a .dex file.
- virtual ~DexFile();
-
- const std::string& GetLocation() const {
- return location_;
- }
-
- // For DexFiles directly from .dex files, this is the checksum from the DexFile::Header.
- // For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex.
- uint32_t GetLocationChecksum() const {
- return location_checksum_;
- }
-
- const Header& GetHeader() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return *header_;
- }
-
- // Decode the dex magic version
- uint32_t GetDexVersion() const {
- return GetHeader().GetVersion();
- }
-
- // Returns true if the byte string points to the magic value.
- virtual bool IsMagicValid() const = 0;
-
- // Returns true if the byte string after the magic is the correct value.
- virtual bool IsVersionValid() const = 0;
-
- // Returns true if the dex file supports default methods.
- virtual bool SupportsDefaultMethods() const = 0;
-
- // Returns the maximum size in bytes needed to store an equivalent dex file strictly conforming to
- // the dex file specification. That is the size if we wanted to get rid of all the
- // quickening/compact-dexing/etc.
- //
- // TODO This should really be an exact size! b/72402467
- virtual size_t GetDequickenedSize() const = 0;
-
- // Returns the number of string identifiers in the .dex file.
- size_t NumStringIds() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return header_->string_ids_size_;
- }
-
- // Returns the StringId at the specified index.
- const StringId& GetStringId(dex::StringIndex idx) const {
- DCHECK_LT(idx.index_, NumStringIds()) << GetLocation();
- return string_ids_[idx.index_];
- }
-
- dex::StringIndex GetIndexForStringId(const StringId& string_id) const {
- CHECK_GE(&string_id, string_ids_) << GetLocation();
- CHECK_LT(&string_id, string_ids_ + header_->string_ids_size_) << GetLocation();
- return dex::StringIndex(&string_id - string_ids_);
- }
-
- int32_t GetStringLength(const StringId& string_id) const;
-
- // Returns a pointer to the UTF-8 string data referred to by the given string_id as well as the
- // length of the string when decoded as a UTF-16 string. Note the UTF-16 length is not the same
- // as the string length of the string data.
- const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const;
-
- const char* GetStringData(const StringId& string_id) const;
-
- // Index version of GetStringDataAndUtf16Length.
- const char* StringDataAndUtf16LengthByIdx(dex::StringIndex idx, uint32_t* utf16_length) const;
-
- const char* StringDataByIdx(dex::StringIndex idx) const;
-
- // Looks up a string id for a given modified utf8 string.
- const StringId* FindStringId(const char* string) const;
-
- const TypeId* FindTypeId(const char* string) const;
-
- // Looks up a string id for a given utf16 string.
- const StringId* FindStringId(const uint16_t* string, size_t length) const;
-
- // Returns the number of type identifiers in the .dex file.
- uint32_t NumTypeIds() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return header_->type_ids_size_;
- }
-
- bool IsTypeIndexValid(dex::TypeIndex idx) const {
- return idx.IsValid() && idx.index_ < NumTypeIds();
- }
-
- // Returns the TypeId at the specified index.
- const TypeId& GetTypeId(dex::TypeIndex idx) const {
- DCHECK_LT(idx.index_, NumTypeIds()) << GetLocation();
- return type_ids_[idx.index_];
- }
-
- dex::TypeIndex GetIndexForTypeId(const TypeId& type_id) const {
- CHECK_GE(&type_id, type_ids_) << GetLocation();
- CHECK_LT(&type_id, type_ids_ + header_->type_ids_size_) << GetLocation();
- size_t result = &type_id - type_ids_;
- DCHECK_LT(result, 65536U) << GetLocation();
- return dex::TypeIndex(static_cast<uint16_t>(result));
- }
-
- // Get the descriptor string associated with a given type index.
- const char* StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const;
-
- const char* StringByTypeIdx(dex::TypeIndex idx) const;
-
- // Returns the type descriptor string of a type id.
- const char* GetTypeDescriptor(const TypeId& type_id) const;
-
- // Looks up a type for the given string index
- const TypeId* FindTypeId(dex::StringIndex string_idx) const;
-
- // Returns the number of field identifiers in the .dex file.
- size_t NumFieldIds() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return header_->field_ids_size_;
- }
-
- // Returns the FieldId at the specified index.
- const FieldId& GetFieldId(uint32_t idx) const {
- DCHECK_LT(idx, NumFieldIds()) << GetLocation();
- return field_ids_[idx];
- }
-
- uint32_t GetIndexForFieldId(const FieldId& field_id) const {
- CHECK_GE(&field_id, field_ids_) << GetLocation();
- CHECK_LT(&field_id, field_ids_ + header_->field_ids_size_) << GetLocation();
- return &field_id - field_ids_;
- }
-
- // Looks up a field by its declaring class, name and type
- const FieldId* FindFieldId(const DexFile::TypeId& declaring_klass,
- const DexFile::StringId& name,
- const DexFile::TypeId& type) const;
-
- uint32_t FindCodeItemOffset(const DexFile::ClassDef& class_def,
- uint32_t dex_method_idx) const;
-
- virtual uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item) const = 0;
-
- // Returns the declaring class descriptor string of a field id.
- const char* GetFieldDeclaringClassDescriptor(const FieldId& field_id) const {
- const DexFile::TypeId& type_id = GetTypeId(field_id.class_idx_);
- return GetTypeDescriptor(type_id);
- }
-
- // Returns the class descriptor string of a field id.
- const char* GetFieldTypeDescriptor(const FieldId& field_id) const;
-
- // Returns the name of a field id.
- const char* GetFieldName(const FieldId& field_id) const;
-
- // Returns the number of method identifiers in the .dex file.
- size_t NumMethodIds() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return header_->method_ids_size_;
- }
-
- // Returns the MethodId at the specified index.
- const MethodId& GetMethodId(uint32_t idx) const {
- DCHECK_LT(idx, NumMethodIds()) << GetLocation();
- return method_ids_[idx];
- }
-
- uint32_t GetIndexForMethodId(const MethodId& method_id) const {
- CHECK_GE(&method_id, method_ids_) << GetLocation();
- CHECK_LT(&method_id, method_ids_ + header_->method_ids_size_) << GetLocation();
- return &method_id - method_ids_;
- }
-
- // Looks up a method by its declaring class, name and proto_id
- const MethodId* FindMethodId(const DexFile::TypeId& declaring_klass,
- const DexFile::StringId& name,
- const DexFile::ProtoId& signature) const;
-
- // Returns the declaring class descriptor string of a method id.
- const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const;
-
- // Returns the prototype of a method id.
- const ProtoId& GetMethodPrototype(const MethodId& method_id) const {
- return GetProtoId(method_id.proto_idx_);
- }
-
- // Returns a representation of the signature of a method id.
- const Signature GetMethodSignature(const MethodId& method_id) const;
-
- // Returns a representation of the signature of a proto id.
- const Signature GetProtoSignature(const ProtoId& proto_id) const;
-
- // Returns the name of a method id.
- const char* GetMethodName(const MethodId& method_id) const;
-
- // Returns the shorty of a method by its index.
- const char* GetMethodShorty(uint32_t idx) const;
-
- // Returns the shorty of a method id.
- const char* GetMethodShorty(const MethodId& method_id) const;
- const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const;
-
- // Returns the number of class definitions in the .dex file.
- uint32_t NumClassDefs() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return header_->class_defs_size_;
- }
-
- // Returns the ClassDef at the specified index.
- const ClassDef& GetClassDef(uint16_t idx) const {
- DCHECK_LT(idx, NumClassDefs()) << GetLocation();
- return class_defs_[idx];
- }
-
- uint16_t GetIndexForClassDef(const ClassDef& class_def) const {
- CHECK_GE(&class_def, class_defs_) << GetLocation();
- CHECK_LT(&class_def, class_defs_ + header_->class_defs_size_) << GetLocation();
- return &class_def - class_defs_;
- }
-
- // Returns the class descriptor string of a class definition.
- const char* GetClassDescriptor(const ClassDef& class_def) const;
-
- // Looks up a class definition by its type index.
- const ClassDef* FindClassDef(dex::TypeIndex type_idx) const;
-
- const TypeList* GetInterfacesList(const ClassDef& class_def) const {
- return DataPointer<TypeList>(class_def.interfaces_off_);
- }
-
- 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 {
- return DataPointer<uint8_t>(class_def.class_data_off_);
- }
-
- // Return the code item for a provided offset.
- const CodeItem* GetCodeItem(const uint32_t code_off) const {
- // May be null for native or abstract methods.
- return DataPointer<CodeItem>(code_off);
- }
-
- const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const;
-
- // Returns the number of prototype identifiers in the .dex file.
- size_t NumProtoIds() const {
- DCHECK(header_ != nullptr) << GetLocation();
- return header_->proto_ids_size_;
- }
-
- // Returns the ProtoId at the specified index.
- const ProtoId& GetProtoId(uint16_t idx) const {
- DCHECK_LT(idx, NumProtoIds()) << GetLocation();
- return proto_ids_[idx];
- }
-
- uint16_t GetIndexForProtoId(const ProtoId& proto_id) const {
- CHECK_GE(&proto_id, proto_ids_) << GetLocation();
- CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation();
- return &proto_id - proto_ids_;
- }
-
- // Looks up a proto id for a given return type and signature type list
- const ProtoId* FindProtoId(dex::TypeIndex return_type_idx,
- const dex::TypeIndex* signature_type_idxs,
- uint32_t signature_length) const;
- const ProtoId* FindProtoId(dex::TypeIndex return_type_idx,
- const std::vector<dex::TypeIndex>& signature_type_idxs) const {
- return FindProtoId(return_type_idx, &signature_type_idxs[0], signature_type_idxs.size());
- }
-
- // Given a signature place the type ids into the given vector, returns true on success
- bool CreateTypeList(const StringPiece& signature,
- dex::TypeIndex* return_type_idx,
- std::vector<dex::TypeIndex>* param_type_idxs) const;
-
- // Create a Signature from the given string signature or return Signature::NoSignature if not
- // possible.
- const Signature CreateSignature(const StringPiece& signature) const;
-
- // Returns the short form method descriptor for the given prototype.
- const char* GetShorty(uint32_t proto_idx) const;
-
- const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
- return DataPointer<TypeList>(proto_id.parameters_off_);
- }
-
- const uint8_t* GetEncodedStaticFieldValuesArray(const ClassDef& class_def) const {
- return DataPointer<uint8_t>(class_def.static_values_off_);
- }
-
- const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const {
- return DataBegin() + call_site_id.data_off_;
- }
-
- static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset);
-
- // Get the base of the encoded data for the given DexCode.
- static const uint8_t* GetCatchHandlerData(const DexInstructionIterator& code_item_end,
- uint32_t tries_size,
- uint32_t offset);
-
- // Find which try region is associated with the given address (ie dex pc). Returns -1 if none.
- static int32_t FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address);
-
- // Get the pointer to the start of the debugging data
- const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const {
- // Check that the offset is in bounds.
- // Note that although the specification says that 0 should be used if there
- // is no debug information, some applications incorrectly use 0xFFFFFFFF.
- return (debug_info_off == 0 || debug_info_off >= data_size_)
- ? nullptr
- : DataBegin() + debug_info_off;
- }
-
- struct PositionInfo {
- PositionInfo() = default;
-
- uint32_t address_ = 0; // In 16-bit code units.
- uint32_t line_ = 0; // Source code line number starting at 1.
- const char* source_file_ = nullptr; // nullptr if the file from ClassDef still applies.
- bool prologue_end_ = false;
- bool epilogue_begin_ = false;
- };
-
- struct LocalInfo {
- LocalInfo() = default;
-
- const char* name_ = nullptr; // E.g., list. It can be nullptr if unknown.
- const char* descriptor_ = nullptr; // E.g., Ljava/util/LinkedList;
- const char* signature_ = nullptr; // E.g., java.util.LinkedList<java.lang.Integer>
- uint32_t start_address_ = 0; // PC location where the local is first defined.
- uint32_t end_address_ = 0; // PC location where the local is no longer defined.
- uint16_t reg_ = 0; // Dex register which stores the values.
- bool is_live_ = false; // Is the local defined and live.
- };
-
- // Callback for "new locals table entry".
- typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry);
-
- static bool LineNumForPcCb(void* context, const PositionInfo& entry);
-
- const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const {
- return DataPointer<AnnotationsDirectoryItem>(class_def.annotations_off_);
- }
-
- const AnnotationSetItem* GetClassAnnotationSet(const AnnotationsDirectoryItem* anno_dir) const {
- return DataPointer<AnnotationSetItem>(anno_dir->class_annotations_off_);
- }
-
- const FieldAnnotationsItem* GetFieldAnnotations(const AnnotationsDirectoryItem* anno_dir) const {
- return (anno_dir->fields_size_ == 0)
- ? nullptr
- : reinterpret_cast<const FieldAnnotationsItem*>(&anno_dir[1]);
- }
-
- const MethodAnnotationsItem* GetMethodAnnotations(const AnnotationsDirectoryItem* anno_dir)
- const {
- if (anno_dir->methods_size_ == 0) {
- return nullptr;
- }
- // Skip past the header and field annotations.
- const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
- addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
- return reinterpret_cast<const MethodAnnotationsItem*>(addr);
- }
-
- const ParameterAnnotationsItem* GetParameterAnnotations(const AnnotationsDirectoryItem* anno_dir)
- const {
- if (anno_dir->parameters_size_ == 0) {
- return nullptr;
- }
- // Skip past the header, field annotations, and method annotations.
- const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
- addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
- addr += anno_dir->methods_size_ * sizeof(MethodAnnotationsItem);
- return reinterpret_cast<const ParameterAnnotationsItem*>(addr);
- }
-
- const AnnotationSetItem* GetFieldAnnotationSetItem(const FieldAnnotationsItem& anno_item) const {
- return DataPointer<AnnotationSetItem>(anno_item.annotations_off_);
- }
-
- const AnnotationSetItem* GetMethodAnnotationSetItem(const MethodAnnotationsItem& anno_item)
- const {
- return DataPointer<AnnotationSetItem>(anno_item.annotations_off_);
- }
-
- const AnnotationSetRefList* GetParameterAnnotationSetRefList(
- const ParameterAnnotationsItem* anno_item) const {
- return DataPointer<AnnotationSetRefList>(anno_item->annotations_off_);
- }
-
- ALWAYS_INLINE const AnnotationItem* GetAnnotationItemAtOffset(uint32_t offset) const {
- return DataPointer<AnnotationItem>(offset);
- }
-
- const AnnotationItem* GetAnnotationItem(const AnnotationSetItem* set_item, uint32_t index) const {
- DCHECK_LE(index, set_item->size_);
- return GetAnnotationItemAtOffset(set_item->entries_[index]);
- }
-
- const AnnotationSetItem* GetSetRefItemItem(const AnnotationSetRefItem* anno_item) const {
- return DataPointer<AnnotationSetItem>(anno_item->annotations_off_);
- }
-
- // Debug info opcodes and constants
- enum {
- DBG_END_SEQUENCE = 0x00,
- DBG_ADVANCE_PC = 0x01,
- DBG_ADVANCE_LINE = 0x02,
- DBG_START_LOCAL = 0x03,
- DBG_START_LOCAL_EXTENDED = 0x04,
- DBG_END_LOCAL = 0x05,
- DBG_RESTART_LOCAL = 0x06,
- DBG_SET_PROLOGUE_END = 0x07,
- DBG_SET_EPILOGUE_BEGIN = 0x08,
- DBG_SET_FILE = 0x09,
- DBG_FIRST_SPECIAL = 0x0a,
- DBG_LINE_BASE = -4,
- DBG_LINE_RANGE = 15,
- };
-
- struct LineNumFromPcContext {
- LineNumFromPcContext(uint32_t address, uint32_t line_num)
- : address_(address), line_num_(line_num) {}
- uint32_t address_;
- uint32_t line_num_;
- private:
- DISALLOW_COPY_AND_ASSIGN(LineNumFromPcContext);
- };
-
- // Returns false if there is no debugging information or if it cannot be decoded.
- template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
- static bool DecodeDebugLocalInfo(const uint8_t* stream,
- const std::string& location,
- const char* declaring_class_descriptor,
- const std::vector<const char*>& arg_descriptors,
- const std::string& method_name,
- bool is_static,
- uint16_t registers_size,
- uint16_t ins_size,
- uint16_t insns_size_in_code_units,
- IndexToStringData index_to_string_data,
- TypeIndexToStringData type_index_to_string_data,
- NewLocalCallback new_local,
- void* context);
- template<typename NewLocalCallback>
- bool DecodeDebugLocalInfo(uint32_t registers_size,
- uint32_t ins_size,
- uint32_t insns_size_in_code_units,
- uint32_t debug_info_offset,
- bool is_static,
- uint32_t method_idx,
- NewLocalCallback new_local,
- void* context) const;
-
- // Returns false if there is no debugging information or if it cannot be decoded.
- template<typename DexDebugNewPosition, typename IndexToStringData>
- static bool DecodeDebugPositionInfo(const uint8_t* stream,
- IndexToStringData index_to_string_data,
- DexDebugNewPosition position_functor,
- void* context);
- template<typename DexDebugNewPosition>
- bool DecodeDebugPositionInfo(uint32_t debug_info_offset,
- DexDebugNewPosition position_functor,
- void* context) const;
-
- const char* GetSourceFile(const ClassDef& class_def) const {
- if (!class_def.source_file_idx_.IsValid()) {
- return nullptr;
- } else {
- return StringDataByIdx(class_def.source_file_idx_);
- }
- }
-
- int GetPermissions() const;
-
- bool IsReadOnly() const;
-
- bool EnableWrite() const;
-
- bool DisableWrite() const;
-
- const uint8_t* Begin() const {
- return begin_;
- }
-
- size_t Size() const {
- return size_;
- }
-
- const uint8_t* DataBegin() const {
- return data_begin_;
- }
-
- size_t DataSize() const {
- return data_size_;
- }
-
- template <typename T>
- const T* DataPointer(size_t offset) const {
- DCHECK_LT(offset, DataSize()) << "Offset past end of data section";
- return (offset != 0u) ? reinterpret_cast<const T*>(DataBegin() + offset) : nullptr;
- }
-
- const OatDexFile* GetOatDexFile() const {
- return oat_dex_file_;
- }
-
- // Used by oat writer.
- void SetOatDexFile(OatDexFile* oat_dex_file) const {
- oat_dex_file_ = oat_dex_file;
- }
-
- // Read MapItems and validate/set remaining offsets.
- const DexFile::MapList* GetMapList() const {
- return reinterpret_cast<const DexFile::MapList*>(DataBegin() + header_->map_off_);
- }
-
- // Utility methods for reading integral values from a buffer.
- static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth);
- static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right);
- static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth);
- static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right);
-
- // Recalculates the checksum of the dex file. Does not use the current value in the header.
- virtual uint32_t CalculateChecksum() const;
- static uint32_t CalculateChecksum(const uint8_t* begin, size_t size);
- static uint32_t ChecksumMemoryRange(const uint8_t* begin, size_t size);
-
- // Returns a human-readable form of the method at an index.
- std::string PrettyMethod(uint32_t method_idx, bool with_signature = true) const;
- // Returns a human-readable form of the field at an index.
- std::string PrettyField(uint32_t field_idx, bool with_type = true) const;
- // Returns a human-readable form of the type at an index.
- std::string PrettyType(dex::TypeIndex type_idx) const;
-
- // Not virtual for performance reasons.
- ALWAYS_INLINE bool IsCompactDexFile() const {
- return is_compact_dex_;
- }
- ALWAYS_INLINE bool IsStandardDexFile() const {
- return !is_compact_dex_;
- }
- ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
- ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
-
- bool IsInMainSection(const void* addr) const {
- return Begin() <= addr && addr < Begin() + Size();
- }
-
- bool IsInDataSection(const void* addr) const {
- return DataBegin() <= addr && addr < DataBegin() + DataSize();
- }
-
- DexFileContainer* GetContainer() const {
- return container_.get();
- }
-
- protected:
- // First Dex format version supporting default methods.
- static const uint32_t kDefaultMethodsVersion = 37;
-
- DexFile(const uint8_t* base,
- size_t size,
- const uint8_t* data_begin,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- DexFileContainer* container,
- bool is_compact_dex);
-
- // Top-level initializer that calls other Init methods.
- bool Init(std::string* error_msg);
-
- // Returns true if the header magic and version numbers are of the expected values.
- bool CheckMagicAndVersion(std::string* error_msg) const;
-
- // 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_;
-
- // The size of the underlying memory allocation in bytes.
- const size_t size_;
-
- // The base address of the data section (same as Begin() for standard dex).
- const uint8_t* const data_begin_;
-
- // The size of the data section.
- const size_t data_size_;
-
- // Typically the dex file name when available, alternatively some identifying string.
- //
- // The ClassLinker will use this to match DexFiles the boot class
- // path to DexCache::GetLocation when loading from an image.
- const std::string location_;
-
- const uint32_t location_checksum_;
-
- // Points to the header section.
- const Header* const header_;
-
- // Points to the base of the string identifier list.
- const StringId* const string_ids_;
-
- // Points to the base of the type identifier list.
- const TypeId* const type_ids_;
-
- // Points to the base of the field identifier list.
- const FieldId* const field_ids_;
-
- // Points to the base of the method identifier list.
- const MethodId* const method_ids_;
-
- // Points to the base of the prototype identifier list.
- const ProtoId* const proto_ids_;
-
- // 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.
- mutable const OatDexFile* oat_dex_file_;
-
- // Manages the underlying memory allocation.
- std::unique_ptr<DexFileContainer> container_;
-
- // If the dex file is a compact dex file. If false then the dex file is a standard dex file.
- const bool is_compact_dex_;
-
- friend class DexFileLoader;
- friend class DexFileVerifierTest;
- friend class OatWriter;
-};
-
-std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
-
-// Iterate over a dex file's ProtoId's paramters
-class DexFileParameterIterator {
- public:
- DexFileParameterIterator(const DexFile& dex_file, const DexFile::ProtoId& proto_id)
- : dex_file_(dex_file) {
- type_list_ = dex_file_.GetProtoParameters(proto_id);
- if (type_list_ != nullptr) {
- size_ = type_list_->Size();
- }
- }
- bool HasNext() const { return pos_ < size_; }
- size_t Size() const { return size_; }
- void Next() { ++pos_; }
- dex::TypeIndex GetTypeIdx() {
- return type_list_->GetTypeItem(pos_).type_idx_;
- }
- const char* GetDescriptor() {
- return dex_file_.StringByTypeIdx(dex::TypeIndex(GetTypeIdx()));
- }
- private:
- const DexFile& dex_file_;
- const DexFile::TypeList* type_list_ = nullptr;
- uint32_t size_ = 0;
- uint32_t pos_ = 0;
- DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator);
-};
-
-// Abstract the signature of a method.
-class Signature : public ValueObject {
- public:
- std::string ToString() const;
-
- static Signature NoSignature() {
- return Signature();
- }
-
- bool IsVoid() const;
- uint32_t GetNumberOfParameters() const;
-
- bool operator==(const Signature& rhs) const;
- bool operator!=(const Signature& rhs) const {
- return !(*this == rhs);
- }
-
- bool operator==(const StringPiece& rhs) const;
-
- private:
- Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
- }
-
- Signature() = default;
-
- friend class DexFile;
-
- const DexFile* const dex_file_ = nullptr;
- const DexFile::ProtoId* const proto_id_ = nullptr;
-};
-std::ostream& operator<<(std::ostream& os, const Signature& sig);
-
-// Iterate and decode class_data_item
-class ClassDataItemIterator {
- public:
- ClassDataItemIterator(const DexFile& dex_file, const uint8_t* raw_class_data_item)
- : dex_file_(dex_file), pos_(0), ptr_pos_(raw_class_data_item), last_idx_(0) {
- ReadClassDataHeader();
- if (EndOfInstanceFieldsPos() > 0) {
- ReadClassDataField();
- } else if (EndOfVirtualMethodsPos() > 0) {
- ReadClassDataMethod();
- }
- }
- uint32_t NumStaticFields() const {
- return header_.static_fields_size_;
- }
- uint32_t NumInstanceFields() const {
- return header_.instance_fields_size_;
- }
- uint32_t NumDirectMethods() const {
- return header_.direct_methods_size_;
- }
- uint32_t NumVirtualMethods() const {
- return header_.virtual_methods_size_;
- }
- bool IsAtMethod() const {
- return pos_ >= EndOfInstanceFieldsPos();
- }
- bool HasNextStaticField() const {
- return pos_ < EndOfStaticFieldsPos();
- }
- bool HasNextInstanceField() const {
- return pos_ >= EndOfStaticFieldsPos() && pos_ < EndOfInstanceFieldsPos();
- }
- bool HasNextDirectMethod() const {
- return pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfDirectMethodsPos();
- }
- bool HasNextVirtualMethod() const {
- return pos_ >= EndOfDirectMethodsPos() && pos_ < EndOfVirtualMethodsPos();
- }
- bool HasNextMethod() const {
- const bool result = pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfVirtualMethodsPos();
- DCHECK_EQ(result, HasNextDirectMethod() || HasNextVirtualMethod());
- return result;
- }
- void SkipStaticFields() {
- while (HasNextStaticField()) {
- Next();
- }
- }
- void SkipInstanceFields() {
- while (HasNextInstanceField()) {
- Next();
- }
- }
- void SkipAllFields() {
- SkipStaticFields();
- SkipInstanceFields();
- }
- void SkipDirectMethods() {
- while (HasNextDirectMethod()) {
- Next();
- }
- }
- void SkipVirtualMethods() {
- while (HasNextVirtualMethod()) {
- Next();
- }
- }
- bool HasNext() const {
- return pos_ < EndOfVirtualMethodsPos();
- }
- inline void Next() {
- pos_++;
- if (pos_ < EndOfStaticFieldsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataField();
- } else if (pos_ == EndOfStaticFieldsPos() && NumInstanceFields() > 0) {
- last_idx_ = 0; // transition to next array, reset last index
- ReadClassDataField();
- } else if (pos_ < EndOfInstanceFieldsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataField();
- } else if (pos_ == EndOfInstanceFieldsPos() && NumDirectMethods() > 0) {
- last_idx_ = 0; // transition to next array, reset last index
- ReadClassDataMethod();
- } else if (pos_ < EndOfDirectMethodsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataMethod();
- } else if (pos_ == EndOfDirectMethodsPos() && NumVirtualMethods() > 0) {
- last_idx_ = 0; // transition to next array, reset last index
- ReadClassDataMethod();
- } else if (pos_ < EndOfVirtualMethodsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataMethod();
- } else {
- DCHECK(!HasNext());
- }
- }
- uint32_t GetMemberIndex() const {
- if (pos_ < EndOfInstanceFieldsPos()) {
- return last_idx_ + field_.field_idx_delta_;
- } else {
- DCHECK_LT(pos_, EndOfVirtualMethodsPos());
- return last_idx_ + method_.method_idx_delta_;
- }
- }
- uint32_t GetRawMemberAccessFlags() const {
- if (pos_ < EndOfInstanceFieldsPos()) {
- return field_.access_flags_;
- } else {
- DCHECK_LT(pos_, EndOfVirtualMethodsPos());
- return method_.access_flags_;
- }
- }
- uint32_t GetFieldAccessFlags() const {
- return GetMemberAccessFlags() & kAccValidFieldFlags;
- }
- uint32_t GetMethodAccessFlags() const {
- return GetMemberAccessFlags() & kAccValidMethodFlags;
- }
- uint32_t GetMemberAccessFlags() const {
- return HiddenApiAccessFlags::RemoveFromDex(GetRawMemberAccessFlags());
- }
- HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const {
- return HiddenApiAccessFlags::DecodeFromDex(GetRawMemberAccessFlags());
- }
- bool MemberIsNative() const {
- return GetRawMemberAccessFlags() & kAccNative;
- }
- bool MemberIsFinal() const {
- return GetRawMemberAccessFlags() & kAccFinal;
- }
- ALWAYS_INLINE InvokeType GetMethodInvokeType(const DexFile::ClassDef& class_def) const;
- const DexFile::CodeItem* GetMethodCodeItem() const {
- return dex_file_.GetCodeItem(method_.code_off_);
- }
- uint32_t GetMethodCodeItemOffset() const {
- return method_.code_off_;
- }
- const uint8_t* DataPointer() const {
- return ptr_pos_;
- }
- const uint8_t* EndDataPointer() const {
- CHECK(!HasNext());
- return ptr_pos_;
- }
-
- private:
- // A dex file's class_data_item is leb128 encoded, this structure holds a decoded form of the
- // header for a class_data_item
- struct ClassDataHeader {
- uint32_t static_fields_size_; // the number of static fields
- uint32_t instance_fields_size_; // the number of instance fields
- uint32_t direct_methods_size_; // the number of direct methods
- uint32_t virtual_methods_size_; // the number of virtual methods
- } header_;
-
- // Read and decode header from a class_data_item stream into header
- void ReadClassDataHeader();
-
- uint32_t EndOfStaticFieldsPos() const {
- return header_.static_fields_size_;
- }
- uint32_t EndOfInstanceFieldsPos() const {
- return EndOfStaticFieldsPos() + header_.instance_fields_size_;
- }
- uint32_t EndOfDirectMethodsPos() const {
- return EndOfInstanceFieldsPos() + header_.direct_methods_size_;
- }
- uint32_t EndOfVirtualMethodsPos() const {
- return EndOfDirectMethodsPos() + header_.virtual_methods_size_;
- }
-
- // A decoded version of the field of a class_data_item
- struct ClassDataField {
- uint32_t field_idx_delta_; // delta of index into the field_ids array for FieldId
- uint32_t access_flags_; // access flags for the field
- ClassDataField() : field_idx_delta_(0), access_flags_(0) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ClassDataField);
- };
- ClassDataField field_;
-
- // Read and decode a field from a class_data_item stream into field
- void ReadClassDataField();
-
- // A decoded version of the method of a class_data_item
- struct ClassDataMethod {
- uint32_t method_idx_delta_; // delta of index into the method_ids array for MethodId
- uint32_t access_flags_;
- uint32_t code_off_;
- ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ClassDataMethod);
- };
- ClassDataMethod method_;
-
- // Read and decode a method from a class_data_item stream into method
- void ReadClassDataMethod();
-
- const DexFile& dex_file_;
- size_t pos_; // integral number of items passed
- const uint8_t* ptr_pos_; // pointer into stream of class_data_item
- uint32_t last_idx_; // last read field or method index to apply delta to
- DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator);
-};
-
-class EncodedArrayValueIterator {
- public:
- 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,
- 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_; }
- const jvalue& GetJavaValue() const { return jval_; }
-
- protected:
- static constexpr uint8_t kEncodedValueTypeMask = 0x1f; // 0b11111
- static constexpr uint8_t kEncodedValueArgShift = 5;
-
- const DexFile& dex_file_;
- size_t array_size_; // Size of array.
- size_t pos_; // Current position.
- const uint8_t* ptr_; // Pointer into encoded data array.
- ValueType type_; // Type of current encoded value.
- 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);
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_H_
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index e01890f541..3431bb7efb 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -23,7 +23,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_linker-inl.h"
-#include "dex_file-inl.h"
+#include "dex/dex_file-inl.h"
#include "jni_internal.h"
#include "jvalue-inl.h"
#include "mirror/field.h"
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 26773729c2..d7ebf84b1c 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_
#define ART_RUNTIME_DEX_DEX_FILE_ANNOTATIONS_H_
-#include "dex_file.h"
+#include "dex/dex_file.h"
#include "handle.h"
#include "mirror/dex_cache.h"
diff --git a/runtime/dex/dex_file_exception_helpers.cc b/runtime/dex/dex_file_exception_helpers.cc
deleted file mode 100644
index 8e597fd3dd..0000000000
--- a/runtime/dex/dex_file_exception_helpers.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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 "dex_file_exception_helpers.h"
-
-#include "code_item_accessors-inl.h"
-
-namespace art {
-
-CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address) {
- handler_.address_ = -1;
- int32_t offset = -1;
-
- // Short-circuit the overwhelmingly common cases.
- switch (accessor.TriesSize()) {
- case 0:
- break;
- case 1: {
- const DexFile::TryItem* tries = accessor.TryItems().begin();
- uint32_t start = tries->start_addr_;
- if (address >= start) {
- uint32_t end = start + tries->insn_count_;
- if (address < end) {
- offset = tries->handler_off_;
- }
- }
- break;
- }
- default: {
- const DexFile::TryItem* try_item = accessor.FindTryItem(address);
- offset = try_item != nullptr ? try_item->handler_off_ : -1;
- break;
- }
- }
- Init(accessor, offset);
-}
-
-CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor,
- const DexFile::TryItem& try_item) {
- handler_.address_ = -1;
- Init(accessor, try_item.handler_off_);
-}
-
-void CatchHandlerIterator::Init(const CodeItemDataAccessor& accessor, int32_t offset) {
- if (offset >= 0) {
- Init(accessor.GetCatchHandlerData(offset));
- } else {
- // Not found, initialize as empty
- current_data_ = nullptr;
- remaining_count_ = -1;
- catch_all_ = false;
- DCHECK(!HasNext());
- }
-}
-
-void CatchHandlerIterator::Init(const uint8_t* handler_data) {
- current_data_ = handler_data;
- remaining_count_ = DecodeSignedLeb128(&current_data_);
-
- // If remaining_count_ is non-positive, then it is the negative of
- // the number of catch types, and the catches are followed by a
- // catch-all handler.
- if (remaining_count_ <= 0) {
- catch_all_ = true;
- remaining_count_ = -remaining_count_;
- } else {
- catch_all_ = false;
- }
- Next();
-}
-
-void CatchHandlerIterator::Next() {
- if (remaining_count_ > 0) {
- handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(&current_data_));
- handler_.address_ = DecodeUnsignedLeb128(&current_data_);
- remaining_count_--;
- return;
- }
-
- if (catch_all_) {
- handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16);
- handler_.address_ = DecodeUnsignedLeb128(&current_data_);
- catch_all_ = false;
- return;
- }
-
- // no more handler
- remaining_count_ = -1;
-}
-
-} // namespace art
diff --git a/runtime/dex/dex_file_exception_helpers.h b/runtime/dex/dex_file_exception_helpers.h
deleted file mode 100644
index bd6cb7e747..0000000000
--- a/runtime/dex/dex_file_exception_helpers.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
-#define ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
-
-#include "dex_file.h"
-
-namespace art {
-
-class CodeItemDataAccessor;
-
-class CatchHandlerIterator {
- public:
- CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address);
-
- CatchHandlerIterator(const CodeItemDataAccessor& accessor, const DexFile::TryItem& try_item);
-
- explicit CatchHandlerIterator(const uint8_t* handler_data) {
- Init(handler_data);
- }
-
- dex::TypeIndex GetHandlerTypeIndex() const {
- return handler_.type_idx_;
- }
- uint32_t GetHandlerAddress() const {
- return handler_.address_;
- }
- void Next();
- bool HasNext() const {
- return remaining_count_ != -1 || catch_all_;
- }
- // End of this set of catch blocks, convenience method to locate next set of catch blocks
- const uint8_t* EndDataPointer() const {
- CHECK(!HasNext());
- return current_data_;
- }
-
- private:
- void Init(const CodeItemDataAccessor& accessor, int32_t offset);
- void Init(const uint8_t* handler_data);
-
- struct CatchHandlerItem {
- dex::TypeIndex type_idx_; // type index of the caught exception type
- uint32_t address_; // handler address
- } handler_;
- const uint8_t* current_data_; // the current handler in dex file.
- int32_t remaining_count_; // number of handlers not read.
- bool catch_all_; // is there a handler that will catch all exceptions in case
- // that all typed handler does not match.
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
diff --git a/runtime/dex/dex_file_layout.cc b/runtime/dex/dex_file_layout.cc
index 312898d82f..d85d61d56b 100644
--- a/runtime/dex/dex_file_layout.cc
+++ b/runtime/dex/dex_file_layout.cc
@@ -19,8 +19,7 @@
#include <sys/mman.h>
#include "base/file_utils.h"
-#include "descriptors_names.h"
-#include "dex_file.h"
+#include "dex/dex_file.h"
namespace art {
diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc
deleted file mode 100644
index 2c75c5b5d9..0000000000
--- a/runtime/dex/dex_file_loader.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * 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 "dex_file_loader.h"
-
-#include "android-base/stringprintf.h"
-
-#include "base/stl_util.h"
-#include "compact_dex_file.h"
-#include "dex_file.h"
-#include "dex_file_verifier.h"
-#include "standard_dex_file.h"
-#include "ziparchive/zip_archive.h"
-
-// system/core/zip_archive definitions.
-struct ZipEntry;
-typedef void* ZipArchiveHandle;
-
-namespace art {
-
-namespace {
-
-class VectorContainer : public DexFileContainer {
- public:
- explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { }
- virtual ~VectorContainer() OVERRIDE { }
-
- int GetPermissions() OVERRIDE {
- return 0;
- }
-
- bool IsReadOnly() OVERRIDE {
- return true;
- }
-
- bool EnableWrite() OVERRIDE {
- return false;
- }
-
- bool DisableWrite() OVERRIDE {
- return false;
- }
-
- private:
- std::vector<uint8_t> vector_;
- DISALLOW_COPY_AND_ASSIGN(VectorContainer);
-};
-
-} // namespace
-
-using android::base::StringPrintf;
-
-class DexZipArchive;
-
-class DexZipEntry {
- public:
- // Extract this entry to memory.
- // Returns null on failure and sets error_msg.
- const std::vector<uint8_t> Extract(std::string* error_msg) {
- std::vector<uint8_t> map(GetUncompressedLength());
- if (map.size() == 0) {
- DCHECK(!error_msg->empty());
- return map;
- }
- const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size());
- if (error) {
- *error_msg = std::string(ErrorCodeString(error));
- }
- return map;
- }
-
- virtual ~DexZipEntry() {
- delete zip_entry_;
- }
-
- uint32_t GetUncompressedLength() {
- return zip_entry_->uncompressed_length;
- }
-
- uint32_t GetCrc32() {
- return zip_entry_->crc32;
- }
-
- private:
- DexZipEntry(ZipArchiveHandle handle,
- ::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 DexZipArchive;
- DISALLOW_COPY_AND_ASSIGN(DexZipEntry);
-};
-
-class DexZipArchive {
- public:
- // return new DexZipArchive instance on success, null on error.
- static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) {
- ZipArchiveHandle handle;
- uint8_t* nonconst_base = const_cast<uint8_t*>(base);
- const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle);
- if (error) {
- *error_msg = std::string(ErrorCodeString(error));
- CloseArchive(handle);
- return nullptr;
- }
- return new DexZipArchive(handle);
- }
-
- DexZipEntry* Find(const char* name, std::string* error_msg) const {
- DCHECK(name != nullptr);
- // Resist the urge to delete the space. <: is a bigraph sequence.
- std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
- const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get());
- if (error) {
- *error_msg = std::string(ErrorCodeString(error));
- return nullptr;
- }
- return new DexZipEntry(handle_, zip_entry.release(), name);
- }
-
- ~DexZipArchive() {
- CloseArchive(handle_);
- }
-
-
- private:
- explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {}
- ZipArchiveHandle handle_;
-
- friend class DexZipEntry;
- DISALLOW_COPY_AND_ASSIGN(DexZipArchive);
-};
-
-static bool IsZipMagic(uint32_t magic) {
- return (('P' == ((magic >> 0) & 0xff)) &&
- ('K' == ((magic >> 8) & 0xff)));
-}
-
-bool DexFileLoader::IsMagicValid(uint32_t magic) {
- return IsMagicValid(reinterpret_cast<uint8_t*>(&magic));
-}
-
-bool DexFileLoader::IsMagicValid(const uint8_t* magic) {
- return StandardDexFile::IsMagicValid(magic) ||
- CompactDexFile::IsMagicValid(magic);
-}
-
-bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) {
- if (StandardDexFile::IsMagicValid(magic)) {
- return StandardDexFile::IsVersionValid(magic);
- }
- if (CompactDexFile::IsMagicValid(magic)) {
- return CompactDexFile::IsVersionValid(magic);
- }
- return false;
-}
-
-bool DexFileLoader::IsMultiDexLocation(const char* location) {
- return strrchr(location, kMultiDexSeparator) != nullptr;
-}
-
-std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) {
- return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1);
-}
-
-std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
- return (index == 0)
- ? dex_location
- : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
-}
-
-std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) {
- CHECK_NE(dex_location, static_cast<const char*>(nullptr));
- std::string base_location = GetBaseLocation(dex_location);
- const char* suffix = dex_location + base_location.size();
- DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
- UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
- if (path != nullptr && path.get() != base_location) {
- return std::string(path.get()) + suffix;
- } else if (suffix[0] == 0) {
- return base_location;
- } else {
- return dex_location;
- }
-}
-
-// All of the implementations here should be independent of the runtime.
-// TODO: implement all the virtual methods.
-
-bool DexFileLoader::GetMultiDexChecksums(const char* filename ATTRIBUTE_UNUSED,
- std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
- std::string* error_msg,
- int zip_fd ATTRIBUTE_UNUSED) const {
- *error_msg = "UNIMPLEMENTED";
- return false;
-}
-
-std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const {
- return OpenCommon(base,
- size,
- /*data_base*/ nullptr,
- /*data_size*/ 0,
- location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg,
- /*container*/ nullptr,
- /*verify_result*/ nullptr);
-}
-
-std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection(
- const uint8_t* base,
- size_t size,
- const uint8_t* data_base,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const {
- return OpenCommon(base,
- size,
- data_base,
- data_size,
- location,
- location_checksum,
- oat_dex_file,
- verify,
- verify_checksum,
- error_msg,
- /*container*/ nullptr,
- /*verify_result*/ nullptr);
-}
-
-bool DexFileLoader::OpenAll(
- const uint8_t* base,
- size_t size,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
- uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
- if (IsZipMagic(magic)) {
- std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
- if (zip_archive.get() == nullptr) {
- DCHECK(!error_msg->empty());
- return false;
- }
- return OpenAllDexFilesFromZip(*zip_archive.get(),
- location,
- verify,
- verify_checksum,
- error_msg,
- dex_files);
- }
- if (IsMagicValid(magic)) {
- const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base);
- std::unique_ptr<const DexFile> dex_file(Open(base,
- size,
- location,
- dex_header->checksum_,
- /*oat_dex_file*/ nullptr,
- verify,
- verify_checksum,
- error_msg));
- if (dex_file.get() != nullptr) {
- dex_files->push_back(std::move(dex_file));
- return true;
- } else {
- return false;
- }
- }
- *error_msg = StringPrintf("Expected valid zip or dex file");
- return false;
-}
-
-std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
- size_t size,
- const uint8_t* data_base,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- DexFileContainer* container,
- VerifyResult* verify_result) {
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifyNotAttempted;
- }
- std::unique_ptr<DexFile> dex_file;
- if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
- if (data_size != 0) {
- CHECK_EQ(base, data_base) << "Unsupported for standard dex";
- }
- dex_file.reset(new StandardDexFile(base,
- size,
- location,
- location_checksum,
- oat_dex_file,
- container));
- } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
- if (data_base == nullptr) {
- // TODO: Is there a clean way to support both an explicit data section and reading the one
- // from the header.
- CHECK_EQ(data_size, 0u);
- const CompactDexFile::Header* const header = CompactDexFile::Header::At(base);
- data_base = base + header->data_off_;
- data_size = header->data_size_;
- }
- dex_file.reset(new CompactDexFile(base,
- size,
- data_base,
- data_size,
- location,
- location_checksum,
- oat_dex_file,
- container));
- } else {
- *error_msg = "Invalid or truncated dex file";
- }
- if (dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
- error_msg->c_str());
- return nullptr;
- }
- if (!dex_file->Init(error_msg)) {
- dex_file.reset();
- return nullptr;
- }
- if (verify && !DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- location.c_str(),
- verify_checksum,
- error_msg)) {
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifyFailed;
- }
- return nullptr;
- }
- if (verify_result != nullptr) {
- *verify_result = VerifyResult::kVerifySucceeded;
- }
- return dex_file;
-}
-
-std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
- const DexZipArchive& zip_archive,
- const char* entry_name,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- ZipOpenErrorCode* error_code) const {
- CHECK(!location.empty());
- std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
- if (zip_entry == nullptr) {
- *error_code = ZipOpenErrorCode::kEntryNotFound;
- return nullptr;
- }
- if (zip_entry->GetUncompressedLength() == 0) {
- *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
- *error_code = ZipOpenErrorCode::kDexFileError;
- return nullptr;
- }
-
- std::vector<uint8_t> map(zip_entry->Extract(error_msg));
- if (map.size() == 0) {
- *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
- error_msg->c_str());
- *error_code = ZipOpenErrorCode::kExtractToMemoryError;
- return nullptr;
- }
- VerifyResult verify_result;
- std::unique_ptr<const DexFile> dex_file = OpenCommon(map.data(),
- map.size(),
- /*data_base*/ nullptr,
- /*data_size*/ 0u,
- location,
- zip_entry->GetCrc32(),
- /*oat_dex_file*/ nullptr,
- verify,
- verify_checksum,
- error_msg,
- new VectorContainer(std::move(map)),
- &verify_result);
- if (dex_file == nullptr) {
- if (verify_result == VerifyResult::kVerifyNotAttempted) {
- *error_code = ZipOpenErrorCode::kDexFileError;
- } else {
- *error_code = ZipOpenErrorCode::kVerifyError;
- }
- return nullptr;
- }
- if (verify_result != VerifyResult::kVerifySucceeded) {
- *error_code = ZipOpenErrorCode::kVerifyError;
- return nullptr;
- }
- *error_code = ZipOpenErrorCode::kNoError;
- return dex_file;
-}
-
-// Technically we do not have a limitation with respect to the number of dex files that can be in a
-// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
-// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
-// seems an excessive number.
-static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
-
-bool DexFileLoader::OpenAllDexFilesFromZip(
- const DexZipArchive& zip_archive,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
- DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
- ZipOpenErrorCode error_code;
- std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
- kClassesDex,
- location,
- verify,
- verify_checksum,
- error_msg,
- &error_code));
- if (dex_file.get() == nullptr) {
- return false;
- } else {
- // Had at least classes.dex.
- dex_files->push_back(std::move(dex_file));
-
- // Now try some more.
-
- // We could try to avoid std::string allocations by working on a char array directly. As we
- // do not expect a lot of iterations, this seems too involved and brittle.
-
- for (size_t i = 1; ; ++i) {
- std::string name = GetMultiDexClassesDexName(i);
- std::string fake_location = GetMultiDexLocation(i, location.c_str());
- std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
- name.c_str(),
- fake_location,
- verify,
- verify_checksum,
- error_msg,
- &error_code));
- if (next_dex_file.get() == nullptr) {
- if (error_code != ZipOpenErrorCode::kEntryNotFound) {
- LOG(WARNING) << "Zip open failed: " << *error_msg;
- }
- break;
- } else {
- dex_files->push_back(std::move(next_dex_file));
- }
-
- if (i == kWarnOnManyDexFilesThreshold) {
- LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
- << " dex files. Please consider coalescing and shrinking the number to "
- " avoid runtime overhead.";
- }
-
- if (i == std::numeric_limits<size_t>::max()) {
- LOG(ERROR) << "Overflow in number of dex files!";
- break;
- }
- }
-
- return true;
- }
-}
-} // namespace art
diff --git a/runtime/dex/dex_file_loader.h b/runtime/dex/dex_file_loader.h
deleted file mode 100644
index 508397cb00..0000000000
--- a/runtime/dex/dex_file_loader.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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_DEX_DEX_FILE_LOADER_H_
-#define ART_RUNTIME_DEX_DEX_FILE_LOADER_H_
-
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace art {
-
-class DexFile;
-class DexFileContainer;
-class MemMap;
-class OatDexFile;
-
-class DexZipArchive;
-
-// Class that is used to open dex files and deal with corresponding multidex and location logic.
-class DexFileLoader {
- public:
- // name of the DexFile entry within a zip archive
- static constexpr const char* kClassesDex = "classes.dex";
-
- // The separator character in MultiDex locations.
- static constexpr char kMultiDexSeparator = '!';
-
- // Return true if the magic is valid for dex or cdex.
- static bool IsMagicValid(uint32_t magic);
- static bool IsMagicValid(const uint8_t* magic);
-
- // Return true if the corresponding version and magic is valid.
- static bool IsVersionAndMagicValid(const uint8_t* magic);
-
- // 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);
-
- // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for
- // index == 0, and classes{index + 1}.dex else.
- static std::string GetMultiDexClassesDexName(size_t index);
-
- // Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for
- // index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else.
- static std::string GetMultiDexLocation(size_t index, const char* dex_location);
-
- // Returns the canonical form of the given dex location.
- //
- // There are different flavors of "dex locations" as follows:
- // the file name of a dex file:
- // The actual file path that the dex file has on disk.
- // dex_location:
- // This acts as a key for the class linker to know which dex file to load.
- // It may correspond to either an old odex file or a particular dex file
- // inside an oat file. In the first case it will also match the file name
- // of the dex file. In the second case (oat) it will include the file name
- // and possibly some multidex annotation to uniquely identify it.
- // canonical_dex_location:
- // the dex_location where it's file name part has been made canonical.
- static std::string GetDexCanonicalLocation(const char* dex_location);
-
- // For normal dex files, location and base location coincide. If a dex file is part of a multidex
- // archive, the base location is the name of the originating jar/apk, stripped of any internal
- // classes*.dex path.
- static std::string GetBaseLocation(const char* location) {
- const char* pos = strrchr(location, kMultiDexSeparator);
- return (pos == nullptr) ? location : std::string(location, pos - location);
- }
-
- static std::string GetBaseLocation(const std::string& location) {
- return GetBaseLocation(location.c_str());
- }
-
- // Returns the '!classes*.dex' part of the dex location. Returns an empty
- // string if there is no multidex suffix for the given location.
- // The kMultiDexSeparator is included in the returned suffix.
- static std::string GetMultiDexSuffix(const std::string& location) {
- size_t pos = location.rfind(kMultiDexSeparator);
- return (pos == std::string::npos) ? std::string() : location.substr(pos);
- }
-
- virtual ~DexFileLoader() { }
-
- // 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.
- // If a valid zip_fd is provided the file content will be read directly from
- // the descriptor and `filename` will be used as alias for error logging. If
- // zip_fd is -1, the method will try to open the `filename` and read the
- // content from it.
- // Return true if the checksums could be found, false otherwise.
- virtual bool GetMultiDexChecksums(const char* filename,
- std::vector<uint32_t>* checksums,
- std::string* error_msg,
- int zip_fd = -1) const;
-
- // Opens .dex file, backed by existing memory
- virtual std::unique_ptr<const DexFile> Open(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const;
-
- // Open a dex file with a separate data section.
- virtual std::unique_ptr<const DexFile> OpenWithDataSection(
- const uint8_t* base,
- size_t size,
- const uint8_t* data_base,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg) const;
-
-
- // Opens all .dex files found in the memory map, guessing the container format based on file
- // extension.
- virtual bool OpenAll(const uint8_t* base,
- size_t size,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- protected:
- enum class ZipOpenErrorCode {
- kNoError,
- kEntryNotFound,
- kExtractToMemoryError,
- kDexFileError,
- kMakeReadOnlyError,
- kVerifyError
- };
-
- enum class VerifyResult { // private
- kVerifyNotAttempted,
- kVerifySucceeded,
- kVerifyFailed
- };
-
- static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base,
- size_t size,
- const uint8_t* data_base,
- size_t data_size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- DexFileContainer* container,
- VerifyResult* verify_result);
-
- private:
- // Open all classesXXX.dex files from a zip archive.
- bool OpenAllDexFilesFromZip(const DexZipArchive& zip_archive,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
-
- // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
- // return.
- std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const DexZipArchive& zip_archive,
- const char* entry_name,
- const std::string& location,
- bool verify,
- bool verify_checksum,
- std::string* error_msg,
- ZipOpenErrorCode* error_code) const;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_LOADER_H_
diff --git a/runtime/dex/dex_file_reference.h b/runtime/dex/dex_file_reference.h
deleted file mode 100644
index 6f882900c6..0000000000
--- a/runtime/dex/dex_file_reference.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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_DEX_DEX_FILE_REFERENCE_H_
-#define ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_
-
-#include <cstdint>
-
-namespace art {
-
-class DexFile;
-
-class DexFileReference {
- public:
- DexFileReference(const DexFile* file, uint32_t idx) : dex_file(file), index(idx) {}
- const DexFile* dex_file;
- uint32_t index;
-
- struct Comparator {
- bool operator()(const DexFileReference& a, const DexFileReference& b) const {
- if (a.dex_file != b.dex_file) {
- return a.dex_file < b.dex_file;
- }
- return a.index < b.index;
- }
- };
-};
-
-// Default comparators, compares the indicies, not the backing data.
-inline bool operator<(const DexFileReference& a, const DexFileReference& b) {
- return DexFileReference::Comparator()(a, b);
-}
-inline bool operator==(const DexFileReference& a, const DexFileReference& b) {
- return a.dex_file == b.dex_file && a.index == b.index;
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_REFERENCE_H_
diff --git a/runtime/dex/dex_file_test.cc b/runtime/dex/dex_file_test.cc
deleted file mode 100644
index 2bb86672dc..0000000000
--- a/runtime/dex/dex_file_test.cc
+++ /dev/null
@@ -1,749 +0,0 @@
-/*
- * Copyright (C) 2011 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 "dex_file.h"
-
-#include <sys/mman.h>
-
-#include <memory>
-
-#include "art_dex_file_loader.h"
-#include "base/stl_util.h"
-#include "base/unix_file/fd_file.h"
-#include "base64_test_util.h"
-#include "code_item_accessors-inl.h"
-#include "common_runtime_test.h"
-#include "descriptors_names.h"
-#include "dex_file-inl.h"
-#include "dex_file_loader.h"
-#include "mem_map.h"
-#include "os.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
-
-namespace art {
-
-class DexFileTest : public CommonRuntimeTest {};
-
-TEST_F(DexFileTest, Open) {
- ScopedObjectAccess soa(Thread::Current());
- std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested"));
- ASSERT_TRUE(dex.get() != nullptr);
-}
-
-static inline std::vector<uint8_t> DecodeBase64Vec(const char* src) {
- std::vector<uint8_t> res;
- size_t size;
- std::unique_ptr<uint8_t[]> data(DecodeBase64(src, &size));
- res.resize(size);
- memcpy(res.data(), data.get(), size);
- return res;
-}
-
-// Although this is the same content logically as the Nested test dex,
-// the DexFileHeader test is sensitive to subtle changes in the
-// contents due to the checksum etc, so we embed the exact input here.
-//
-// class Nested {
-// class Inner {
-// }
-// }
-static const char kRawDex[] =
- "ZGV4CjAzNQAQedgAe7gM1B/WHsWJ6L7lGAISGC7yjD2IAwAAcAAAAHhWNBIAAAAAAAAAAMQCAAAP"
- "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAABAAAA4AAAAAMAAADoAAAAAgAAAAABAABIAgAAQAEAAK4B"
- "AAC2AQAAvQEAAM0BAADXAQAA+wEAABsCAAA+AgAAUgIAAF8CAABiAgAAZgIAAHMCAAB5AgAAgQIA"
- "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABgAAAAAAAAAKAAAABgAAAKgBAAAAAAEA"
- "DQAAAAAAAQAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAiAEAAKsCAAAA"
- "AAAAAQAAAAAAAAAFAAAAAAAAAAgAAACYAQAAuAIAAAAAAAACAAAAlAIAAJoCAAABAAAAowIAAAIA"
- "AgABAAAAiAIAAAYAAABbAQAAcBACAAAADgABAAEAAQAAAI4CAAAEAAAAcBACAAAADgBAAQAAAAAA"
- "AAAAAAAAAAAATAEAAAAAAAAAAAAAAAAAAAEAAAABAAY8aW5pdD4ABUlubmVyAA5MTmVzdGVkJElu"
- "bmVyOwAITE5lc3RlZDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2"
- "aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNz"
- "ZXM7ABJMamF2YS9sYW5nL09iamVjdDsAC05lc3RlZC5qYXZhAAFWAAJWTAALYWNjZXNzRmxhZ3MA"
- "BG5hbWUABnRoaXMkMAAFdmFsdWUAAgEABw4AAQAHDjwAAgIBDhgBAgMCCwQADBcBAgQBDhwBGAAA"
- "AQEAAJAgAICABNQCAAABAAGAgATwAgAAEAAAAAAAAAABAAAAAAAAAAEAAAAPAAAAcAAAAAIAAAAH"
- "AAAArAAAAAMAAAACAAAAyAAAAAQAAAABAAAA4AAAAAUAAAADAAAA6AAAAAYAAAACAAAAAAEAAAMQ"
- "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
- "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
-
-// kRawDex{38,39,40,41} are dex'ed versions of the following Java source :
-//
-// public class Main {
-// public static void main(String[] foo) {
-// }
-// }
-//
-// The dex file was manually edited to change its dex version code to 38
-// or 39, respectively.
-static const char kRawDex38[] =
- "ZGV4CjAzOAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
- "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
- "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
- "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
- "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
- "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
- "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
- "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
- "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
-
-static const char kRawDex39[] =
- "ZGV4CjAzOQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
- "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
- "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
- "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
- "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
- "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
- "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
- "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
- "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
-
-static const char kRawDex40[] =
- "ZGV4CjA0MAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
- "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
- "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
- "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
- "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
- "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
- "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
- "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
- "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
-
-static const char kRawDex41[] =
- "ZGV4CjA0MQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
- "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
- "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
- "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
- "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
- "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
- "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
- "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
- "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
-
-static const char kRawDexZeroLength[] =
- "UEsDBAoAAAAAAOhxAkkAAAAAAAAAAAAAAAALABwAY2xhc3Nlcy5kZXhVVAkAA2QNoVdnDaFXdXgL"
- "AAEE5AMBAASIEwAAUEsBAh4DCgAAAAAA6HECSQAAAAAAAAAAAAAAAAsAGAAAAAAAAAAAAKCBAAAA"
- "AGNsYXNzZXMuZGV4VVQFAANkDaFXdXgLAAEE5AMBAASIEwAAUEsFBgAAAAABAAEAUQAAAEUAAAAA"
- "AA==";
-
-static const char kRawZipClassesDexPresent[] =
- "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VXdXgL"
- "AAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMDQ0WY"
- "iRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEIEEcA"
- "cS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu4IOa"
- "wczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhHIykL"
- "LinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkVIGpA"
- "Yc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEBAAAg"
- "AgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEABIgTAABQ"
- "SwUGAAAAAAEAAQBRAAAAdgEAAAAA";
-
-static const char kRawZipClassesDexAbsent[] =
- "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAAOABwAbm90Y2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VX"
- "dXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMD"
- "Q0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEI"
- "EEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu"
- "4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhH"
- "IykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkV"
- "IGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEB"
- "AAAgAgAADgAYAAAAAAAAAAAAoIEAAAAAbm90Y2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEA"
- "BIgTAABQSwUGAAAAAAEAAQBUAAAAeQEAAAAA";
-
-static const char kRawZipThreeDexFiles[] =
- "UEsDBBQAAAAIAP1WN0ms99lIMQEAACACAAAMABwAY2xhc3NlczIuZGV4VVQJAAOtbOVXrWzlV3V4"
- "CwABBOQDAQAEiBMAAEtJreAyMLZg2GHVfXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NF"
- "mIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBAAGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBH"
- "AHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiDHWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCD"
- "msHMwGaTmZdZYsfA5uObmJlnzSDkk5VYlqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMp"
- "Cy4pysxLt2ZgyQUqAzmYj4EZTIL909jA8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBq"
- "QGHOKAARB4UbkwLETFA8MEPVgMKCQQGiBhxOUPWgeAYAUEsDBBQAAAAIAABXN0ms99lIMQEAACAC"
- "AAAMABwAY2xhc3NlczMuZGV4VVQJAAOvbOVXr2zlV3V4CwABBOQDAQAEiBMAAEtJreAyMLZg2GHV"
- "fXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NFmIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBA"
- "AGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBHAHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiD"
- "HWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCDmsHMwGaTmZdZYsfA5uObmJlnzSDkk5VY"
- "lqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMpCy4pysxLt2ZgyQUqAzmYj4EZTIL909jA"
- "8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBqQGHOKAARB4UbkwLETFA8MEPVgMKCQQGi"
- "BhxOUPWgeAYAUEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj"
- "5VetbOVXdXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQC"
- "EwNDAQMDQ0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGx"
- "GxAHAnEIEEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8"
- "UFGgP6Fu4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYx"
- "MIX5MAhHIykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHC"
- "mg0pvBkVIGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACAD9VjdJ"
- "rPfZSDEBAAAgAgAADAAYAAAAAAAAAAAAoIEAAAAAY2xhc3NlczIuZGV4VVQFAAOtbOVXdXgLAAEE"
- "5AMBAASIEwAAUEsBAh4DFAAAAAgAAFc3Saz32UgxAQAAIAIAAAwAGAAAAAAAAAAAAKCBdwEAAGNs"
- "YXNzZXMzLmRleFVUBQADr2zlV3V4CwABBOQDAQAEiBMAAFBLAQIeAxQAAAAIANVRN0ms99lIMQEA"
- "ACACAAALABgAAAAAAAAAAACgge4CAABjbGFzc2VzLmRleFVUBQADAWPlV3V4CwABBOQDAQAEiBMA"
- "AFBLBQYAAAAAAwADAPUAAABkBAAAAAA=";
-
-static const char kRawDexBadMapOffset[] =
- "ZGV4CjAzNQAZKGSz85r+tXJ1I24FYi+FpQtWbXtelAmoAQAAcAAAAHhWNBIAAAAAAAAAAEAwIBAF"
- "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADcAAAAzAAAAOQA"
- "AADsAAAA9AAAAPkAAAANAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAA"
- "AAAAAAABAAAAAQAAAAAAAAABAAAAAAAAABUBAAAAAAAAAQABAAEAAAAQAQAABAAAAHAQAQAAAA4A"
- "Bjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABAAcOAAAAAQAAgYAE"
- "zAEACwAAAAAAAAABAAAAAAAAAAEAAAAFAAAAcAAAAAIAAAADAAAAhAAAAAMAAAABAAAAkAAAAAUA"
- "AAACAAAAnAAAAAYAAAABAAAArAAAAAEgAAABAAAAzAAAAAIgAAAFAAAA5AAAAAMgAAABAAAAEAEA"
- "AAAgAAABAAAAFQEAAAAQAAABAAAAIAEAAA==";
-
-static const char kRawDexDebugInfoLocalNullType[] =
- "ZGV4CjAzNQA+Kwj2g6OZMH88OvK9Ey6ycdIsFCt18ED8AQAAcAAAAHhWNBIAAAAAAAAAAHQBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAMAQAA8AAAABwB"
- "AAAkAQAALAEAAC8BAAA0AQAASAEAAEsBAABOAQAAAgAAAAMAAAAEAAAABQAAAAIAAAAAAAAAAAAA"
- "AAUAAAADAAAAAAAAAAEAAQAAAAAAAQAAAAYAAAACAAEAAAAAAAEAAAABAAAAAgAAAAAAAAABAAAA"
- "AAAAAGMBAAAAAAAAAQABAAEAAABUAQAABAAAAHAQAgAAAA4AAgABAAAAAABZAQAAAgAAABIQDwAG"
- "PGluaXQ+AAZBLmphdmEAAUkAA0xBOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAFhAAR0aGlzAAEA"
- "Bw4AAwAHDh4DAAcAAAAAAQEAgYAE8AEBAIgCAAAACwAAAAAAAAABAAAAAAAAAAEAAAAIAAAAcAAA"
- "AAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAAuAAAAAYAAAABAAAA0AAAAAEgAAACAAAA"
- "8AAAAAIgAAAIAAAAHAEAAAMgAAACAAAAVAEAAAAgAAABAAAAYwEAAAAQAAABAAAAdAEAAA==";
-
-static void DecodeAndWriteDexFile(const char* base64, const char* location) {
- // decode base64
- CHECK(base64 != nullptr);
- std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64);
- CHECK_NE(dex_bytes.size(), 0u);
-
- // write to provided file
- std::unique_ptr<File> file(OS::CreateEmptyFile(location));
- CHECK(file.get() != nullptr);
- if (!file->WriteFully(dex_bytes.data(), dex_bytes.size())) {
- PLOG(FATAL) << "Failed to write base64 as dex file";
- }
- if (file->FlushCloseOrErase() != 0) {
- PLOG(FATAL) << "Could not flush and close test file.";
- }
-}
-
-static bool OpenDexFilesBase64(const char* base64,
- const char* location,
- std::vector<std::unique_ptr<const DexFile>>* dex_files,
- std::string* error_msg) {
- DecodeAndWriteDexFile(base64, location);
-
- // read dex file(s)
- ScopedObjectAccess soa(Thread::Current());
- static constexpr bool kVerifyChecksum = true;
- std::vector<std::unique_ptr<const DexFile>> tmp;
- const ArtDexFileLoader dex_file_loader;
- bool success = dex_file_loader.Open(
- location, location, /* verify */ true, kVerifyChecksum, error_msg, &tmp);
- if (success) {
- for (std::unique_ptr<const DexFile>& dex_file : tmp) {
- EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
- EXPECT_TRUE(dex_file->IsReadOnly());
- }
- *dex_files = std::move(tmp);
- }
- return success;
-}
-
-static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
- const char* location) {
- // read dex files.
- std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- bool success = OpenDexFilesBase64(base64, location, &dex_files, &error_msg);
- CHECK(success) << error_msg;
- EXPECT_EQ(1U, dex_files.size());
- return std::move(dex_files[0]);
-}
-
-static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
- const char* location,
- uint32_t location_checksum,
- bool expect_success) {
- CHECK(base64 != nullptr);
- std::vector<uint8_t> dex_bytes = DecodeBase64Vec(base64);
- CHECK_NE(dex_bytes.size(), 0u);
-
- std::string error_message;
- std::unique_ptr<MemMap> region(MemMap::MapAnonymous("test-region",
- nullptr,
- dex_bytes.size(),
- PROT_READ | PROT_WRITE,
- /* low_4gb */ false,
- /* reuse */ false,
- &error_message));
- memcpy(region->Begin(), dex_bytes.data(), dex_bytes.size());
- const ArtDexFileLoader dex_file_loader;
- std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location,
- location_checksum,
- std::move(region),
- /* verify */ true,
- /* verify_checksum */ true,
- &error_message));
- if (expect_success) {
- CHECK(dex_file != nullptr) << error_message;
- } else {
- CHECK(dex_file == nullptr) << "Expected dex file open to fail.";
- }
- return dex_file;
-}
-
-static void ValidateDexFileHeader(std::unique_ptr<const DexFile> dex_file) {
- static const uint8_t kExpectedDexFileMagic[8] = {
- /* d */ 0x64, /* e */ 0x64, /* x */ 0x78, /* \n */ 0x0d,
- /* 0 */ 0x30, /* 3 */ 0x33, /* 5 */ 0x35, /* \0 */ 0x00
- };
- static const uint8_t kExpectedSha1[DexFile::kSha1DigestSize] = {
- 0x7b, 0xb8, 0x0c, 0xd4, 0x1f, 0xd6, 0x1e, 0xc5,
- 0x89, 0xe8, 0xbe, 0xe5, 0x18, 0x02, 0x12, 0x18,
- 0x2e, 0xf2, 0x8c, 0x3d,
- };
-
- const DexFile::Header& header = dex_file->GetHeader();
- EXPECT_EQ(*kExpectedDexFileMagic, *header.magic_);
- EXPECT_EQ(0x00d87910U, header.checksum_);
- EXPECT_EQ(*kExpectedSha1, *header.signature_);
- EXPECT_EQ(904U, header.file_size_);
- EXPECT_EQ(112U, header.header_size_);
- EXPECT_EQ(0U, header.link_size_);
- EXPECT_EQ(0U, header.link_off_);
- EXPECT_EQ(15U, header.string_ids_size_);
- EXPECT_EQ(112U, header.string_ids_off_);
- EXPECT_EQ(7U, header.type_ids_size_);
- EXPECT_EQ(172U, header.type_ids_off_);
- EXPECT_EQ(2U, header.proto_ids_size_);
- EXPECT_EQ(200U, header.proto_ids_off_);
- EXPECT_EQ(1U, header.field_ids_size_);
- EXPECT_EQ(224U, header.field_ids_off_);
- EXPECT_EQ(3U, header.method_ids_size_);
- EXPECT_EQ(232U, header.method_ids_off_);
- EXPECT_EQ(2U, header.class_defs_size_);
- EXPECT_EQ(256U, header.class_defs_off_);
- EXPECT_EQ(584U, header.data_size_);
- EXPECT_EQ(320U, header.data_off_);
-
- EXPECT_EQ(header.checksum_, dex_file->GetLocationChecksum());
-}
-
-TEST_F(DexFileTest, Header) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
- ValidateDexFileHeader(std::move(raw));
-}
-
-TEST_F(DexFileTest, HeaderInMemory) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw =
- OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U, true);
- ValidateDexFileHeader(std::move(raw));
-}
-
-TEST_F(DexFileTest, Version38Accepted) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex38, tmp.GetFilename().c_str()));
- ASSERT_TRUE(raw.get() != nullptr);
-
- const DexFile::Header& header = raw->GetHeader();
- EXPECT_EQ(38u, header.GetVersion());
-}
-
-TEST_F(DexFileTest, Version39Accepted) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex39, tmp.GetFilename().c_str()));
- ASSERT_TRUE(raw.get() != nullptr);
-
- const DexFile::Header& header = raw->GetHeader();
- EXPECT_EQ(39u, header.GetVersion());
-}
-
-TEST_F(DexFileTest, Version40Rejected) {
- ScratchFile tmp;
- const char* location = tmp.GetFilename().c_str();
- DecodeAndWriteDexFile(kRawDex40, location);
-
- ScopedObjectAccess soa(Thread::Current());
- static constexpr bool kVerifyChecksum = true;
- std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- const ArtDexFileLoader dex_file_loader;
- ASSERT_FALSE(dex_file_loader.Open(
- location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files));
-}
-
-TEST_F(DexFileTest, Version41Rejected) {
- ScratchFile tmp;
- const char* location = tmp.GetFilename().c_str();
- DecodeAndWriteDexFile(kRawDex41, location);
-
- ScopedObjectAccess soa(Thread::Current());
- static constexpr bool kVerifyChecksum = true;
- std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- const ArtDexFileLoader dex_file_loader;
- ASSERT_FALSE(dex_file_loader.Open(
- location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files));
-}
-
-TEST_F(DexFileTest, ZeroLengthDexRejected) {
- ScratchFile tmp;
- const char* location = tmp.GetFilename().c_str();
- DecodeAndWriteDexFile(kRawDexZeroLength, location);
-
- ScopedObjectAccess soa(Thread::Current());
- static constexpr bool kVerifyChecksum = true;
- std::string error_msg;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- const ArtDexFileLoader dex_file_loader;
- ASSERT_FALSE(dex_file_loader.Open(
- location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files));
-}
-
-TEST_F(DexFileTest, GetLocationChecksum) {
- ScopedObjectAccess soa(Thread::Current());
- std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main"));
- EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum());
-}
-
-TEST_F(DexFileTest, GetChecksum) {
- std::vector<uint32_t> checksums;
- ScopedObjectAccess soa(Thread::Current());
- std::string error_msg;
- const ArtDexFileLoader dex_file_loader;
- EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(),
- &checksums,
- &error_msg))
- << error_msg;
- 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");
- const ArtDexFileLoader dex_file_loader;
- EXPECT_TRUE(dex_file_loader.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(), DexFileLoader::GetMultiDexLocation(0, multidex_file.c_str()));
- EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]);
-
- EXPECT_EQ(dexes[1]->GetLocation(), DexFileLoader::GetMultiDexLocation(1, multidex_file.c_str()));
- EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
-}
-
-TEST_F(DexFileTest, ClassDefs) {
- ScopedObjectAccess soa(Thread::Current());
- std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
- ASSERT_TRUE(raw.get() != nullptr);
- EXPECT_EQ(3U, raw->NumClassDefs());
-
- const DexFile::ClassDef& c0 = raw->GetClassDef(0);
- EXPECT_STREQ("LNested$1;", raw->GetClassDescriptor(c0));
-
- const DexFile::ClassDef& c1 = raw->GetClassDef(1);
- EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c1));
-
- const DexFile::ClassDef& c2 = raw->GetClassDef(2);
- EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c2));
-}
-
-TEST_F(DexFileTest, GetMethodSignature) {
- ScopedObjectAccess soa(Thread::Current());
- std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
- ASSERT_TRUE(raw.get() != nullptr);
- EXPECT_EQ(1U, raw->NumClassDefs());
-
- const DexFile::ClassDef& class_def = raw->GetClassDef(0);
- ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def));
-
- const uint8_t* class_data = raw->GetClassData(class_def);
- ASSERT_TRUE(class_data != nullptr);
- ClassDataItemIterator it(*raw, class_data);
-
- EXPECT_EQ(1u, it.NumDirectMethods());
-
- // Check the signature for the static initializer.
- {
- ASSERT_EQ(1U, it.NumDirectMethods());
- const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
- const char* name = raw->StringDataByIdx(method_id.name_idx_);
- ASSERT_STREQ("<init>", name);
- std::string signature(raw->GetMethodSignature(method_id).ToString());
- ASSERT_EQ("()V", signature);
- }
-
- // Check all virtual methods.
- struct Result {
- const char* name;
- const char* signature;
- const char* pretty_method;
- };
- static const Result results[] = {
- {
- "m1",
- "(IDJLjava/lang/Object;)Ljava/lang/Float;",
- "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)"
- },
- {
- "m2",
- "(ZSC)LGetMethodSignature;",
- "GetMethodSignature GetMethodSignature.m2(boolean, short, char)"
- },
- {
- "m3",
- "()V",
- "void GetMethodSignature.m3()"
- },
- {
- "m4",
- "(I)V",
- "void GetMethodSignature.m4(int)"
- },
- {
- "m5",
- "(II)V",
- "void GetMethodSignature.m5(int, int)"
- },
- {
- "m6",
- "(II[[I)V",
- "void GetMethodSignature.m6(int, int, int[][])"
- },
- {
- "m7",
- "(II[[ILjava/lang/Object;)V",
- "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)"
- },
- {
- "m8",
- "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V",
- "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])"
- },
- {
- "m9",
- "()I",
- "int GetMethodSignature.m9()"
- },
- {
- "mA",
- "()[[I",
- "int[][] GetMethodSignature.mA()"
- },
- {
- "mB",
- "()[[Ljava/lang/Object;",
- "java.lang.Object[][] GetMethodSignature.mB()"
- },
- };
- ASSERT_EQ(arraysize(results), it.NumVirtualMethods());
- for (const Result& r : results) {
- it.Next();
- const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
-
- const char* name = raw->StringDataByIdx(method_id.name_idx_);
- ASSERT_STREQ(r.name, name);
-
- std::string signature(raw->GetMethodSignature(method_id).ToString());
- ASSERT_EQ(r.signature, signature);
-
- std::string plain_method = std::string("GetMethodSignature.") + r.name;
- ASSERT_EQ(plain_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ false));
- ASSERT_EQ(r.pretty_method, raw->PrettyMethod(it.GetMemberIndex(), /* with_signature */ true));
- }
-}
-
-TEST_F(DexFileTest, FindStringId) {
- ScopedObjectAccess soa(Thread::Current());
- std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
- ASSERT_TRUE(raw.get() != nullptr);
- EXPECT_EQ(1U, raw->NumClassDefs());
-
- const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
- "D", "I", "J", nullptr };
- for (size_t i = 0; strings[i] != nullptr; i++) {
- const char* str = strings[i];
- const DexFile::StringId* str_id = raw->FindStringId(str);
- const char* dex_str = raw->GetStringData(*str_id);
- EXPECT_STREQ(dex_str, str);
- }
-}
-
-TEST_F(DexFileTest, FindTypeId) {
- for (size_t i = 0; i < java_lang_dex_file_->NumTypeIds(); i++) {
- const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i));
- const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str);
- ASSERT_TRUE(type_str_id != nullptr);
- dex::StringIndex type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id);
- const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx);
- ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str));
- ASSERT_TRUE(type_id != nullptr);
- EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id).index_, i);
- }
-}
-
-TEST_F(DexFileTest, FindProtoId) {
- for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) {
- const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i);
- const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find);
- std::vector<dex::TypeIndex> to_find_types;
- if (to_find_tl != nullptr) {
- for (size_t j = 0; j < to_find_tl->Size(); j++) {
- to_find_types.push_back(to_find_tl->GetTypeItem(j).type_idx_);
- }
- }
- const DexFile::ProtoId* found =
- java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types);
- ASSERT_TRUE(found != nullptr);
- EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i);
- }
-}
-
-TEST_F(DexFileTest, FindMethodId) {
- for (size_t i = 0; i < java_lang_dex_file_->NumMethodIds(); i++) {
- const DexFile::MethodId& to_find = java_lang_dex_file_->GetMethodId(i);
- const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_);
- const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_);
- const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_);
- const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature);
- ASSERT_TRUE(found != nullptr) << "Didn't find method " << i << ": "
- << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "."
- << java_lang_dex_file_->GetStringData(name)
- << java_lang_dex_file_->GetMethodSignature(to_find);
- EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i);
- }
-}
-
-TEST_F(DexFileTest, FindFieldId) {
- for (size_t i = 0; i < java_lang_dex_file_->NumFieldIds(); i++) {
- const DexFile::FieldId& to_find = java_lang_dex_file_->GetFieldId(i);
- const DexFile::TypeId& klass = java_lang_dex_file_->GetTypeId(to_find.class_idx_);
- const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_);
- const DexFile::TypeId& type = java_lang_dex_file_->GetTypeId(to_find.type_idx_);
- const DexFile::FieldId* found = java_lang_dex_file_->FindFieldId(klass, name, type);
- ASSERT_TRUE(found != nullptr) << "Didn't find field " << i << ": "
- << java_lang_dex_file_->StringByTypeIdx(to_find.type_idx_) << " "
- << java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "."
- << java_lang_dex_file_->GetStringData(name);
- EXPECT_EQ(java_lang_dex_file_->GetIndexForFieldId(*found), i);
- }
-}
-
-TEST_F(DexFileTest, GetMultiDexClassesDexName) {
- ASSERT_EQ("classes.dex", DexFileLoader::GetMultiDexClassesDexName(0));
- ASSERT_EQ("classes2.dex", DexFileLoader::GetMultiDexClassesDexName(1));
- ASSERT_EQ("classes3.dex", DexFileLoader::GetMultiDexClassesDexName(2));
- ASSERT_EQ("classes100.dex", DexFileLoader::GetMultiDexClassesDexName(99));
-}
-
-TEST_F(DexFileTest, GetMultiDexLocation) {
- std::string dex_location_str = "/system/app/framework.jar";
- const char* dex_location = dex_location_str.c_str();
- ASSERT_EQ("/system/app/framework.jar", DexFileLoader::GetMultiDexLocation(0, dex_location));
- ASSERT_EQ("/system/app/framework.jar!classes2.dex",
- DexFileLoader::GetMultiDexLocation(1, dex_location));
- ASSERT_EQ("/system/app/framework.jar!classes101.dex",
- DexFileLoader::GetMultiDexLocation(100, dex_location));
-}
-
-TEST_F(DexFileTest, GetDexCanonicalLocation) {
- ScratchFile file;
- UniqueCPtr<const char[]> dex_location_real(realpath(file.GetFilename().c_str(), nullptr));
- std::string dex_location(dex_location_real.get());
-
- ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location.c_str()));
- std::string multidex_location = DexFileLoader::GetMultiDexLocation(1, dex_location.c_str());
- ASSERT_EQ(multidex_location, DexFileLoader::GetDexCanonicalLocation(multidex_location.c_str()));
-
- std::string dex_location_sym = dex_location + "symlink";
- ASSERT_EQ(0, symlink(dex_location.c_str(), dex_location_sym.c_str()));
-
- ASSERT_EQ(dex_location, DexFileLoader::GetDexCanonicalLocation(dex_location_sym.c_str()));
-
- std::string multidex_location_sym = DexFileLoader::GetMultiDexLocation(
- 1, dex_location_sym.c_str());
- ASSERT_EQ(multidex_location,
- DexFileLoader::GetDexCanonicalLocation(multidex_location_sym.c_str()));
-
- ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
-}
-
-TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) {
- EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar"));
- EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes2.dex"));
- EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes8.dex"));
- EXPECT_EQ("", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar"));
- EXPECT_EQ("!classes2.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes2.dex"));
- EXPECT_EQ("!classes8.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes8.dex"));
-}
-
-TEST_F(DexFileTest, ZipOpenClassesPresent) {
- ScratchFile tmp;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- std::string error_msg;
- ASSERT_TRUE(OpenDexFilesBase64(kRawZipClassesDexPresent, tmp.GetFilename().c_str(), &dex_files,
- &error_msg));
- EXPECT_EQ(dex_files.size(), 1u);
-}
-
-TEST_F(DexFileTest, ZipOpenClassesAbsent) {
- ScratchFile tmp;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- std::string error_msg;
- ASSERT_FALSE(OpenDexFilesBase64(kRawZipClassesDexAbsent, tmp.GetFilename().c_str(), &dex_files,
- &error_msg));
- EXPECT_EQ(dex_files.size(), 0u);
-}
-
-TEST_F(DexFileTest, ZipOpenThreeDexFiles) {
- ScratchFile tmp;
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- std::string error_msg;
- ASSERT_TRUE(OpenDexFilesBase64(kRawZipThreeDexFiles, tmp.GetFilename().c_str(), &dex_files,
- &error_msg));
- EXPECT_EQ(dex_files.size(), 3u);
-}
-
-TEST_F(DexFileTest, OpenDexBadMapOffset) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw =
- OpenDexFileInMemoryBase64(kRawDexBadMapOffset, tmp.GetFilename().c_str(), 0xb3642819U, false);
- EXPECT_EQ(raw, nullptr);
-}
-
-TEST_F(DexFileTest, GetStringWithNoIndex) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
- dex::TypeIndex idx;
- EXPECT_EQ(raw->StringByTypeIdx(idx), nullptr);
-}
-
-static void Callback(void* context ATTRIBUTE_UNUSED,
- const DexFile::LocalInfo& entry ATTRIBUTE_UNUSED) {
-}
-
-TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) {
- ScratchFile tmp;
- std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64(
- kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true);
- const DexFile::ClassDef& class_def = raw->GetClassDef(0);
- constexpr uint32_t kMethodIdx = 1;
- const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def,
- kMethodIdx));
- CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx);
- ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr));
-}
-
-} // namespace art
diff --git a/runtime/dex/dex_file_tracking_registrar.cc b/runtime/dex/dex_file_tracking_registrar.cc
deleted file mode 100644
index 78ea9c16cb..0000000000
--- a/runtime/dex/dex_file_tracking_registrar.cc
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * 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 "dex_file_tracking_registrar.h"
-
-#include <deque>
-#include <tuple>
-
-#include <android-base/logging.h>
-
-// For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for
-// the ifdefs and early include.
-#ifdef ART_DEX_FILE_ACCESS_TRACKING
-#ifndef ART_ENABLE_ADDRESS_SANITIZER
-#define ART_ENABLE_ADDRESS_SANITIZER
-#endif
-#endif
-#include "base/memory_tool.h"
-
-#include "code_item_accessors-inl.h"
-#include "dex_file-inl.h"
-
-namespace art {
-namespace dex {
-namespace tracking {
-
-// If true, poison dex files to track accesses.
-static constexpr bool kDexFileAccessTracking =
-#ifdef ART_DEX_FILE_ACCESS_TRACKING
- true;
-#else
- false;
-#endif
-
-// The following are configurations of poisoning certain sections of a Dex File.
-// More will be added
-enum DexTrackingType {
- // Poisons all of a Dex File when set.
- kWholeDexTracking,
- // Poisons all Code Items of a Dex File when set.
- kCodeItemTracking,
- // Poisons all subsections of a Code Item, except the Insns bytecode array
- // section, when set for all Code Items in a Dex File.
- kCodeItemNonInsnsTracking,
- // Poisons all subsections of a Code Item, except the Insns bytecode array
- // section, when set for all Code Items in a Dex File.
- // Additionally unpoisons the entire Code Item when method is a class
- // initializer.
- kCodeItemNonInsnsNoClinitTracking,
- // Poisons the size and offset information along with the first instruction.
- // This is so that accessing multiple instructions while accessing a code item
- // once will not trigger unnecessary accesses.
- kCodeItemStartTracking,
- // Poisons all String Data Items of a Dex Files when set.
- kStringDataItemTracking,
- // Poisons the first byte of the utf16_size value and the first byte of the
- // data section for all String Data Items of a Dex File.
- kStringDataItemStartTracking,
- // Poisons based on a custom tracking system which can be specified in
- // SetDexSections
- kCustomTracking,
-};
-
-// Intended for local changes only.
-// Represents the current configuration being run.
-static constexpr DexTrackingType kCurrentTrackingSystem = kWholeDexTracking;
-
-// Intended for local changes only.
-void DexFileTrackingRegistrar::SetDexSections() {
- if (kDexFileAccessTracking && dex_file_ != nullptr) {
- // Logs the Dex File's location and starting address if tracking is enabled
- LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex
- << reinterpret_cast<uintptr_t>(dex_file_->Begin());
- switch (kCurrentTrackingSystem) {
- case kWholeDexTracking:
- SetDexFileRegistration(true);
- break;
- case kCodeItemTracking:
- SetAllCodeItemRegistration(true);
- break;
- case kCodeItemNonInsnsTracking:
- SetAllCodeItemRegistration(true);
- SetAllInsnsRegistration(false);
- break;
- case kCodeItemNonInsnsNoClinitTracking:
- SetAllCodeItemRegistration(true);
- SetAllInsnsRegistration(false);
- SetCodeItemRegistration("<clinit>", false);
- break;
- case kCodeItemStartTracking:
- SetAllCodeItemStartRegistration(true);
- break;
- case kStringDataItemTracking:
- SetAllStringDataRegistration(true);
- break;
- case kStringDataItemStartTracking:
- SetAllStringDataStartRegistration(true);
- break;
- case kCustomTracking:
- // TODO: Add/remove additional calls here to (un)poison sections of
- // dex_file_
- break;
- default:
- break;
- }
- }
-}
-
-void RegisterDexFile(const DexFile* dex_file) {
- DexFileTrackingRegistrar dex_tracking_registrar(dex_file);
- dex_tracking_registrar.SetDexSections();
- dex_tracking_registrar.SetCurrentRanges();
-}
-
-inline void SetRegistrationRange(const void* begin, size_t size, bool should_poison) {
- if (should_poison) {
- MEMORY_TOOL_MAKE_NOACCESS(begin, size);
- } else {
- // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
- // Sanitizer. The difference has not been tested with Valgrind
- MEMORY_TOOL_MAKE_DEFINED(begin, size);
- }
-}
-
-void DexFileTrackingRegistrar::SetCurrentRanges() {
- // This also empties range_values_ to avoid redundant (un)poisoning upon
- // subsequent calls.
- while (!range_values_.empty()) {
- const std::tuple<const void*, size_t, bool>& current_range = range_values_.front();
- SetRegistrationRange(std::get<0>(current_range),
- std::get<1>(current_range),
- std::get<2>(current_range));
- range_values_.pop_front();
- }
-}
-
-void DexFileTrackingRegistrar::SetDexFileRegistration(bool should_poison) {
- const void* dex_file_begin = reinterpret_cast<const void*>(dex_file_->Begin());
- size_t dex_file_size = dex_file_->Size();
- range_values_.push_back(std::make_tuple(dex_file_begin, dex_file_size, should_poison));
-}
-
-void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) {
- for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
- const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
- const uint8_t* class_data = dex_file_->GetClassData(cd);
- if (class_data != nullptr) {
- ClassDataItemIterator cdit(*dex_file_, class_data);
- cdit.SkipAllFields();
- while (cdit.HasNextMethod()) {
- const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
- if (code_item != nullptr) {
- const void* code_item_begin = reinterpret_cast<const void*>(code_item);
- size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
- range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
- }
- cdit.Next();
- }
- }
- }
-}
-
-void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) {
- for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
- const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
- const uint8_t* class_data = dex_file_->GetClassData(cd);
- if (class_data != nullptr) {
- ClassDataItemIterator cdit(*dex_file_, class_data);
- cdit.SkipAllFields();
- while (cdit.HasNextMethod()) {
- const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
- if (code_item != nullptr) {
- const void* code_item_begin = reinterpret_cast<const void*>(code_item);
- size_t code_item_start = reinterpret_cast<size_t>(code_item);
- CodeItemInstructionAccessor accessor(*dex_file_, code_item);
- size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns());
- size_t code_item_start_size = code_item_start_end - code_item_start;
- range_values_.push_back(std::make_tuple(code_item_begin,
- code_item_start_size,
- should_poison));
- }
- cdit.Next();
- }
- }
- }
-}
-
-void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) {
- for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
- const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
- const uint8_t* class_data = dex_file_->GetClassData(cd);
- if (class_data != nullptr) {
- ClassDataItemIterator cdit(*dex_file_, class_data);
- cdit.SkipAllFields();
- while (cdit.HasNextMethod()) {
- const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
- if (code_item != nullptr) {
- CodeItemInstructionAccessor accessor(*dex_file_, code_item);
- const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns());
- // Member insns_size_in_code_units_ is in 2-byte units
- size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2;
- range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison));
- }
- cdit.Next();
- }
- }
- }
-}
-
-void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, bool should_poison) {
- for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
- const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
- const uint8_t* class_data = dex_file_->GetClassData(cd);
- if (class_data != nullptr) {
- ClassDataItemIterator cdit(*dex_file_, class_data);
- cdit.SkipAllFields();
- while (cdit.HasNextMethod()) {
- const DexFile::MethodId& methodid_item = dex_file_->GetMethodId(cdit.GetMemberIndex());
- const char * methodid_name = dex_file_->GetMethodName(methodid_item);
- const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
- if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) {
- const void* code_item_begin = reinterpret_cast<const void*>(code_item);
- size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
- range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
- }
- cdit.Next();
- }
- }
- }
-}
-
-void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) {
- for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) {
- const DexFile::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr));
- const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_);
- // Data Section of String Data Item
- const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id));
- range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison));
- range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison));
- }
-}
-
-void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) {
- size_t map_offset = dex_file_->GetHeader().map_off_;
- auto map_list = reinterpret_cast<const DexFile::MapList*>(dex_file_->Begin() + map_offset);
- for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) {
- const DexFile::MapItem& map_item = map_list->list_[map_ctr];
- if (map_item.type_ == DexFile::kDexTypeStringDataItem) {
- const DexFile::MapItem& next_map_item = map_list->list_[map_ctr + 1];
- const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_);
- size_t string_data_size = next_map_item.offset_ - map_item.offset_;
- range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison));
- }
- }
-}
-
-} // namespace tracking
-} // namespace dex
-} // namespace art
diff --git a/runtime/dex/dex_file_tracking_registrar.h b/runtime/dex/dex_file_tracking_registrar.h
deleted file mode 100644
index 71b8ed7bde..0000000000
--- a/runtime/dex/dex_file_tracking_registrar.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
-#define ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
-
-#include <deque>
-#include <tuple>
-
-#include "dex_file.h"
-
-namespace art {
-namespace dex {
-namespace tracking {
-
-// Class for (un)poisoning various sections of Dex Files
-//
-// This class provides the means to log accesses only of sections whose
-// accesses are needed. All accesses are displayed as stack traces in
-// logcat.
-class DexFileTrackingRegistrar {
- public:
- explicit DexFileTrackingRegistrar(const DexFile* const dex_file)
- : dex_file_(dex_file) {
- }
-
- // This function is where the functions below it are called to actually
- // poison sections.
- void SetDexSections();
-
- // Uses data contained inside range_values_ to poison memory through the
- // memory tool.
- void SetCurrentRanges();
-
- private:
- void SetDexFileRegistration(bool should_poison);
-
- // Set of functions concerning Code Items of dex_file_
- void SetAllCodeItemRegistration(bool should_poison);
- // Sets the insns_ section of all code items.
- void SetAllInsnsRegistration(bool should_poison);
- // This function finds the code item of a class based on class name.
- void SetCodeItemRegistration(const char* class_name, bool should_poison);
- // Sets the size and offset information along with first instruction in insns_
- // section of all code items.
- void SetAllCodeItemStartRegistration(bool should_poison);
-
- // Set of functions concerning String Data Items of dex_file_
- void SetAllStringDataRegistration(bool should_poison);
- // Sets the first byte of size value and data section of all string data
- // items.
- void SetAllStringDataStartRegistration(bool should_poison);
-
- // Contains tuples of all ranges of memory that need to be explicitly
- // (un)poisoned by the memory tool.
- std::deque<std::tuple<const void *, size_t, bool>> range_values_;
-
- const DexFile* const dex_file_;
-};
-
-// This function is meant to called externally to use DexfileTrackingRegistrar
-void RegisterDexFile(const DexFile* dex_file);
-
-} // namespace tracking
-} // namespace dex
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
diff --git a/runtime/dex/dex_file_types.h b/runtime/dex/dex_file_types.h
deleted file mode 100644
index 2c508f9c99..0000000000
--- a/runtime/dex/dex_file_types.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_DEX_DEX_FILE_TYPES_H_
-#define ART_RUNTIME_DEX_DEX_FILE_TYPES_H_
-
-#include <limits>
-#include <ostream>
-
-namespace art {
-namespace dex {
-
-constexpr uint32_t kDexNoIndex = 0xFFFFFFFF;
-
-class StringIndex {
- public:
- uint32_t index_;
-
- constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
- explicit constexpr StringIndex(uint32_t idx) : index_(idx) {}
-
- bool IsValid() const {
- return index_ != std::numeric_limits<decltype(index_)>::max();
- }
- static StringIndex Invalid() {
- return StringIndex(std::numeric_limits<decltype(index_)>::max());
- }
-
- bool operator==(const StringIndex& other) const {
- return index_ == other.index_;
- }
- bool operator!=(const StringIndex& other) const {
- return index_ != other.index_;
- }
- bool operator<(const StringIndex& other) const {
- return index_ < other.index_;
- }
- bool operator<=(const StringIndex& other) const {
- return index_ <= other.index_;
- }
- bool operator>(const StringIndex& other) const {
- return index_ > other.index_;
- }
- bool operator>=(const StringIndex& other) const {
- return index_ >= other.index_;
- }
-};
-std::ostream& operator<<(std::ostream& os, const StringIndex& index);
-
-class TypeIndex {
- public:
- uint16_t index_;
-
- constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
- explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {}
-
- bool IsValid() const {
- return index_ != std::numeric_limits<decltype(index_)>::max();
- }
- static TypeIndex Invalid() {
- return TypeIndex(std::numeric_limits<decltype(index_)>::max());
- }
-
- bool operator==(const TypeIndex& other) const {
- return index_ == other.index_;
- }
- bool operator!=(const TypeIndex& other) const {
- return index_ != other.index_;
- }
- bool operator<(const TypeIndex& other) const {
- return index_ < other.index_;
- }
- bool operator<=(const TypeIndex& other) const {
- return index_ <= other.index_;
- }
- bool operator>(const TypeIndex& other) const {
- return index_ > other.index_;
- }
- bool operator>=(const TypeIndex& other) const {
- return index_ >= other.index_;
- }
-};
-std::ostream& operator<<(std::ostream& os, const TypeIndex& index);
-
-} // namespace dex
-} // namespace art
-
-namespace std {
-
-template<> struct hash<art::dex::StringIndex> {
- size_t operator()(const art::dex::StringIndex& index) const {
- return hash<uint32_t>()(index.index_);
- }
-};
-
-template<> struct hash<art::dex::TypeIndex> {
- size_t operator()(const art::dex::TypeIndex& index) const {
- return hash<uint16_t>()(index.index_);
- }
-};
-
-} // namespace std
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_TYPES_H_
diff --git a/runtime/dex/dex_file_verifier.cc b/runtime/dex/dex_file_verifier.cc
deleted file mode 100644
index 62667052ad..0000000000
--- a/runtime/dex/dex_file_verifier.cc
+++ /dev/null
@@ -1,3290 +0,0 @@
-/*
- * Copyright (C) 2011 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 "dex_file_verifier.h"
-
-#include <inttypes.h>
-
-#include <limits>
-#include <memory>
-
-#include "android-base/stringprintf.h"
-
-#include "code_item_accessors-inl.h"
-#include "descriptors_names.h"
-#include "dex_file-inl.h"
-#include "leb128.h"
-#include "modifiers.h"
-#include "utf-inl.h"
-
-namespace art {
-
-using android::base::StringAppendV;
-using android::base::StringPrintf;
-
-static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max();
-
-static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) {
- return (high == 0) || ((high == 0xffffU) && (low == 0xffffU));
-}
-
-static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) {
- return (high == 0);
-}
-
-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;
- case DexFile::kDexTypeProtoIdItem: return 1 << 3;
- case DexFile::kDexTypeFieldIdItem: return 1 << 4;
- case DexFile::kDexTypeMethodIdItem: return 1 << 5;
- case DexFile::kDexTypeClassDefItem: return 1 << 6;
- 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(DexFile::MapItemType map_item_type) {
- switch (map_item_type) {
- case DexFile::kDexTypeHeaderItem:
- case DexFile::kDexTypeStringIdItem:
- case DexFile::kDexTypeTypeIdItem:
- case DexFile::kDexTypeProtoIdItem:
- case DexFile::kDexTypeFieldIdItem:
- 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;
-}
-
-const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const char* error_string) {
- if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumStringIds(), error_string))) {
- return nullptr;
- }
- return dex_file_->StringDataByIdx(idx);
-}
-
-// Try to find the name of the method with the given index. We do not want to rely on DexFile
-// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
-// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
-// (flagged by the return value), otherwise error_msg will contain an error string.
-static bool FindMethodName(uint32_t method_index,
- const uint8_t* begin,
- const DexFile::Header* header,
- const char** str,
- std::string* error_msg) {
- if (method_index >= header->method_ids_size_) {
- *error_msg = "Method index not available for method flags verification";
- return false;
- }
- uint32_t string_idx =
- (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
- method_index)->name_idx_.index_;
- if (string_idx >= header->string_ids_size_) {
- *error_msg = "String index not available for method flags verification";
- return false;
- }
- uint32_t string_off =
- (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
- string_data_off_;
- if (string_off >= header->file_size_) {
- *error_msg = "String offset out of bounds for method flags verification";
- return false;
- }
- const uint8_t* str_data_ptr = begin + string_off;
- uint32_t dummy;
- if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) {
- *error_msg = "String size out of bounds for method flags verification";
- return false;
- }
- *str = reinterpret_cast<const char*>(str_data_ptr);
- return true;
-}
-
-// Gets constructor flags based on the |method_name|. Returns true if
-// method_name is either <clinit> or <init> and sets
-// |constructor_flags_by_name| appropriately. Otherwise set
-// |constructor_flags_by_name| to zero and returns whether
-// |method_name| is valid.
-bool GetConstructorFlagsForMethodName(const char* method_name,
- uint32_t* constructor_flags_by_name) {
- if (method_name[0] != '<') {
- *constructor_flags_by_name = 0;
- return true;
- }
- if (strcmp(method_name + 1, "clinit>") == 0) {
- *constructor_flags_by_name = kAccStatic | kAccConstructor;
- return true;
- }
- if (strcmp(method_name + 1, "init>") == 0) {
- *constructor_flags_by_name = kAccConstructor;
- return true;
- }
- *constructor_flags_by_name = 0;
- return false;
-}
-
-const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx,
- const char* error_string) {
- if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) {
- return nullptr;
- }
- return CheckLoadStringByIdx(dex_file_->GetTypeId(type_idx).descriptor_idx_, error_string);
-}
-
-const DexFile::FieldId* DexFileVerifier::CheckLoadFieldId(uint32_t idx, const char* error_string) {
- if (UNLIKELY(!CheckIndex(idx, dex_file_->NumFieldIds(), error_string))) {
- return nullptr;
- }
- return &dex_file_->GetFieldId(idx);
-}
-
-const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const char* err_string) {
- if (UNLIKELY(!CheckIndex(idx, dex_file_->NumMethodIds(), err_string))) {
- return nullptr;
- }
- return &dex_file_->GetMethodId(idx);
-}
-
-const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) {
- if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) {
- return nullptr;
- }
- return &dex_file_->GetProtoId(idx);
-}
-
-// Helper macro to load string and return false on error.
-#define LOAD_STRING(var, idx, error) \
- const char* (var) = CheckLoadStringByIdx(idx, error); \
- if (UNLIKELY((var) == nullptr)) { \
- return false; \
- }
-
-// Helper macro to load string by type idx and return false on error.
-#define LOAD_STRING_BY_TYPE(var, type_idx, error) \
- const char* (var) = CheckLoadStringByTypeIdx(type_idx, error); \
- if (UNLIKELY((var) == nullptr)) { \
- return false; \
- }
-
-// Helper macro to load method id. Return last parameter on error.
-#define LOAD_METHOD(var, idx, error_string, error_stmt) \
- const DexFile::MethodId* (var) = CheckLoadMethodId(idx, error_string); \
- if (UNLIKELY((var) == nullptr)) { \
- error_stmt; \
- }
-
-// Helper macro to load method id. Return last parameter on error.
-#define LOAD_FIELD(var, idx, fmt, error_stmt) \
- const DexFile::FieldId* (var) = CheckLoadFieldId(idx, fmt); \
- if (UNLIKELY((var) == nullptr)) { \
- error_stmt; \
- }
-
-bool DexFileVerifier::Verify(const DexFile* dex_file,
- const uint8_t* begin,
- size_t size,
- const char* location,
- bool verify_checksum,
- std::string* error_msg) {
- std::unique_ptr<DexFileVerifier> verifier(
- new DexFileVerifier(dex_file, begin, size, location, verify_checksum));
- if (!verifier->Verify()) {
- *error_msg = verifier->FailureReason();
- return false;
- }
- return true;
-}
-
-bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
- bool is_return_type) {
- switch (shorty_char) {
- case 'V':
- if (UNLIKELY(!is_return_type)) {
- ErrorStringPrintf("Invalid use of void");
- return false;
- }
- FALLTHROUGH_INTENDED;
- case 'B':
- case 'C':
- case 'D':
- case 'F':
- case 'I':
- case 'J':
- case 'S':
- case 'Z':
- if (UNLIKELY((descriptor[0] != shorty_char) || (descriptor[1] != '\0'))) {
- ErrorStringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'",
- shorty_char, descriptor);
- return false;
- }
- break;
- case 'L':
- if (UNLIKELY((descriptor[0] != 'L') && (descriptor[0] != '['))) {
- ErrorStringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor);
- return false;
- }
- break;
- default:
- ErrorStringPrintf("Bad shorty character: '%c'", shorty_char);
- return false;
- }
- return true;
-}
-
-bool DexFileVerifier::CheckListSize(const void* start, size_t count, size_t elem_size,
- const char* label) {
- // Check that size is not 0.
- CHECK_NE(elem_size, 0U);
-
- const uint8_t* range_start = reinterpret_cast<const uint8_t*>(start);
- const uint8_t* file_start = reinterpret_cast<const uint8_t*>(begin_);
-
- // Check for overflow.
- uintptr_t max = 0 - 1;
- size_t available_bytes_till_end_of_mem = max - reinterpret_cast<uintptr_t>(start);
- size_t max_count = available_bytes_till_end_of_mem / elem_size;
- if (max_count < count) {
- ErrorStringPrintf("Overflow in range for %s: %zx for %zu@%zu", label,
- static_cast<size_t>(range_start - file_start),
- count, elem_size);
- return false;
- }
-
- const uint8_t* range_end = range_start + count * elem_size;
- const uint8_t* file_end = file_start + size_;
- if (UNLIKELY((range_start < file_start) || (range_end > file_end))) {
- // Note: these two tests are enough as we make sure above that there's no overflow.
- ErrorStringPrintf("Bad range for %s: %zx to %zx", label,
- static_cast<size_t>(range_start - file_start),
- static_cast<size_t>(range_end - file_start));
- return false;
- }
- return true;
-}
-
-bool DexFileVerifier::CheckList(size_t element_size, const char* label, const uint8_t* *ptr) {
- // Check that the list is available. The first 4B are the count.
- if (!CheckListSize(*ptr, 1, 4U, label)) {
- return false;
- }
-
- uint32_t count = *reinterpret_cast<const uint32_t*>(*ptr);
- if (count > 0) {
- if (!CheckListSize(*ptr + 4, count, element_size, label)) {
- return false;
- }
- }
-
- *ptr += 4 + count * element_size;
- return true;
-}
-
-bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
- if (UNLIKELY(field >= limit)) {
- ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
- return false;
- }
- return true;
-}
-
-bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset,
- uint32_t size,
- size_t alignment,
- const char* label) {
- if (size == 0) {
- if (offset != 0) {
- ErrorStringPrintf("Offset(%d) should be zero when size is zero for %s.", offset, label);
- return false;
- }
- }
- if (size_ <= offset) {
- ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label);
- return false;
- }
- if (alignment != 0 && !IsAlignedParam(offset, alignment)) {
- ErrorStringPrintf("Offset(%d) should be aligned by %zu for %s.", offset, alignment, label);
- return false;
- }
- return true;
-}
-
-bool DexFileVerifier::CheckSizeLimit(uint32_t size, uint32_t limit, const char* label) {
- if (size > limit) {
- ErrorStringPrintf("Size(%u) should not exceed limit(%u) for %s.", size, limit, label);
- return false;
- }
- return true;
-}
-
-bool DexFileVerifier::CheckHeader() {
- // Check file size from the header.
- uint32_t expected_size = header_->file_size_;
- if (size_ != expected_size) {
- ErrorStringPrintf("Bad file size (%zd, expected %u)", size_, expected_size);
- return false;
- }
-
- uint32_t adler_checksum = dex_file_->CalculateChecksum();
- // Compute and verify the checksum in the header.
- if (adler_checksum != header_->checksum_) {
- if (verify_checksum_) {
- ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
- return false;
- } else {
- LOG(WARNING) << StringPrintf(
- "Ignoring bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
- }
- }
-
- // Check the contents of the header.
- if (header_->endian_tag_ != DexFile::kDexEndianConstant) {
- ErrorStringPrintf("Unexpected endian_tag: %x", header_->endian_tag_);
- return false;
- }
-
- const uint32_t expected_header_size = dex_file_->IsCompactDexFile()
- ? sizeof(CompactDexFile::Header)
- : sizeof(StandardDexFile::Header);
-
- if (header_->header_size_ != expected_header_size) {
- ErrorStringPrintf("Bad header size: %ud expected %ud",
- header_->header_size_,
- expected_header_size);
- return false;
- }
-
- // Check that all offsets are inside the file.
- bool result =
- CheckValidOffsetAndSize(header_->link_off_,
- header_->link_size_,
- 0 /* unaligned */,
- "link") &&
- CheckValidOffsetAndSize(header_->map_off_,
- header_->map_off_,
- 4,
- "map") &&
- CheckValidOffsetAndSize(header_->string_ids_off_,
- header_->string_ids_size_,
- 4,
- "string-ids") &&
- CheckValidOffsetAndSize(header_->type_ids_off_,
- header_->type_ids_size_,
- 4,
- "type-ids") &&
- CheckSizeLimit(header_->type_ids_size_, DexFile::kDexNoIndex16, "type-ids") &&
- CheckValidOffsetAndSize(header_->proto_ids_off_,
- header_->proto_ids_size_,
- 4,
- "proto-ids") &&
- CheckSizeLimit(header_->proto_ids_size_, DexFile::kDexNoIndex16, "proto-ids") &&
- CheckValidOffsetAndSize(header_->field_ids_off_,
- header_->field_ids_size_,
- 4,
- "field-ids") &&
- CheckValidOffsetAndSize(header_->method_ids_off_,
- header_->method_ids_size_,
- 4,
- "method-ids") &&
- CheckValidOffsetAndSize(header_->class_defs_off_,
- header_->class_defs_size_,
- 4,
- "class-defs") &&
- CheckValidOffsetAndSize(header_->data_off_,
- header_->data_size_,
- 0, // Unaligned, spec doesn't talk about it, even though size
- // is supposed to be a multiple of 4.
- "data");
- return result;
-}
-
-bool DexFileVerifier::CheckMap() {
- const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ +
- header_->map_off_);
- // Check that map list content is available.
- if (!CheckListSize(map, 1, sizeof(DexFile::MapList), "maplist content")) {
- return false;
- }
-
- const DexFile::MapItem* item = map->list_;
-
- uint32_t count = map->size_;
- uint32_t last_offset = 0;
- uint32_t last_type = 0;
- uint32_t data_item_count = 0;
- uint32_t data_items_left = header_->data_size_;
- uint32_t used_bits = 0;
-
- // Sanity check the size of the map list.
- if (!CheckListSize(item, count, sizeof(DexFile::MapItem), "map size")) {
- return false;
- }
-
- // Check the items listed in the map.
- for (uint32_t i = 0; i < count; i++) {
- if (UNLIKELY(last_offset >= item->offset_ && i != 0)) {
- ErrorStringPrintf("Out of order map item: %x then %x for type %x last type was %x",
- last_offset,
- item->offset_,
- static_cast<uint32_t>(item->type_),
- last_type);
- return false;
- }
- if (UNLIKELY(item->offset_ >= header_->file_size_)) {
- ErrorStringPrintf("Map item after end of file: %x, size %x",
- item->offset_, header_->file_size_);
- return false;
- }
-
- 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 item_type %zx",
- data_item_count + icount,
- static_cast<size_t>(item_type));
- return false;
- }
- data_items_left -= icount;
- data_item_count += icount;
- }
-
- uint32_t bit = MapTypeToBitMask(item_type);
-
- if (UNLIKELY(bit == 0)) {
- ErrorStringPrintf("Unknown map section type %x", item->type_);
- return false;
- }
-
- if (UNLIKELY((used_bits & bit) != 0)) {
- ErrorStringPrintf("Duplicate map section of type %x", item->type_);
- return false;
- }
-
- used_bits |= bit;
- last_offset = item->offset_;
- last_type = item->type_;
- item++;
- }
-
- // Check for missing sections in the map.
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0)) {
- ErrorStringPrintf("Map is missing header entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0)) {
- ErrorStringPrintf("Map is missing map_list entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 &&
- ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0)))) {
- ErrorStringPrintf("Map is missing string_ids entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 &&
- ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0)))) {
- ErrorStringPrintf("Map is missing type_ids entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 &&
- ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0)))) {
- ErrorStringPrintf("Map is missing proto_ids entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 &&
- ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0)))) {
- ErrorStringPrintf("Map is missing field_ids entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 &&
- ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0)))) {
- ErrorStringPrintf("Map is missing method_ids entry");
- return false;
- }
- if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 &&
- ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0)))) {
- ErrorStringPrintf("Map is missing class_defs entry");
- return false;
- }
- return true;
-}
-
-uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) {
- uint32_t result = 0;
- if (LIKELY(CheckListSize(ptr_, size, sizeof(uint8_t), "encoded_value"))) {
- for (uint32_t i = 0; i < size; i++) {
- result |= ((uint32_t) *(ptr_++)) << (i * 8);
- }
- }
- return result;
-}
-
-
-#define DECODE_UNSIGNED_CHECKED_FROM_WITH_ERROR_VALUE(ptr, var, error_value) \
- uint32_t var; \
- if (!DecodeUnsignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \
- return error_value; \
- }
-
-#define DECODE_UNSIGNED_CHECKED_FROM(ptr, var) \
- uint32_t var; \
- if (!DecodeUnsignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \
- ErrorStringPrintf("Read out of bounds"); \
- return false; \
- }
-
-#define DECODE_SIGNED_CHECKED_FROM(ptr, var) \
- int32_t var; \
- if (!DecodeSignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \
- ErrorStringPrintf("Read out of bounds"); \
- return false; \
- }
-
-bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
- uint32_t* handler_offsets, uint32_t handlers_size) {
- CodeItemDataAccessor accessor(*dex_file_, code_item);
- const uint8_t* handlers_base = accessor.GetCatchHandlerData();
-
- for (uint32_t i = 0; i < handlers_size; i++) {
- bool catch_all;
- size_t offset = ptr_ - handlers_base;
- DECODE_SIGNED_CHECKED_FROM(ptr_, size);
-
- if (UNLIKELY((size < -65536) || (size > 65536))) {
- ErrorStringPrintf("Invalid exception handler size: %d", size);
- return false;
- }
-
- if (size <= 0) {
- catch_all = true;
- size = -size;
- } else {
- catch_all = false;
- }
-
- handler_offsets[i] = static_cast<uint32_t>(offset);
-
- while (size-- > 0) {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
- if (!CheckIndex(type_idx, header_->type_ids_size_, "handler type_idx")) {
- return false;
- }
-
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr);
- if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) {
- ErrorStringPrintf("Invalid handler addr: %x", addr);
- return false;
- }
- }
-
- if (catch_all) {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr);
- if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) {
- ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr);
- return false;
- }
- }
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckClassDataItemField(uint32_t idx,
- uint32_t access_flags,
- uint32_t class_access_flags,
- dex::TypeIndex class_type_index,
- bool expect_static) {
- // Check for overflow.
- if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
- return false;
- }
-
- // Check that it's the right class.
- dex::TypeIndex my_class_index =
- (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)->
- class_idx_;
- if (class_type_index != my_class_index) {
- ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16,
- my_class_index.index_,
- class_type_index.index_);
- return false;
- }
-
- // Check that it falls into the right class-data list.
- bool is_static = (access_flags & kAccStatic) != 0;
- if (UNLIKELY(is_static != expect_static)) {
- ErrorStringPrintf("Static/instance field not in expected list");
- return false;
- }
-
- // Check field access flags.
- std::string error_msg;
- if (!CheckFieldAccessFlags(idx, access_flags, class_access_flags, &error_msg)) {
- ErrorStringPrintf("%s", error_msg.c_str());
- return false;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx,
- uint32_t access_flags,
- uint32_t class_access_flags,
- dex::TypeIndex class_type_index,
- uint32_t code_offset,
- std::unordered_set<uint32_t>* direct_method_indexes,
- bool expect_direct) {
- DCHECK(direct_method_indexes != nullptr);
- // Check for overflow.
- if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
- return false;
- }
-
- // Check that it's the right class.
- dex::TypeIndex my_class_index =
- (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)->
- class_idx_;
- if (class_type_index != my_class_index) {
- ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16,
- my_class_index.index_,
- class_type_index.index_);
- return false;
- }
-
- // Check that it's not defined as both direct and virtual.
- if (expect_direct) {
- direct_method_indexes->insert(idx);
- } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) {
- ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
- return false;
- }
-
- std::string error_msg;
- const char* method_name;
- if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) {
- ErrorStringPrintf("%s", error_msg.c_str());
- return false;
- }
-
- uint32_t constructor_flags_by_name = 0;
- if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) {
- ErrorStringPrintf("Bad method name: %s", method_name);
- return false;
- }
-
- bool has_code = (code_offset != 0);
- if (!CheckMethodAccessFlags(idx,
- access_flags,
- class_access_flags,
- constructor_flags_by_name,
- has_code,
- expect_direct,
- &error_msg)) {
- ErrorStringPrintf("%s", error_msg.c_str());
- return false;
- }
-
- if (constructor_flags_by_name != 0) {
- if (!CheckConstructorProperties(idx, constructor_flags_by_name)) {
- DCHECK(FailureReasonIsSet());
- return false;
- }
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckPadding(size_t offset,
- uint32_t aligned_offset,
- DexFile::MapItemType type) {
- if (offset < aligned_offset) {
- if (!CheckListSize(begin_ + offset, aligned_offset - offset, sizeof(uint8_t), "section")) {
- return false;
- }
- while (offset < aligned_offset) {
- if (UNLIKELY(*ptr_ != '\0')) {
- ErrorStringPrintf("Non-zero padding %x before section of type %zu at offset 0x%zx",
- *ptr_,
- static_cast<size_t>(type),
- offset);
- return false;
- }
- ptr_++;
- offset++;
- }
- }
- return true;
-}
-
-bool DexFileVerifier::CheckEncodedValue() {
- if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "encoded_value header")) {
- return false;
- }
-
- uint8_t header_byte = *(ptr_++);
- uint32_t value_type = header_byte & DexFile::kDexAnnotationValueTypeMask;
- uint32_t value_arg = header_byte >> DexFile::kDexAnnotationValueArgShift;
-
- switch (value_type) {
- case DexFile::kDexAnnotationByte:
- if (UNLIKELY(value_arg != 0)) {
- ErrorStringPrintf("Bad encoded_value byte size %x", value_arg);
- return false;
- }
- ptr_++;
- break;
- case DexFile::kDexAnnotationShort:
- case DexFile::kDexAnnotationChar:
- if (UNLIKELY(value_arg > 1)) {
- ErrorStringPrintf("Bad encoded_value char/short size %x", value_arg);
- return false;
- }
- ptr_ += value_arg + 1;
- break;
- case DexFile::kDexAnnotationInt:
- case DexFile::kDexAnnotationFloat:
- if (UNLIKELY(value_arg > 3)) {
- ErrorStringPrintf("Bad encoded_value int/float size %x", value_arg);
- return false;
- }
- ptr_ += value_arg + 1;
- break;
- case DexFile::kDexAnnotationLong:
- case DexFile::kDexAnnotationDouble:
- ptr_ += value_arg + 1;
- break;
- case DexFile::kDexAnnotationString: {
- if (UNLIKELY(value_arg > 3)) {
- ErrorStringPrintf("Bad encoded_value string size %x", value_arg);
- return false;
- }
- uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
- if (!CheckIndex(idx, header_->string_ids_size_, "encoded_value string")) {
- return false;
- }
- break;
- }
- case DexFile::kDexAnnotationType: {
- if (UNLIKELY(value_arg > 3)) {
- ErrorStringPrintf("Bad encoded_value type size %x", value_arg);
- return false;
- }
- uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
- if (!CheckIndex(idx, header_->type_ids_size_, "encoded_value type")) {
- return false;
- }
- break;
- }
- case DexFile::kDexAnnotationField:
- case DexFile::kDexAnnotationEnum: {
- if (UNLIKELY(value_arg > 3)) {
- ErrorStringPrintf("Bad encoded_value field/enum size %x", value_arg);
- return false;
- }
- uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
- if (!CheckIndex(idx, header_->field_ids_size_, "encoded_value field")) {
- return false;
- }
- break;
- }
- case DexFile::kDexAnnotationMethod: {
- if (UNLIKELY(value_arg > 3)) {
- ErrorStringPrintf("Bad encoded_value method size %x", value_arg);
- return false;
- }
- uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
- if (!CheckIndex(idx, header_->method_ids_size_, "encoded_value method")) {
- return false;
- }
- break;
- }
- case DexFile::kDexAnnotationArray:
- if (UNLIKELY(value_arg != 0)) {
- ErrorStringPrintf("Bad encoded_value array value_arg %x", value_arg);
- return false;
- }
- if (!CheckEncodedArray()) {
- return false;
- }
- break;
- case DexFile::kDexAnnotationAnnotation:
- if (UNLIKELY(value_arg != 0)) {
- ErrorStringPrintf("Bad encoded_value annotation value_arg %x", value_arg);
- return false;
- }
- if (!CheckEncodedAnnotation()) {
- return false;
- }
- break;
- case DexFile::kDexAnnotationNull:
- if (UNLIKELY(value_arg != 0)) {
- ErrorStringPrintf("Bad encoded_value null value_arg %x", value_arg);
- return false;
- }
- break;
- case DexFile::kDexAnnotationBoolean:
- if (UNLIKELY(value_arg > 1)) {
- ErrorStringPrintf("Bad encoded_value boolean size %x", value_arg);
- 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;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckEncodedArray() {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
-
- while (size--) {
- if (!CheckEncodedValue()) {
- failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str());
- return false;
- }
- }
- return true;
-}
-
-bool DexFileVerifier::CheckEncodedAnnotation() {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, anno_idx);
- if (!CheckIndex(anno_idx, header_->type_ids_size_, "encoded_annotation type_idx")) {
- return false;
- }
-
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
- uint32_t last_idx = 0;
-
- for (uint32_t i = 0; i < size; i++) {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, idx);
- if (!CheckIndex(idx, header_->string_ids_size_, "annotation_element name_idx")) {
- return false;
- }
-
- if (UNLIKELY(last_idx >= idx && i != 0)) {
- ErrorStringPrintf("Out-of-order annotation_element name_idx: %x then %x",
- last_idx, idx);
- return false;
- }
-
- if (!CheckEncodedValue()) {
- return false;
- }
-
- last_idx = idx;
- }
- return true;
-}
-
-bool DexFileVerifier::FindClassIndexAndDef(uint32_t index,
- bool is_field,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** output_class_def) {
- DCHECK(class_type_index != nullptr);
- DCHECK(output_class_def != nullptr);
-
- // First check if the index is valid.
- if (index >= (is_field ? header_->field_ids_size_ : header_->method_ids_size_)) {
- return false;
- }
-
- // Next get the type index.
- if (is_field) {
- *class_type_index =
- (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + index)->
- class_idx_;
- } else {
- *class_type_index =
- (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + index)->
- class_idx_;
- }
-
- // Check if that is valid.
- if (class_type_index->index_ >= header_->type_ids_size_) {
- return false;
- }
-
- // Now search for the class def. This is basically a specialized version of the DexFile code, as
- // we should not trust that this is a valid DexFile just yet.
- const DexFile::ClassDef* class_def_begin =
- reinterpret_cast<const DexFile::ClassDef*>(begin_ + header_->class_defs_off_);
- for (size_t i = 0; i < header_->class_defs_size_; ++i) {
- const DexFile::ClassDef* class_def = class_def_begin + i;
- if (class_def->class_idx_ == *class_type_index) {
- *output_class_def = class_def;
- return true;
- }
- }
-
- // Didn't find the class-def, not defined here...
- return false;
-}
-
-bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field,
- const char* type_descr,
- uint32_t curr_index,
- uint32_t prev_index,
- bool* have_class,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** class_def) {
- if (curr_index < prev_index) {
- ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32,
- type_descr,
- prev_index,
- curr_index);
- return false;
- }
-
- if (!*have_class) {
- *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def);
- if (!*have_class) {
- // Should have really found one.
- ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
- type_descr,
- curr_index);
- return false;
- }
- }
- return true;
-}
-
-bool DexFileVerifier::CheckStaticFieldTypes(const DexFile::ClassDef* class_def) {
- if (class_def == nullptr) {
- return true;
- }
-
- ClassDataItemIterator field_it(*dex_file_, ptr_);
- EncodedStaticFieldValueIterator array_it(*dex_file_, *class_def);
-
- for (; field_it.HasNextStaticField() && array_it.HasNext(); field_it.Next(), array_it.Next()) {
- uint32_t index = field_it.GetMemberIndex();
- const DexFile::TypeId& type_id = dex_file_->GetTypeId(dex_file_->GetFieldId(index).type_idx_);
- const char* field_type_name =
- dex_file_->GetStringData(dex_file_->GetStringId(type_id.descriptor_idx_));
- Primitive::Type field_type = Primitive::GetType(field_type_name[0]);
- EncodedArrayValueIterator::ValueType array_type = array_it.GetValueType();
- // Ensure this matches RuntimeEncodedStaticFieldValueIterator.
- switch (array_type) {
- case EncodedArrayValueIterator::ValueType::kBoolean:
- if (field_type != Primitive::kPrimBoolean) {
- ErrorStringPrintf("unexpected static field initial value type: 'Z' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kByte:
- if (field_type != Primitive::kPrimByte) {
- ErrorStringPrintf("unexpected static field initial value type: 'B' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kShort:
- if (field_type != Primitive::kPrimShort) {
- ErrorStringPrintf("unexpected static field initial value type: 'S' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kChar:
- if (field_type != Primitive::kPrimChar) {
- ErrorStringPrintf("unexpected static field initial value type: 'C' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kInt:
- if (field_type != Primitive::kPrimInt) {
- ErrorStringPrintf("unexpected static field initial value type: 'I' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kLong:
- if (field_type != Primitive::kPrimLong) {
- ErrorStringPrintf("unexpected static field initial value type: 'J' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kFloat:
- if (field_type != Primitive::kPrimFloat) {
- ErrorStringPrintf("unexpected static field initial value type: 'F' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kDouble:
- if (field_type != Primitive::kPrimDouble) {
- ErrorStringPrintf("unexpected static field initial value type: 'D' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- case EncodedArrayValueIterator::ValueType::kNull:
- case EncodedArrayValueIterator::ValueType::kString:
- case EncodedArrayValueIterator::ValueType::kType:
- if (field_type != Primitive::kPrimNot) {
- ErrorStringPrintf("unexpected static field initial value type: 'L' vs '%c'",
- field_type_name[0]);
- return false;
- }
- break;
- default:
- ErrorStringPrintf("unexpected static field initial value type: %x", array_type);
- return false;
- }
- }
-
- if (array_it.HasNext()) {
- ErrorStringPrintf("too many static field initial values");
- return false;
- }
- return true;
-}
-
-template <bool kStatic>
-bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it,
- bool* have_class,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** class_def) {
- DCHECK(it != nullptr);
- // These calls use the raw access flags to check whether the whole dex field is valid.
- uint32_t prev_index = 0;
- for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) {
- uint32_t curr_index = it->GetMemberIndex();
- if (!CheckOrderAndGetClassDef(true,
- kStatic ? "static field" : "instance field",
- curr_index,
- prev_index,
- have_class,
- class_type_index,
- class_def)) {
- return false;
- }
- DCHECK(class_def != nullptr);
- if (!CheckClassDataItemField(curr_index,
- it->GetRawMemberAccessFlags(),
- (*class_def)->access_flags_,
- *class_type_index,
- kStatic)) {
- return false;
- }
-
- prev_index = curr_index;
- }
-
- return true;
-}
-
-template <bool kDirect>
-bool DexFileVerifier::CheckIntraClassDataItemMethods(
- ClassDataItemIterator* it,
- std::unordered_set<uint32_t>* direct_method_indexes,
- bool* have_class,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** class_def) {
- uint32_t prev_index = 0;
- for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) {
- uint32_t curr_index = it->GetMemberIndex();
- if (!CheckOrderAndGetClassDef(false,
- kDirect ? "direct method" : "virtual method",
- curr_index,
- prev_index,
- have_class,
- class_type_index,
- class_def)) {
- return false;
- }
- DCHECK(class_def != nullptr);
- if (!CheckClassDataItemMethod(curr_index,
- it->GetRawMemberAccessFlags(),
- (*class_def)->access_flags_,
- *class_type_index,
- it->GetMethodCodeItemOffset(),
- direct_method_indexes,
- kDirect)) {
- return false;
- }
-
- prev_index = curr_index;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckIntraClassDataItem() {
- ClassDataItemIterator it(*dex_file_, ptr_);
- std::unordered_set<uint32_t> direct_method_indexes;
-
- // This code is complicated by the fact that we don't directly know which class this belongs to.
- // So we need to explicitly search with the first item we find (either field or method), and then,
- // as the lookup is expensive, cache the result.
- bool have_class = false;
- dex::TypeIndex class_type_index;
- const DexFile::ClassDef* class_def = nullptr;
-
- // Check fields.
- if (!CheckIntraClassDataItemFields<true>(&it,
- &have_class,
- &class_type_index,
- &class_def)) {
- return false;
- }
- if (!CheckIntraClassDataItemFields<false>(&it,
- &have_class,
- &class_type_index,
- &class_def)) {
- return false;
- }
-
- // Check methods.
- if (!CheckIntraClassDataItemMethods<true>(&it,
- &direct_method_indexes,
- &have_class,
- &class_type_index,
- &class_def)) {
- return false;
- }
- if (!CheckIntraClassDataItemMethods<false>(&it,
- &direct_method_indexes,
- &have_class,
- &class_type_index,
- &class_def)) {
- return false;
- }
-
- const uint8_t* end_ptr = it.EndDataPointer();
-
- // Check static field types against initial static values in encoded array.
- if (!CheckStaticFieldTypes(class_def)) {
- return false;
- }
-
- ptr_ = end_ptr;
- return true;
-}
-
-bool DexFileVerifier::CheckIntraCodeItem() {
- const DexFile::CodeItem* code_item = reinterpret_cast<const DexFile::CodeItem*>(ptr_);
- if (!CheckListSize(code_item, 1, sizeof(DexFile::CodeItem), "code")) {
- return false;
- }
-
- CodeItemDataAccessor accessor(*dex_file_, code_item);
- if (UNLIKELY(accessor.InsSize() > accessor.RegistersSize())) {
- ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)",
- accessor.InsSize(), accessor.RegistersSize());
- return false;
- }
-
- if (UNLIKELY(accessor.OutsSize() > 5 && accessor.OutsSize() > accessor.RegistersSize())) {
- /*
- * outs_size can be up to 5, even if registers_size is smaller, since the
- * short forms of method invocation allow repetitions of a register multiple
- * times within a single parameter list. However, longer parameter lists
- * need to be represented in-order in the register file.
- */
- ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)",
- accessor.OutsSize(), accessor.RegistersSize());
- return false;
- }
-
- const uint16_t* insns = accessor.Insns();
- uint32_t insns_size = accessor.InsnsSizeInCodeUnits();
- if (!CheckListSize(insns, insns_size, sizeof(uint16_t), "insns size")) {
- return false;
- }
-
- // Grab the end of the insns if there are no try_items.
- uint32_t try_items_size = accessor.TriesSize();
- if (try_items_size == 0) {
- ptr_ = reinterpret_cast<const uint8_t*>(&insns[insns_size]);
- return true;
- }
-
- // try_items are 4-byte aligned. Verify the spacer is 0.
- if (((reinterpret_cast<uintptr_t>(&insns[insns_size]) & 3) != 0) && (insns[insns_size] != 0)) {
- ErrorStringPrintf("Non-zero padding: %x", insns[insns_size]);
- return false;
- }
-
- const DexFile::TryItem* try_items = accessor.TryItems().begin();
- if (!CheckListSize(try_items, try_items_size, sizeof(DexFile::TryItem), "try_items size")) {
- return false;
- }
-
- ptr_ = accessor.GetCatchHandlerData();
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, handlers_size);
-
- if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) {
- ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size);
- return false;
- }
-
- std::unique_ptr<uint32_t[]> handler_offsets(new uint32_t[handlers_size]);
- if (!CheckAndGetHandlerOffsets(code_item, &handler_offsets[0], handlers_size)) {
- return false;
- }
-
- uint32_t last_addr = 0;
- while (try_items_size--) {
- if (UNLIKELY(try_items->start_addr_ < last_addr)) {
- ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_);
- return false;
- }
-
- if (UNLIKELY(try_items->start_addr_ >= insns_size)) {
- ErrorStringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_);
- return false;
- }
-
- uint32_t i;
- for (i = 0; i < handlers_size; i++) {
- if (try_items->handler_off_ == handler_offsets[i]) {
- break;
- }
- }
-
- if (UNLIKELY(i == handlers_size)) {
- ErrorStringPrintf("Bogus handler offset: %x", try_items->handler_off_);
- return false;
- }
-
- last_addr = try_items->start_addr_ + try_items->insn_count_;
- if (UNLIKELY(last_addr > insns_size)) {
- ErrorStringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_);
- return false;
- }
-
- try_items++;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckIntraStringDataItem() {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
- const uint8_t* file_end = begin_ + size_;
-
- for (uint32_t i = 0; i < size; i++) {
- CHECK_LT(i, size); // b/15014252 Prevents hitting the impossible case below
- if (UNLIKELY(ptr_ >= file_end)) {
- ErrorStringPrintf("String data would go beyond end-of-file");
- return false;
- }
-
- uint8_t byte = *(ptr_++);
-
- // Switch on the high 4 bits.
- switch (byte >> 4) {
- case 0x00:
- // Special case of bit pattern 0xxx.
- if (UNLIKELY(byte == 0)) {
- CHECK_LT(i, size); // b/15014252 Actually hit this impossible case with clang
- ErrorStringPrintf("String data shorter than indicated utf16_size %x", size);
- return false;
- }
- break;
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- // No extra checks necessary for bit pattern 0xxx.
- break;
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0f:
- // Illegal bit patterns 10xx or 1111.
- // Note: 1111 is valid for normal UTF-8, but not here.
- ErrorStringPrintf("Illegal start byte %x in string data", byte);
- return false;
- case 0x0c:
- case 0x0d: {
- // Bit pattern 110x has an additional byte.
- uint8_t byte2 = *(ptr_++);
- if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
- ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
- return false;
- }
- uint16_t value = ((byte & 0x1f) << 6) | (byte2 & 0x3f);
- if (UNLIKELY((value != 0) && (value < 0x80))) {
- ErrorStringPrintf("Illegal representation for value %x in string data", value);
- return false;
- }
- break;
- }
- case 0x0e: {
- // Bit pattern 1110 has 2 additional bytes.
- uint8_t byte2 = *(ptr_++);
- if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
- ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
- return false;
- }
- uint8_t byte3 = *(ptr_++);
- if (UNLIKELY((byte3 & 0xc0) != 0x80)) {
- ErrorStringPrintf("Illegal continuation byte %x in string data", byte3);
- return false;
- }
- uint16_t value = ((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f);
- if (UNLIKELY(value < 0x800)) {
- ErrorStringPrintf("Illegal representation for value %x in string data", value);
- return false;
- }
- break;
- }
- }
- }
-
- if (UNLIKELY(*(ptr_++) != '\0')) {
- ErrorStringPrintf("String longer than indicated size %x", size);
- return false;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckIntraDebugInfoItem() {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, dummy);
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameters_size);
- if (UNLIKELY(parameters_size > 65536)) {
- ErrorStringPrintf("Invalid parameters_size: %x", parameters_size);
- return false;
- }
-
- for (uint32_t j = 0; j < parameters_size; j++) {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameter_name);
- if (parameter_name != 0) {
- parameter_name--;
- if (!CheckIndex(parameter_name, header_->string_ids_size_, "debug_info_item parameter_name")) {
- return false;
- }
- }
- }
-
- while (true) {
- uint8_t opcode = *(ptr_++);
- switch (opcode) {
- case DexFile::DBG_END_SEQUENCE: {
- return true;
- }
- case DexFile::DBG_ADVANCE_PC: {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, advance_pc_dummy);
- break;
- }
- case DexFile::DBG_ADVANCE_LINE: {
- DECODE_SIGNED_CHECKED_FROM(ptr_, advance_line_dummy);
- break;
- }
- case DexFile::DBG_START_LOCAL: {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
- if (UNLIKELY(reg_num >= 65536)) {
- ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
- return false;
- }
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
- if (name_idx != 0) {
- name_idx--;
- if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL name_idx")) {
- return false;
- }
- }
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
- if (type_idx != 0) {
- type_idx--;
- if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL type_idx")) {
- return false;
- }
- }
- break;
- }
- case DexFile::DBG_END_LOCAL:
- case DexFile::DBG_RESTART_LOCAL: {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
- if (UNLIKELY(reg_num >= 65536)) {
- ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
- return false;
- }
- break;
- }
- case DexFile::DBG_START_LOCAL_EXTENDED: {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
- if (UNLIKELY(reg_num >= 65536)) {
- ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
- return false;
- }
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
- if (name_idx != 0) {
- name_idx--;
- if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED name_idx")) {
- return false;
- }
- }
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
- if (type_idx != 0) {
- type_idx--;
- if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL_EXTENDED type_idx")) {
- return false;
- }
- }
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, sig_idx);
- if (sig_idx != 0) {
- sig_idx--;
- if (!CheckIndex(sig_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED sig_idx")) {
- return false;
- }
- }
- break;
- }
- case DexFile::DBG_SET_FILE: {
- DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
- if (name_idx != 0) {
- name_idx--;
- if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_SET_FILE name_idx")) {
- return false;
- }
- }
- break;
- }
- }
- }
-}
-
-bool DexFileVerifier::CheckIntraAnnotationItem() {
- if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "annotation visibility")) {
- return false;
- }
-
- // Check visibility
- switch (*(ptr_++)) {
- case DexFile::kDexVisibilityBuild:
- case DexFile::kDexVisibilityRuntime:
- case DexFile::kDexVisibilitySystem:
- break;
- default:
- ErrorStringPrintf("Bad annotation visibility: %x", *ptr_);
- return false;
- }
-
- if (!CheckEncodedAnnotation()) {
- return false;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
- const DexFile::AnnotationsDirectoryItem* item =
- reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
- if (!CheckListSize(item, 1, sizeof(DexFile::AnnotationsDirectoryItem), "annotations_directory")) {
- return false;
- }
-
- // Field annotations follow immediately after the annotations directory.
- const DexFile::FieldAnnotationsItem* field_item =
- reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1);
- uint32_t field_count = item->fields_size_;
- if (!CheckListSize(field_item, field_count, sizeof(DexFile::FieldAnnotationsItem), "field_annotations list")) {
- return false;
- }
-
- uint32_t last_idx = 0;
- for (uint32_t i = 0; i < field_count; i++) {
- if (UNLIKELY(last_idx >= field_item->field_idx_ && i != 0)) {
- ErrorStringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_);
- return false;
- }
- last_idx = field_item->field_idx_;
- field_item++;
- }
-
- // Method annotations follow immediately after field annotations.
- const DexFile::MethodAnnotationsItem* method_item =
- reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item);
- uint32_t method_count = item->methods_size_;
- if (!CheckListSize(method_item, method_count, sizeof(DexFile::MethodAnnotationsItem), "method_annotations list")) {
- return false;
- }
-
- last_idx = 0;
- for (uint32_t i = 0; i < method_count; i++) {
- if (UNLIKELY(last_idx >= method_item->method_idx_ && i != 0)) {
- ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
- last_idx, method_item->method_idx_);
- return false;
- }
- last_idx = method_item->method_idx_;
- method_item++;
- }
-
- // Parameter annotations follow immediately after method annotations.
- const DexFile::ParameterAnnotationsItem* parameter_item =
- reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
- uint32_t parameter_count = item->parameters_size_;
- if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem),
- "parameter_annotations list")) {
- return false;
- }
-
- last_idx = 0;
- for (uint32_t i = 0; i < parameter_count; i++) {
- if (UNLIKELY(last_idx >= parameter_item->method_idx_ && i != 0)) {
- ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
- last_idx, parameter_item->method_idx_);
- return false;
- }
- last_idx = parameter_item->method_idx_;
- parameter_item++;
- }
-
- // Return a pointer to the end of the annotations.
- ptr_ = reinterpret_cast<const uint8_t*>(parameter_item);
- return true;
-}
-
-bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count,
- DexFile::MapItemType type) {
- // Get the right alignment mask for the type of section.
- size_t alignment_mask;
- switch (type) {
- case DexFile::kDexTypeClassDataItem:
- case DexFile::kDexTypeStringDataItem:
- case DexFile::kDexTypeDebugInfoItem:
- case DexFile::kDexTypeAnnotationItem:
- case DexFile::kDexTypeEncodedArrayItem:
- alignment_mask = sizeof(uint8_t) - 1;
- break;
- default:
- alignment_mask = sizeof(uint32_t) - 1;
- break;
- }
-
- // Iterate through the items in the section.
- for (uint32_t i = 0; i < section_count; i++) {
- size_t aligned_offset = (offset + alignment_mask) & ~alignment_mask;
-
- // Check the padding between items.
- if (!CheckPadding(offset, aligned_offset, type)) {
- return false;
- }
-
- // 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")) {
- return false;
- }
- ptr_ += sizeof(DexFile::StringId);
- break;
- }
- case DexFile::kDexTypeTypeIdItem: {
- if (!CheckListSize(ptr_, 1, sizeof(DexFile::TypeId), "type_ids")) {
- return false;
- }
- ptr_ += sizeof(DexFile::TypeId);
- break;
- }
- case DexFile::kDexTypeProtoIdItem: {
- if (!CheckListSize(ptr_, 1, sizeof(DexFile::ProtoId), "proto_ids")) {
- return false;
- }
- ptr_ += sizeof(DexFile::ProtoId);
- break;
- }
- case DexFile::kDexTypeFieldIdItem: {
- if (!CheckListSize(ptr_, 1, sizeof(DexFile::FieldId), "field_ids")) {
- return false;
- }
- ptr_ += sizeof(DexFile::FieldId);
- break;
- }
- case DexFile::kDexTypeMethodIdItem: {
- if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodId), "method_ids")) {
- return false;
- }
- ptr_ += sizeof(DexFile::MethodId);
- break;
- }
- case DexFile::kDexTypeClassDefItem: {
- if (!CheckListSize(ptr_, 1, sizeof(DexFile::ClassDef), "class_defs")) {
- return false;
- }
- 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;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationSetRefList: {
- if (!CheckList(sizeof(DexFile::AnnotationSetRefItem), "annotation_set_ref_list", &ptr_)) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationSetItem: {
- if (!CheckList(sizeof(uint32_t), "annotation_set_item", &ptr_)) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeClassDataItem: {
- if (!CheckIntraClassDataItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeCodeItem: {
- if (!CheckIntraCodeItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeStringDataItem: {
- if (!CheckIntraStringDataItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeDebugInfoItem: {
- if (!CheckIntraDebugInfoItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationItem: {
- if (!CheckIntraAnnotationItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeEncodedArrayItem: {
- if (!CheckEncodedArray()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationsDirectoryItem: {
- if (!CheckIntraAnnotationsDirectoryItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeHeaderItem:
- case DexFile::kDexTypeMapList:
- break;
- }
-
- if (start_ptr == ptr_) {
- ErrorStringPrintf("Unknown map item type %x", type);
- return false;
- }
-
- if (IsDataSectionType(type)) {
- if (aligned_offset == 0u) {
- ErrorStringPrintf("Item %d offset is 0", i);
- return false;
- }
- DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end());
- offset_to_type_map_.Insert(std::pair<uint32_t, uint16_t>(aligned_offset, type));
- }
-
- aligned_offset = ptr_ - begin_;
- if (UNLIKELY(aligned_offset > size_)) {
- ErrorStringPrintf("Item %d at ends out of bounds", i);
- return false;
- }
-
- offset = aligned_offset;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckIntraIdSection(size_t offset,
- uint32_t count,
- DexFile::MapItemType type) {
- uint32_t expected_offset;
- uint32_t expected_size;
-
- // Get the expected offset and size from the header.
- switch (type) {
- case DexFile::kDexTypeStringIdItem:
- expected_offset = header_->string_ids_off_;
- expected_size = header_->string_ids_size_;
- break;
- case DexFile::kDexTypeTypeIdItem:
- expected_offset = header_->type_ids_off_;
- expected_size = header_->type_ids_size_;
- break;
- case DexFile::kDexTypeProtoIdItem:
- expected_offset = header_->proto_ids_off_;
- expected_size = header_->proto_ids_size_;
- break;
- case DexFile::kDexTypeFieldIdItem:
- expected_offset = header_->field_ids_off_;
- expected_size = header_->field_ids_size_;
- break;
- case DexFile::kDexTypeMethodIdItem:
- expected_offset = header_->method_ids_off_;
- expected_size = header_->method_ids_size_;
- break;
- case DexFile::kDexTypeClassDefItem:
- expected_offset = header_->class_defs_off_;
- expected_size = header_->class_defs_size_;
- break;
- default:
- ErrorStringPrintf("Bad type for id section: %x", type);
- return false;
- }
-
- // Check that the offset and size are what were expected from the header.
- if (UNLIKELY(offset != expected_offset)) {
- ErrorStringPrintf("Bad offset for section: got %zx, expected %x", offset, expected_offset);
- return false;
- }
- if (UNLIKELY(count != expected_size)) {
- ErrorStringPrintf("Bad size for section: got %x, expected %x", count, expected_size);
- return false;
- }
-
- return CheckIntraSectionIterate(offset, count, 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_;
-
- // Sanity check the offset of the section.
- if (UNLIKELY((offset < data_start) || (offset > data_end))) {
- ErrorStringPrintf("Bad offset for data subsection: %zx", offset);
- return false;
- }
-
- if (!CheckIntraSectionIterate(offset, count, type)) {
- return false;
- }
-
- size_t next_offset = ptr_ - begin_;
- if (next_offset > data_end) {
- ErrorStringPrintf("Out-of-bounds end of data subsection: %zu data_off=%u data_size=%u",
- next_offset,
- header_->data_off_,
- header_->data_size_);
- return false;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckIntraSection() {
- const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
- const DexFile::MapItem* item = map->list_;
- 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_;
- DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
-
- // Check for padding and overlap between items.
- if (!CheckPadding(offset, section_offset, type)) {
- return false;
- } else if (UNLIKELY(offset > section_offset)) {
- ErrorStringPrintf("Section overlap or out-of-order map: %zx, %x", offset, section_offset);
- return false;
- }
-
- // Check each item based on its type.
- switch (type) {
- case DexFile::kDexTypeHeaderItem:
- if (UNLIKELY(section_count != 1)) {
- ErrorStringPrintf("Multiple header items");
- return false;
- }
- if (UNLIKELY(section_offset != 0)) {
- ErrorStringPrintf("Header at %x, not at start of file", section_offset);
- return false;
- }
- ptr_ = begin_ + header_->header_size_;
- offset = header_->header_size_;
- break;
- case DexFile::kDexTypeStringIdItem:
- case DexFile::kDexTypeTypeIdItem:
- case DexFile::kDexTypeProtoIdItem:
- case DexFile::kDexTypeFieldIdItem:
- case DexFile::kDexTypeMethodIdItem:
- case DexFile::kDexTypeClassDefItem:
- if (!CheckIntraIdSection(section_offset, section_count, type)) {
- return false;
- }
- offset = ptr_ - begin_;
- break;
- case DexFile::kDexTypeMapList:
- if (UNLIKELY(section_count != 1)) {
- ErrorStringPrintf("Multiple map list items");
- return false;
- }
- if (UNLIKELY(section_offset != header_->map_off_)) {
- ErrorStringPrintf("Map not at header-defined offset: %x, expected %x",
- section_offset, header_->map_off_);
- return false;
- }
- 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:
- case DexFile::kDexTypeClassDataItem:
- case DexFile::kDexTypeCodeItem:
- case DexFile::kDexTypeStringDataItem:
- case DexFile::kDexTypeDebugInfoItem:
- case DexFile::kDexTypeAnnotationItem:
- case DexFile::kDexTypeEncodedArrayItem:
- case DexFile::kDexTypeAnnotationsDirectoryItem:
- if (!CheckIntraDataSection(section_offset, section_count, type)) {
- return false;
- }
- offset = ptr_ - begin_;
- break;
- }
-
- if (offset == current_offset) {
- ErrorStringPrintf("Unknown map item type %x", type);
- return false;
- }
-
- item++;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckOffsetToTypeMap(size_t offset, uint16_t type) {
- DCHECK_NE(offset, 0u);
- auto it = offset_to_type_map_.Find(offset);
- if (UNLIKELY(it == offset_to_type_map_.end())) {
- ErrorStringPrintf("No data map entry found @ %zx; expected %x", offset, type);
- return false;
- }
- if (UNLIKELY(it->second != type)) {
- ErrorStringPrintf("Unexpected data map entry @ %zx; expected %x, found %x",
- offset, type, it->second);
- return false;
- }
- return true;
-}
-
-dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) {
- ClassDataItemIterator it(*dex_file_, ptr);
- *success = true;
-
- if (it.HasNextStaticField() || it.HasNextInstanceField()) {
- LOAD_FIELD(field, it.GetMemberIndex(), "first_class_data_definer field_id",
- *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
- return field->class_idx_;
- }
-
- if (it.HasNextMethod()) {
- LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id",
- *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
- return method->class_idx_;
- }
-
- return dex::TypeIndex(DexFile::kDexNoIndex16);
-}
-
-dex::TypeIndex DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr,
- bool* success) {
- const DexFile::AnnotationsDirectoryItem* item =
- reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr);
- *success = true;
-
- if (item->fields_size_ != 0) {
- DexFile::FieldAnnotationsItem* field_items = (DexFile::FieldAnnotationsItem*) (item + 1);
- LOAD_FIELD(field, field_items[0].field_idx_, "first_annotations_dir_definer field_id",
- *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
- return field->class_idx_;
- }
-
- if (item->methods_size_ != 0) {
- DexFile::MethodAnnotationsItem* method_items = (DexFile::MethodAnnotationsItem*) (item + 1);
- LOAD_METHOD(method, method_items[0].method_idx_, "first_annotations_dir_definer method id",
- *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
- return method->class_idx_;
- }
-
- if (item->parameters_size_ != 0) {
- DexFile::ParameterAnnotationsItem* parameter_items = (DexFile::ParameterAnnotationsItem*) (item + 1);
- LOAD_METHOD(method, parameter_items[0].method_idx_, "first_annotations_dir_definer method id",
- *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
- return method->class_idx_;
- }
-
- return dex::TypeIndex(DexFile::kDexNoIndex16);
-}
-
-bool DexFileVerifier::CheckInterStringIdItem() {
- const DexFile::StringId* item = reinterpret_cast<const DexFile::StringId*>(ptr_);
-
- // Check the map to make sure it has the right offset->type.
- if (!CheckOffsetToTypeMap(item->string_data_off_, DexFile::kDexTypeStringDataItem)) {
- return false;
- }
-
- // Check ordering between items.
- if (previous_item_ != nullptr) {
- const DexFile::StringId* prev_item = reinterpret_cast<const DexFile::StringId*>(previous_item_);
- const char* prev_str = dex_file_->GetStringData(*prev_item);
- const char* str = dex_file_->GetStringData(*item);
- if (UNLIKELY(CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0)) {
- ErrorStringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str);
- return false;
- }
- }
-
- ptr_ += sizeof(DexFile::StringId);
- return true;
-}
-
-bool DexFileVerifier::CheckInterTypeIdItem() {
- const DexFile::TypeId* item = reinterpret_cast<const DexFile::TypeId*>(ptr_);
-
- LOAD_STRING(descriptor, item->descriptor_idx_, "inter_type_id_item descriptor_idx")
-
- // Check that the descriptor is a valid type.
- if (UNLIKELY(!IsValidDescriptor(descriptor))) {
- ErrorStringPrintf("Invalid type descriptor: '%s'", descriptor);
- return false;
- }
-
- // Check ordering between items.
- if (previous_item_ != nullptr) {
- const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_);
- if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) {
- ErrorStringPrintf("Out-of-order type_ids: %x then %x",
- prev_item->descriptor_idx_.index_,
- item->descriptor_idx_.index_);
- return false;
- }
- }
-
- ptr_ += sizeof(DexFile::TypeId);
- return true;
-}
-
-bool DexFileVerifier::CheckInterProtoIdItem() {
- const DexFile::ProtoId* item = reinterpret_cast<const DexFile::ProtoId*>(ptr_);
-
- LOAD_STRING(shorty, item->shorty_idx_, "inter_proto_id_item shorty_idx")
-
- if (item->parameters_off_ != 0 &&
- !CheckOffsetToTypeMap(item->parameters_off_, DexFile::kDexTypeTypeList)) {
- return false;
- }
-
- // Check that return type is representable as a uint16_t;
- if (UNLIKELY(!IsValidOrNoTypeId(item->return_type_idx_.index_, item->pad_))) {
- ErrorStringPrintf("proto with return type idx outside uint16_t range '%x:%x'",
- item->pad_, item->return_type_idx_.index_);
- return false;
- }
- // Check the return type and advance the shorty.
- LOAD_STRING_BY_TYPE(return_type, item->return_type_idx_, "inter_proto_id_item return_type_idx")
- if (!CheckShortyDescriptorMatch(*shorty, return_type, true)) {
- return false;
- }
- shorty++;
-
- DexFileParameterIterator it(*dex_file_, *item);
- while (it.HasNext() && *shorty != '\0') {
- if (!CheckIndex(it.GetTypeIdx().index_,
- dex_file_->NumTypeIds(),
- "inter_proto_id_item shorty type_idx")) {
- return false;
- }
- const char* descriptor = it.GetDescriptor();
- if (!CheckShortyDescriptorMatch(*shorty, descriptor, false)) {
- return false;
- }
- it.Next();
- shorty++;
- }
- if (UNLIKELY(it.HasNext() || *shorty != '\0')) {
- ErrorStringPrintf("Mismatched length for parameters and shorty");
- return false;
- }
-
- // Check ordering between items. This relies on type_ids being in order.
- if (previous_item_ != nullptr) {
- const DexFile::ProtoId* prev = reinterpret_cast<const DexFile::ProtoId*>(previous_item_);
- if (UNLIKELY(prev->return_type_idx_ > item->return_type_idx_)) {
- ErrorStringPrintf("Out-of-order proto_id return types");
- return false;
- } else if (prev->return_type_idx_ == item->return_type_idx_) {
- DexFileParameterIterator curr_it(*dex_file_, *item);
- DexFileParameterIterator prev_it(*dex_file_, *prev);
-
- while (curr_it.HasNext() && prev_it.HasNext()) {
- dex::TypeIndex prev_idx = prev_it.GetTypeIdx();
- dex::TypeIndex curr_idx = curr_it.GetTypeIdx();
- DCHECK_NE(prev_idx, dex::TypeIndex(DexFile::kDexNoIndex16));
- DCHECK_NE(curr_idx, dex::TypeIndex(DexFile::kDexNoIndex16));
-
- if (prev_idx < curr_idx) {
- break;
- } else if (UNLIKELY(prev_idx > curr_idx)) {
- ErrorStringPrintf("Out-of-order proto_id arguments");
- return false;
- }
-
- prev_it.Next();
- curr_it.Next();
- }
- if (!curr_it.HasNext()) {
- // Either a duplicate ProtoId or a ProtoId with a shorter argument list follows
- // a ProtoId with a longer one. Both cases are forbidden by the specification.
- ErrorStringPrintf("Out-of-order proto_id arguments");
- return false;
- }
- }
- }
-
- ptr_ += sizeof(DexFile::ProtoId);
- return true;
-}
-
-bool DexFileVerifier::CheckInterFieldIdItem() {
- const DexFile::FieldId* item = reinterpret_cast<const DexFile::FieldId*>(ptr_);
-
- // Check that the class descriptor is valid.
- LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_field_id_item class_idx")
- if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor);
- return false;
- }
-
- // Check that the type descriptor is a valid field name.
- LOAD_STRING_BY_TYPE(type_descriptor, item->type_idx_, "inter_field_id_item type_idx")
- if (UNLIKELY(!IsValidDescriptor(type_descriptor) || type_descriptor[0] == 'V')) {
- ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", type_descriptor);
- return false;
- }
-
- // Check that the name is valid.
- LOAD_STRING(descriptor, item->name_idx_, "inter_field_id_item name_idx")
- if (UNLIKELY(!IsValidMemberName(descriptor))) {
- ErrorStringPrintf("Invalid field name: '%s'", descriptor);
- return false;
- }
-
- // Check ordering between items. This relies on the other sections being in order.
- if (previous_item_ != nullptr) {
- const DexFile::FieldId* prev_item = reinterpret_cast<const DexFile::FieldId*>(previous_item_);
- if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
- ErrorStringPrintf("Out-of-order field_ids");
- return false;
- } else if (prev_item->class_idx_ == item->class_idx_) {
- if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
- ErrorStringPrintf("Out-of-order field_ids");
- return false;
- } else if (prev_item->name_idx_ == item->name_idx_) {
- if (UNLIKELY(prev_item->type_idx_ >= item->type_idx_)) {
- ErrorStringPrintf("Out-of-order field_ids");
- return false;
- }
- }
- }
- }
-
- ptr_ += sizeof(DexFile::FieldId);
- return true;
-}
-
-bool DexFileVerifier::CheckInterMethodIdItem() {
- const DexFile::MethodId* item = reinterpret_cast<const DexFile::MethodId*>(ptr_);
-
- // Check that the class descriptor is a valid reference name.
- LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_method_id_item class_idx")
- if (UNLIKELY(!IsValidDescriptor(class_descriptor) || (class_descriptor[0] != 'L' &&
- class_descriptor[0] != '['))) {
- ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor);
- return false;
- }
-
- // Check that the name is valid.
- LOAD_STRING(descriptor, item->name_idx_, "inter_method_id_item name_idx")
- if (UNLIKELY(!IsValidMemberName(descriptor))) {
- ErrorStringPrintf("Invalid method name: '%s'", descriptor);
- return false;
- }
-
- // Check that the proto id is valid.
- if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(),
- "inter_method_id_item proto_idx"))) {
- return false;
- }
-
- // Check ordering between items. This relies on the other sections being in order.
- if (previous_item_ != nullptr) {
- const DexFile::MethodId* prev_item = reinterpret_cast<const DexFile::MethodId*>(previous_item_);
- if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
- ErrorStringPrintf("Out-of-order method_ids");
- return false;
- } else if (prev_item->class_idx_ == item->class_idx_) {
- if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
- ErrorStringPrintf("Out-of-order method_ids");
- return false;
- } else if (prev_item->name_idx_ == item->name_idx_) {
- if (UNLIKELY(prev_item->proto_idx_ >= item->proto_idx_)) {
- ErrorStringPrintf("Out-of-order method_ids");
- return false;
- }
- }
- }
- }
-
- ptr_ += sizeof(DexFile::MethodId);
- return true;
-}
-
-bool DexFileVerifier::CheckInterClassDefItem() {
- const DexFile::ClassDef* item = reinterpret_cast<const DexFile::ClassDef*>(ptr_);
-
- // Check that class_idx_ is representable as a uint16_t;
- if (UNLIKELY(!IsValidTypeId(item->class_idx_.index_, item->pad1_))) {
- ErrorStringPrintf("class with type idx outside uint16_t range '%x:%x'", item->pad1_,
- item->class_idx_.index_);
- return false;
- }
- // Check that superclass_idx_ is representable as a uint16_t;
- if (UNLIKELY(!IsValidOrNoTypeId(item->superclass_idx_.index_, item->pad2_))) {
- ErrorStringPrintf("class with superclass type idx outside uint16_t range '%x:%x'", item->pad2_,
- item->superclass_idx_.index_);
- return false;
- }
- // Check for duplicate class def.
- if (defined_classes_.find(item->class_idx_) != defined_classes_.end()) {
- ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_.index_);
- return false;
- }
- defined_classes_.insert(item->class_idx_);
-
- LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_class_def_item class_idx")
- if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid class descriptor: '%s'", class_descriptor);
- return false;
- }
-
- // Only allow non-runtime modifiers.
- if ((item->access_flags_ & ~kAccJavaFlagsMask) != 0) {
- ErrorStringPrintf("Invalid class flags: '%d'", item->access_flags_);
- return false;
- }
-
- if (item->interfaces_off_ != 0 &&
- !CheckOffsetToTypeMap(item->interfaces_off_, DexFile::kDexTypeTypeList)) {
- return false;
- }
- if (item->annotations_off_ != 0 &&
- !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationsDirectoryItem)) {
- return false;
- }
- if (item->class_data_off_ != 0 &&
- !CheckOffsetToTypeMap(item->class_data_off_, DexFile::kDexTypeClassDataItem)) {
- return false;
- }
- if (item->static_values_off_ != 0 &&
- !CheckOffsetToTypeMap(item->static_values_off_, DexFile::kDexTypeEncodedArrayItem)) {
- return false;
- }
-
- if (item->superclass_idx_.IsValid()) {
- if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) {
- // Check that a class does not inherit from itself directly (by having
- // the same type idx as its super class).
- if (UNLIKELY(item->superclass_idx_ == item->class_idx_)) {
- ErrorStringPrintf("Class with same type idx as its superclass: '%d'",
- item->class_idx_.index_);
- return false;
- }
-
- // Check that a class is defined after its super class (if the
- // latter is defined in the same Dex file).
- const DexFile::ClassDef* superclass_def = dex_file_->FindClassDef(item->superclass_idx_);
- if (superclass_def != nullptr) {
- // The superclass is defined in this Dex file.
- if (superclass_def > item) {
- // ClassDef item for super class appearing after the class' ClassDef item.
- ErrorStringPrintf("Invalid class definition ordering:"
- " class with type idx: '%d' defined before"
- " superclass with type idx: '%d'",
- item->class_idx_.index_,
- item->superclass_idx_.index_);
- return false;
- }
- }
- }
-
- LOAD_STRING_BY_TYPE(superclass_descriptor, item->superclass_idx_,
- "inter_class_def_item superclass_idx")
- if (UNLIKELY(!IsValidDescriptor(superclass_descriptor) || superclass_descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid superclass: '%s'", superclass_descriptor);
- return false;
- }
- }
-
- // Check interfaces.
- const DexFile::TypeList* interfaces = dex_file_->GetInterfacesList(*item);
- if (interfaces != nullptr) {
- uint32_t size = interfaces->Size();
- for (uint32_t i = 0; i < size; i++) {
- if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) {
- // Check that a class does not implement itself directly (by having the
- // same type idx as one of its immediate implemented interfaces).
- if (UNLIKELY(interfaces->GetTypeItem(i).type_idx_ == item->class_idx_)) {
- ErrorStringPrintf("Class with same type idx as implemented interface: '%d'",
- item->class_idx_.index_);
- return false;
- }
-
- // Check that a class is defined after the interfaces it implements
- // (if they are defined in the same Dex file).
- const DexFile::ClassDef* interface_def =
- dex_file_->FindClassDef(interfaces->GetTypeItem(i).type_idx_);
- if (interface_def != nullptr) {
- // The interface is defined in this Dex file.
- if (interface_def > item) {
- // ClassDef item for interface appearing after the class' ClassDef item.
- ErrorStringPrintf("Invalid class definition ordering:"
- " class with type idx: '%d' defined before"
- " implemented interface with type idx: '%d'",
- item->class_idx_.index_,
- interfaces->GetTypeItem(i).type_idx_.index_);
- return false;
- }
- }
- }
-
- // Ensure that the interface refers to a class (not an array nor a primitive type).
- LOAD_STRING_BY_TYPE(inf_descriptor, interfaces->GetTypeItem(i).type_idx_,
- "inter_class_def_item interface type_idx")
- if (UNLIKELY(!IsValidDescriptor(inf_descriptor) || inf_descriptor[0] != 'L')) {
- ErrorStringPrintf("Invalid interface: '%s'", inf_descriptor);
- return false;
- }
- }
-
- /*
- * Ensure that there are no duplicates. This is an O(N^2) test, but in
- * practice the number of interfaces implemented by any given class is low.
- */
- for (uint32_t i = 1; i < size; i++) {
- dex::TypeIndex idx1 = interfaces->GetTypeItem(i).type_idx_;
- for (uint32_t j =0; j < i; j++) {
- dex::TypeIndex idx2 = interfaces->GetTypeItem(j).type_idx_;
- if (UNLIKELY(idx1 == idx2)) {
- ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1));
- return false;
- }
- }
- }
- }
-
- // Check that references in class_data_item are to the right class.
- if (item->class_data_off_ != 0) {
- const uint8_t* data = begin_ + item->class_data_off_;
- bool success;
- dex::TypeIndex data_definer = FindFirstClassDataDefiner(data, &success);
- if (!success) {
- return false;
- }
- if (UNLIKELY((data_definer != item->class_idx_) &&
- (data_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) {
- ErrorStringPrintf("Invalid class_data_item");
- return false;
- }
- }
-
- // Check that references in annotations_directory_item are to right class.
- if (item->annotations_off_ != 0) {
- // annotations_off_ is supposed to be aligned by 4.
- if (!IsAlignedParam(item->annotations_off_, 4)) {
- ErrorStringPrintf("Invalid annotations_off_, not aligned by 4");
- return false;
- }
- const uint8_t* data = begin_ + item->annotations_off_;
- bool success;
- dex::TypeIndex annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success);
- if (!success) {
- return false;
- }
- if (UNLIKELY((annotations_definer != item->class_idx_) &&
- (annotations_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) {
- ErrorStringPrintf("Invalid annotations_directory_item");
- return false;
- }
- }
-
- ptr_ += sizeof(DexFile::ClassDef);
- 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:
- case DexFile::MethodHandleType::kInvokeDirect:
- case DexFile::MethodHandleType::kInvokeInterface: {
- 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_);
- const DexFile::AnnotationSetRefItem* item = list->list_;
- uint32_t count = list->size_;
-
- while (count--) {
- if (item->annotations_off_ != 0 &&
- !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
- return false;
- }
- item++;
- }
-
- ptr_ = reinterpret_cast<const uint8_t*>(item);
- return true;
-}
-
-bool DexFileVerifier::CheckInterAnnotationSetItem() {
- const DexFile::AnnotationSetItem* set = reinterpret_cast<const DexFile::AnnotationSetItem*>(ptr_);
- const uint32_t* offsets = set->entries_;
- uint32_t count = set->size_;
- uint32_t last_idx = 0;
-
- for (uint32_t i = 0; i < count; i++) {
- if (*offsets != 0 && !CheckOffsetToTypeMap(*offsets, DexFile::kDexTypeAnnotationItem)) {
- return false;
- }
-
- // Get the annotation from the offset and the type index for the annotation.
- const DexFile::AnnotationItem* annotation =
- reinterpret_cast<const DexFile::AnnotationItem*>(begin_ + *offsets);
- const uint8_t* data = annotation->annotation_;
- DECODE_UNSIGNED_CHECKED_FROM(data, idx);
-
- if (UNLIKELY(last_idx >= idx && i != 0)) {
- ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
- return false;
- }
-
- last_idx = idx;
- offsets++;
- }
-
- ptr_ = reinterpret_cast<const uint8_t*>(offsets);
- return true;
-}
-
-bool DexFileVerifier::CheckInterClassDataItem() {
- ClassDataItemIterator it(*dex_file_, ptr_);
- bool success;
- dex::TypeIndex defining_class = FindFirstClassDataDefiner(ptr_, &success);
- if (!success) {
- return false;
- }
-
- for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) {
- LOAD_FIELD(field, it.GetMemberIndex(), "inter_class_data_item field_id", return false)
- if (UNLIKELY(field->class_idx_ != defining_class)) {
- ErrorStringPrintf("Mismatched defining class for class_data_item field");
- return false;
- }
- }
- for (; it.HasNextMethod(); it.Next()) {
- uint32_t code_off = it.GetMethodCodeItemOffset();
- if (code_off != 0 && !CheckOffsetToTypeMap(code_off, DexFile::kDexTypeCodeItem)) {
- return false;
- }
- LOAD_METHOD(method, it.GetMemberIndex(), "inter_class_data_item method_id", return false)
- if (UNLIKELY(method->class_idx_ != defining_class)) {
- ErrorStringPrintf("Mismatched defining class for class_data_item method");
- return false;
- }
- }
-
- ptr_ = it.EndDataPointer();
- return true;
-}
-
-bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
- const DexFile::AnnotationsDirectoryItem* item =
- reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
- bool success;
- dex::TypeIndex defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success);
- if (!success) {
- return false;
- }
-
- if (item->class_annotations_off_ != 0 &&
- !CheckOffsetToTypeMap(item->class_annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
- return false;
- }
-
- // Field annotations follow immediately after the annotations directory.
- const DexFile::FieldAnnotationsItem* field_item =
- reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1);
- uint32_t field_count = item->fields_size_;
- for (uint32_t i = 0; i < field_count; i++) {
- LOAD_FIELD(field, field_item->field_idx_, "inter_annotations_directory_item field_id",
- return false)
- if (UNLIKELY(field->class_idx_ != defining_class)) {
- ErrorStringPrintf("Mismatched defining class for field_annotation");
- return false;
- }
- if (!CheckOffsetToTypeMap(field_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
- return false;
- }
- field_item++;
- }
-
- // Method annotations follow immediately after field annotations.
- const DexFile::MethodAnnotationsItem* method_item =
- reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item);
- uint32_t method_count = item->methods_size_;
- for (uint32_t i = 0; i < method_count; i++) {
- LOAD_METHOD(method, method_item->method_idx_, "inter_annotations_directory_item method_id",
- return false)
- if (UNLIKELY(method->class_idx_ != defining_class)) {
- ErrorStringPrintf("Mismatched defining class for method_annotation");
- return false;
- }
- if (!CheckOffsetToTypeMap(method_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
- return false;
- }
- method_item++;
- }
-
- // Parameter annotations follow immediately after method annotations.
- const DexFile::ParameterAnnotationsItem* parameter_item =
- reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
- uint32_t parameter_count = item->parameters_size_;
- for (uint32_t i = 0; i < parameter_count; i++) {
- LOAD_METHOD(parameter_method, parameter_item->method_idx_,
- "inter_annotations_directory_item parameter method_id", return false)
- if (UNLIKELY(parameter_method->class_idx_ != defining_class)) {
- ErrorStringPrintf("Mismatched defining class for parameter_annotation");
- return false;
- }
- if (!CheckOffsetToTypeMap(parameter_item->annotations_off_,
- DexFile::kDexTypeAnnotationSetRefList)) {
- return false;
- }
- parameter_item++;
- }
-
- ptr_ = reinterpret_cast<const uint8_t*>(parameter_item);
- return true;
-}
-
-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) {
- case DexFile::kDexTypeClassDataItem:
- alignment_mask = sizeof(uint8_t) - 1;
- break;
- default:
- alignment_mask = sizeof(uint32_t) - 1;
- break;
- }
-
- // Iterate through the items in the section.
- previous_item_ = nullptr;
- for (uint32_t i = 0; i < count; i++) {
- uint32_t new_offset = (offset + alignment_mask) & ~alignment_mask;
- 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;
- }
- break;
- }
- case DexFile::kDexTypeTypeIdItem: {
- if (!CheckInterTypeIdItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeProtoIdItem: {
- if (!CheckInterProtoIdItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeFieldIdItem: {
- if (!CheckInterFieldIdItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeMethodIdItem: {
- if (!CheckInterMethodIdItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeClassDefItem: {
- // There shouldn't be more class definitions than type ids allow.
- // This check should be redundant, since there are checks that the
- // class_idx_ is within range and that there is only one definition
- // for a given type id.
- if (i > kTypeIdLimit) {
- ErrorStringPrintf("Too many class definition items");
- return false;
- }
- if (!CheckInterClassDefItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeCallSiteIdItem: {
- if (!CheckInterCallSiteIdItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeMethodHandleItem: {
- if (!CheckInterMethodHandleItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationSetRefList: {
- if (!CheckInterAnnotationSetRefList()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationSetItem: {
- if (!CheckInterAnnotationSetItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeClassDataItem: {
- // There shouldn't be more class data than type ids allow.
- // This check should be redundant, since there are checks that the
- // class_idx_ is within range and that there is only one definition
- // for a given type id.
- if (i > kTypeIdLimit) {
- ErrorStringPrintf("Too many class data items");
- return false;
- }
- if (!CheckInterClassDataItem()) {
- return false;
- }
- break;
- }
- case DexFile::kDexTypeAnnotationsDirectoryItem: {
- if (!CheckInterAnnotationsDirectoryItem()) {
- return false;
- }
- break;
- }
- }
-
- previous_item_ = prev_ptr;
- offset = ptr_ - begin_;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckInterSection() {
- const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
- const DexFile::MapItem* item = map->list_;
- uint32_t count = map->size_;
-
- // Cross check the items listed in the map.
- while (count--) {
- uint32_t section_offset = item->offset_;
- uint32_t section_count = item->size_;
- DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
- bool found = false;
-
- 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:
- found = true;
- break;
- case DexFile::kDexTypeStringIdItem:
- case DexFile::kDexTypeTypeIdItem:
- case DexFile::kDexTypeProtoIdItem:
- case DexFile::kDexTypeFieldIdItem:
- case DexFile::kDexTypeMethodIdItem:
- case DexFile::kDexTypeClassDefItem:
- case DexFile::kDexTypeCallSiteIdItem:
- case DexFile::kDexTypeMethodHandleItem:
- case DexFile::kDexTypeAnnotationSetRefList:
- case DexFile::kDexTypeAnnotationSetItem:
- case DexFile::kDexTypeClassDataItem:
- case DexFile::kDexTypeAnnotationsDirectoryItem: {
- if (!CheckInterSectionIterate(section_offset, section_count, type)) {
- return false;
- }
- found = true;
- break;
- }
- }
-
- if (!found) {
- ErrorStringPrintf("Unknown map item type %x", item->type_);
- return false;
- }
-
- item++;
- }
-
- return true;
-}
-
-bool DexFileVerifier::Verify() {
- // Check the header.
- if (!CheckHeader()) {
- return false;
- }
-
- // Check the map section.
- if (!CheckMap()) {
- return false;
- }
-
- // Check structure within remaining sections.
- if (!CheckIntraSection()) {
- return false;
- }
-
- // Check references from one section to another.
- if (!CheckInterSection()) {
- return false;
- }
-
- return true;
-}
-
-void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- DCHECK(failure_reason_.empty()) << failure_reason_;
- failure_reason_ = StringPrintf("Failure to verify dex file '%s': ", location_);
- StringAppendV(&failure_reason_, fmt, ap);
- va_end(ap);
-}
-
-// Fields and methods may have only one of public/protected/private.
-static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
- size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) +
- (((flags & kAccProtected) == 0) ? 0 : 1) +
- (((flags & kAccPrivate) == 0) ? 0 : 1);
- return count <= 1;
-}
-
-// Helper functions to retrieve names from the dex file. We do not want to rely on DexFile
-// functionality, as we're still verifying the dex file. begin and header correspond to the
-// underscored variants in the DexFileVerifier.
-
-static std::string GetStringOrError(const uint8_t* const begin,
- const DexFile::Header* const header,
- dex::StringIndex string_idx) {
- // The `string_idx` is not guaranteed to be valid yet.
- if (header->string_ids_size_ <= string_idx.index_) {
- return "(error)";
- }
-
- const DexFile::StringId* string_id =
- reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_)
- + string_idx.index_;
-
- // Assume that the data is OK at this point. String data has been checked at this point.
-
- const uint8_t* ptr = begin + string_id->string_data_off_;
- uint32_t dummy;
- if (!DecodeUnsignedLeb128Checked(&ptr, begin + header->file_size_, &dummy)) {
- return "(error)";
- }
- return reinterpret_cast<const char*>(ptr);
-}
-
-static std::string GetClassOrError(const uint8_t* const begin,
- const DexFile::Header* const header,
- dex::TypeIndex class_idx) {
- // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and
- // it has already been checked in `DexFileVerifier::CheckClassDataItemField()`
- // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match
- // a valid defining class.
- CHECK_LT(class_idx.index_, header->type_ids_size_);
-
- const DexFile::TypeId* type_id =
- reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx.index_;
-
- // Assume that the data is OK at this point. Type id offsets have been checked at this point.
-
- return GetStringOrError(begin, header, type_id->descriptor_idx_);
-}
-
-static std::string GetFieldDescriptionOrError(const uint8_t* const begin,
- const DexFile::Header* const header,
- uint32_t idx) {
- // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemField()`.
- CHECK_LT(idx, header->field_ids_size_);
-
- const DexFile::FieldId* field_id =
- reinterpret_cast<const DexFile::FieldId*>(begin + header->field_ids_off_) + idx;
-
- // Assume that the data is OK at this point. Field id offsets have been checked at this point.
-
- std::string class_name = GetClassOrError(begin, header, field_id->class_idx_);
- std::string field_name = GetStringOrError(begin, header, field_id->name_idx_);
-
- return class_name + "." + field_name;
-}
-
-static std::string GetMethodDescriptionOrError(const uint8_t* const begin,
- const DexFile::Header* const header,
- uint32_t idx) {
- // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemMethod()`.
- CHECK_LT(idx, header->method_ids_size_);
-
- const DexFile::MethodId* method_id =
- reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + idx;
-
- // Assume that the data is OK at this point. Method id offsets have been checked at this point.
-
- std::string class_name = GetClassOrError(begin, header, method_id->class_idx_);
- std::string method_name = GetStringOrError(begin, header, method_id->name_idx_);
-
- return class_name + "." + method_name;
-}
-
-bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx,
- uint32_t field_access_flags,
- uint32_t class_access_flags,
- std::string* error_msg) {
- // Generally sort out >16-bit flags.
- if ((field_access_flags & ~kAccJavaFlagsMask) != 0) {
- *error_msg = StringPrintf("Bad field access_flags for %s: %x(%s)",
- GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
- field_access_flags,
- PrettyJavaAccessFlags(field_access_flags).c_str());
- return false;
- }
-
- // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
- constexpr uint32_t kFieldAccessFlags = kAccPublic |
- kAccPrivate |
- kAccProtected |
- kAccStatic |
- kAccFinal |
- kAccVolatile |
- kAccTransient |
- kAccSynthetic |
- kAccEnum;
-
- // Fields may have only one of public/protected/final.
- if (!CheckAtMostOneOfPublicProtectedPrivate(field_access_flags)) {
- *error_msg = StringPrintf("Field may have only one of public/protected/private, %s: %x(%s)",
- GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
- field_access_flags,
- PrettyJavaAccessFlags(field_access_flags).c_str());
- return false;
- }
-
- // Interfaces have a pretty restricted list.
- if ((class_access_flags & kAccInterface) != 0) {
- // Interface fields must be public final static.
- constexpr uint32_t kPublicFinalStatic = kAccPublic | kAccFinal | kAccStatic;
- if ((field_access_flags & kPublicFinalStatic) != kPublicFinalStatic) {
- *error_msg = StringPrintf("Interface field is not public final static, %s: %x(%s)",
- GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
- field_access_flags,
- PrettyJavaAccessFlags(field_access_flags).c_str());
- if (dex_file_->SupportsDefaultMethods()) {
- return false;
- } else {
- // Allow in older versions, but warn.
- LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
- << *error_msg;
- }
- }
- // Interface fields may be synthetic, but may not have other flags.
- constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic);
- if ((field_access_flags & kFieldAccessFlags & kDisallowed) != 0) {
- *error_msg = StringPrintf("Interface field has disallowed flag, %s: %x(%s)",
- GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
- field_access_flags,
- PrettyJavaAccessFlags(field_access_flags).c_str());
- if (dex_file_->SupportsDefaultMethods()) {
- return false;
- } else {
- // Allow in older versions, but warn.
- LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
- << *error_msg;
- }
- }
- return true;
- }
-
- // Volatile fields may not be final.
- constexpr uint32_t kVolatileFinal = kAccVolatile | kAccFinal;
- if ((field_access_flags & kVolatileFinal) == kVolatileFinal) {
- *error_msg = StringPrintf("Fields may not be volatile and final: %s",
- GetFieldDescriptionOrError(begin_, header_, idx).c_str());
- return false;
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
- uint32_t method_access_flags,
- uint32_t class_access_flags,
- uint32_t constructor_flags_by_name,
- bool has_code,
- bool expect_direct,
- std::string* error_msg) {
- // Generally sort out >16-bit flags, except dex knows Constructor and DeclaredSynchronized.
- constexpr uint32_t kAllMethodFlags =
- kAccJavaFlagsMask | kAccConstructor | kAccDeclaredSynchronized;
- if ((method_access_flags & ~kAllMethodFlags) != 0) {
- *error_msg = StringPrintf("Bad method access_flags for %s: %x",
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
- method_access_flags);
- return false;
- }
-
- // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
- constexpr uint32_t kMethodAccessFlags = kAccPublic |
- kAccPrivate |
- kAccProtected |
- kAccStatic |
- kAccFinal |
- kAccSynthetic |
- kAccSynchronized |
- kAccBridge |
- kAccVarargs |
- kAccNative |
- kAccAbstract |
- kAccStrict;
-
- // Methods may have only one of public/protected/final.
- if (!CheckAtMostOneOfPublicProtectedPrivate(method_access_flags)) {
- *error_msg = StringPrintf("Method may have only one of public/protected/private, %s: %x",
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
- method_access_flags);
- return false;
- }
-
- constexpr uint32_t kConstructorFlags = kAccStatic | kAccConstructor;
- const bool is_constructor_by_name = (constructor_flags_by_name & kConstructorFlags) != 0;
- const bool is_clinit_by_name = constructor_flags_by_name == kConstructorFlags;
-
- // Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce
- // the reverse for backwards compatibility reasons.
- if (((method_access_flags & kAccConstructor) != 0) && !is_constructor_by_name) {
- *error_msg =
- StringPrintf("Method %" PRIu32 "(%s) is marked constructor, but doesn't match name",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
- }
-
- if (is_constructor_by_name) {
- // Check that the static constructor (= static initializer) is named "<clinit>" and that the
- // instance constructor is called "<init>".
- bool is_static = (method_access_flags & kAccStatic) != 0;
- if (is_static ^ is_clinit_by_name) {
- *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- if (dex_file_->SupportsDefaultMethods()) {
- return false;
- } else {
- // Allow in older versions, but warn.
- LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
- << *error_msg;
- }
- }
- }
-
- // Check that static and private methods, as well as constructors, are in the direct methods list,
- // and other methods in the virtual methods list.
- bool is_direct = ((method_access_flags & (kAccStatic | kAccPrivate)) != 0) ||
- is_constructor_by_name;
- if (is_direct != expect_direct) {
- *error_msg = StringPrintf("Direct/virtual method %" PRIu32 "(%s) not in expected list %d",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
- expect_direct);
- return false;
- }
-
- // From here on out it is easier to mask out the bits we're supposed to ignore.
- method_access_flags &= kMethodAccessFlags;
-
- // Interfaces are special.
- if ((class_access_flags & kAccInterface) != 0) {
- // Non-static interface methods must be public or private.
- uint32_t desired_flags = (kAccPublic | kAccStatic);
- if (dex_file_->SupportsDefaultMethods()) {
- desired_flags |= kAccPrivate;
- }
- if ((method_access_flags & desired_flags) == 0) {
- *error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- if (dex_file_->SupportsDefaultMethods()) {
- return false;
- } else {
- // Allow in older versions, but warn.
- LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
- << *error_msg;
- }
- }
- }
-
- // If there aren't any instructions, make sure that's expected.
- if (!has_code) {
- // Only native or abstract methods may not have code.
- if ((method_access_flags & (kAccNative | kAccAbstract)) == 0) {
- *error_msg = StringPrintf("Method %" PRIu32 "(%s) has no code, but is not marked native or "
- "abstract",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
- }
- // Constructors must always have code.
- if (is_constructor_by_name) {
- *error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- if (dex_file_->SupportsDefaultMethods()) {
- return false;
- } else {
- // Allow in older versions, but warn.
- LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
- << *error_msg;
- }
- }
- if ((method_access_flags & kAccAbstract) != 0) {
- // Abstract methods are not allowed to have the following flags.
- constexpr uint32_t kForbidden =
- kAccPrivate | kAccStatic | kAccFinal | kAccNative | kAccStrict | kAccSynchronized;
- if ((method_access_flags & kForbidden) != 0) {
- *error_msg = StringPrintf("Abstract method %" PRIu32 "(%s) has disallowed access flags %x",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
- method_access_flags);
- return false;
- }
- // Abstract methods should be in an abstract class or interface.
- if ((class_access_flags & (kAccInterface | kAccAbstract)) == 0) {
- LOG(WARNING) << "Method " << GetMethodDescriptionOrError(begin_, header_, method_index)
- << " is abstract, but the declaring class is neither abstract nor an "
- << "interface in dex file "
- << dex_file_->GetLocation();
- }
- }
- // Interfaces are special.
- if ((class_access_flags & kAccInterface) != 0) {
- // Interface methods without code must be abstract.
- if ((method_access_flags & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
- *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- if (dex_file_->SupportsDefaultMethods()) {
- return false;
- } else {
- // Allow in older versions, but warn.
- LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
- << *error_msg;
- }
- }
- // At this point, we know the method is public and abstract. This means that all the checks
- // for invalid combinations above applies. In addition, interface methods must not be
- // protected. This is caught by the check for only-one-of-public-protected-private.
- }
- return true;
- }
-
- // When there's code, the method must not be native or abstract.
- if ((method_access_flags & (kAccNative | kAccAbstract)) != 0) {
- *error_msg = StringPrintf("Method %" PRIu32 "(%s) has code, but is marked native or abstract",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
- }
-
- // Instance constructors must not be synchronized and a few other flags.
- if (constructor_flags_by_name == kAccConstructor) {
- static constexpr uint32_t kInitAllowed =
- kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic;
- if ((method_access_flags & ~kInitAllowed) != 0) {
- *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) flagged inappropriately %x",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
- method_access_flags);
- return false;
- }
- }
-
- return true;
-}
-
-bool DexFileVerifier::CheckConstructorProperties(
- uint32_t method_index,
- uint32_t constructor_flags) {
- DCHECK(constructor_flags == kAccConstructor ||
- constructor_flags == (kAccConstructor | kAccStatic));
-
- // Check signature matches expectations.
- const DexFile::MethodId* const method_id = CheckLoadMethodId(method_index,
- "Bad <init>/<clinit> method id");
- if (method_id == nullptr) {
- return false;
- }
-
- // Check the ProtoId for the corresponding method.
- //
- // TODO(oth): the error message here is to satisfy the MethodId test
- // in the DexFileVerifierTest. The test is checking that the error
- // contains this string if the index is out of range.
- const DexFile::ProtoId* const proto_id = CheckLoadProtoId(method_id->proto_idx_,
- "inter_method_id_item proto_idx");
- if (proto_id == nullptr) {
- return false;
- }
-
- Signature signature = dex_file_->GetMethodSignature(*method_id);
- if (constructor_flags == (kAccStatic | kAccConstructor)) {
- if (!signature.IsVoid() || signature.GetNumberOfParameters() != 0) {
- ErrorStringPrintf("<clinit> must have descriptor ()V");
- return false;
- }
- } else if (!signature.IsVoid()) {
- ErrorStringPrintf("Constructor %u(%s) must be void",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
- }
-
- return true;
-}
-
-} // namespace art
diff --git a/runtime/dex/dex_file_verifier.h b/runtime/dex/dex_file_verifier.h
deleted file mode 100644
index 6cb5d4c629..0000000000
--- a/runtime/dex/dex_file_verifier.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DEX_FILE_VERIFIER_H_
-#define ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_
-
-#include <unordered_set>
-
-#include "base/allocator.h"
-#include "base/hash_map.h"
-#include "dex_file.h"
-#include "dex_file_types.h"
-#include "safe_map.h"
-
-namespace art {
-
-class DexFileVerifier {
- public:
- static bool Verify(const DexFile* dex_file,
- const uint8_t* begin,
- size_t size,
- const char* location,
- bool verify_checksum,
- std::string* error_msg);
-
- const std::string& FailureReason() const {
- return failure_reason_;
- }
-
- private:
- DexFileVerifier(const DexFile* dex_file,
- const uint8_t* begin,
- size_t size,
- const char* location,
- bool verify_checksum)
- : dex_file_(dex_file),
- begin_(begin),
- size_(size),
- location_(location),
- verify_checksum_(verify_checksum),
- header_(&dex_file->GetHeader()),
- ptr_(nullptr),
- previous_item_(nullptr) {
- }
-
- bool Verify();
-
- bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
- bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label);
- // Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If
- // successful, the ptr will be moved forward the amount covered by the list.
- bool CheckList(size_t element_size, const char* label, const uint8_t* *ptr);
- // Checks whether the offset is zero (when size is zero) or that the offset falls within the area
- // claimed by the file.
- bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, size_t alignment, const char* label);
- // Checks whether the size is less than the limit.
- bool CheckSizeLimit(uint32_t size, uint32_t limit, const char* label);
- bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
-
- bool CheckHeader();
- bool CheckMap();
-
- uint32_t ReadUnsignedLittleEndian(uint32_t size);
- bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
- uint32_t* handler_offsets, uint32_t handlers_size);
- bool CheckClassDataItemField(uint32_t idx,
- uint32_t access_flags,
- uint32_t class_access_flags,
- dex::TypeIndex class_type_index,
- bool expect_static);
- bool CheckClassDataItemMethod(uint32_t idx,
- uint32_t access_flags,
- uint32_t class_access_flags,
- dex::TypeIndex class_type_index,
- uint32_t code_offset,
- std::unordered_set<uint32_t>* direct_method_indexes,
- bool expect_direct);
- bool CheckOrderAndGetClassDef(bool is_field,
- const char* type_descr,
- uint32_t curr_index,
- uint32_t prev_index,
- bool* have_class,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** class_def);
- bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def);
-
- bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type);
- bool CheckEncodedValue();
- bool CheckEncodedArray();
- bool CheckEncodedAnnotation();
-
- bool CheckIntraClassDataItem();
- // Check all fields of the given type from the given iterator. Load the class data from the first
- // field, if necessary (and return it), or use the given values.
- template <bool kStatic>
- bool CheckIntraClassDataItemFields(ClassDataItemIterator* it,
- bool* have_class,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** class_def);
- // Check all methods of the given type from the given iterator. Load the class data from the first
- // method, if necessary (and return it), or use the given values.
- template <bool kDirect>
- bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it,
- std::unordered_set<uint32_t>* direct_method_indexes,
- bool* have_class,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** class_def);
-
- bool CheckIntraCodeItem();
- bool CheckIntraStringDataItem();
- bool CheckIntraDebugInfoItem();
- bool CheckIntraAnnotationItem();
- bool CheckIntraAnnotationsDirectoryItem();
-
- 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);
-
- // Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an
- // additional out parameter to signal any errors loading an index.
- dex::TypeIndex FindFirstClassDataDefiner(const uint8_t* ptr, bool* success);
- dex::TypeIndex FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success);
-
- bool CheckInterStringIdItem();
- bool CheckInterTypeIdItem();
- bool CheckInterProtoIdItem();
- 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, DexFile::MapItemType type);
- bool CheckInterSection();
-
- // Load a string by (type) index. Checks whether the index is in bounds, printing the error if
- // not. If there is an error, null is returned.
- const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt);
- const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt);
-
- // Load a field/method/proto Id by index. Checks whether the index is in bounds, printing the
- // error if not. If there is an error, null is returned.
- const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt);
- const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt);
- const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt);
-
- void ErrorStringPrintf(const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
- bool FailureReasonIsSet() const { return failure_reason_.size() != 0; }
-
- // Retrieve class index and class def from the given member. index is the member index, which is
- // taken as either a field or a method index (as designated by is_field). The result, if the
- // member and declaring class could be found, is stored in class_type_index and class_def.
- // This is an expensive lookup, as we have to find the class def by type index, which is a
- // linear search. The output values should thus be cached by the caller.
- bool FindClassIndexAndDef(uint32_t index,
- bool is_field,
- dex::TypeIndex* class_type_index,
- const DexFile::ClassDef** output_class_def);
-
- // Check validity of the given access flags, interpreted for a field in the context of a class
- // with the given second access flags.
- bool CheckFieldAccessFlags(uint32_t idx,
- uint32_t field_access_flags,
- uint32_t class_access_flags,
- std::string* error_message);
-
- // Check validity of the given method and access flags, in the context of a class with the given
- // second access flags.
- bool CheckMethodAccessFlags(uint32_t method_index,
- uint32_t method_access_flags,
- uint32_t class_access_flags,
- uint32_t constructor_flags_by_name,
- bool has_code,
- bool expect_direct,
- std::string* error_message);
-
- // Check validity of given method if it's a constructor or class initializer.
- bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags);
-
- const DexFile* const dex_file_;
- const uint8_t* const begin_;
- const size_t size_;
- const char* const location_;
- const bool verify_checksum_;
- const DexFile::Header* const header_;
-
- struct OffsetTypeMapEmptyFn {
- // Make a hash map slot empty by making the offset 0. Offset 0 is a valid dex file offset that
- // is in the offset of the dex file header. However, we only store data section items in the
- // map, and these are after the header.
- void MakeEmpty(std::pair<uint32_t, uint16_t>& pair) const {
- pair.first = 0u;
- }
- // Check if a hash map slot is empty.
- bool IsEmpty(const std::pair<uint32_t, uint16_t>& pair) const {
- return pair.first == 0;
- }
- };
- struct OffsetTypeMapHashCompareFn {
- // Hash function for offset.
- size_t operator()(const uint32_t key) const {
- return key;
- }
- // std::equal function for offset.
- bool operator()(const uint32_t a, const uint32_t b) const {
- return a == b;
- }
- };
- // Map from offset to dex file type, HashMap for performance reasons.
- template<class Key,
- class T,
- class EmptyFn,
- AllocatorTag kTag,
- class Hash = std::hash<Key>,
- class Pred = std::equal_to<Key>>
- using AllocationTrackingHashMap = HashMap<
- Key, T, EmptyFn, Hash, Pred, TrackingAllocator<std::pair<Key, T>, kTag>>;
-
- AllocationTrackingHashMap<uint32_t,
- uint16_t,
- OffsetTypeMapEmptyFn,
- kAllocatorTagDexFileVerifier,
- OffsetTypeMapHashCompareFn,
- OffsetTypeMapHashCompareFn> offset_to_type_map_;
- const uint8_t* ptr_;
- const void* previous_item_;
-
- std::string failure_reason_;
-
- // Set of type ids for which there are ClassDef elements in the dex file.
- std::unordered_set<decltype(DexFile::ClassDef::class_idx_)> defined_classes_;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_FILE_VERIFIER_H_
diff --git a/runtime/dex/dex_file_verifier_test.cc b/runtime/dex/dex_file_verifier_test.cc
deleted file mode 100644
index 1cd4b2c07b..0000000000
--- a/runtime/dex/dex_file_verifier_test.cc
+++ /dev/null
@@ -1,2186 +0,0 @@
-/*
- * Copyright (C) 2011 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 "dex_file_verifier.h"
-
-#include <zlib.h>
-
-#include <functional>
-#include <memory>
-
-#include "base/bit_utils.h"
-#include "base/macros.h"
-#include "base64_test_util.h"
-#include "descriptors_names.h"
-#include "dex_file-inl.h"
-#include "dex_file_loader.h"
-#include "dex_file_types.h"
-#include "gtest/gtest.h"
-#include "leb128.h"
-#include "standard_dex_file.h"
-
-namespace art {
-
-static constexpr char kLocationString[] = "dex_file_location";
-
-// Make the Dex file version 37.
-static void MakeDexVersion37(DexFile* dex_file) {
- size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
- CHECK_EQ(*(dex_file->Begin() + offset), '5');
- *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
-}
-
-static void FixUpChecksum(uint8_t* dex_file) {
- DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
- uint32_t expected_size = header->file_size_;
- uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
- const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
- const uint8_t* non_sum_ptr = dex_file + non_sum;
- adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
- header->checksum_ = adler_checksum;
-}
-
-class DexFileVerifierTest : public testing::Test {
- protected:
- DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
- return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
- }
-
- void VerifyModification(const char* dex_file_base64_content,
- const char* location,
- const std::function<void(DexFile*)>& f,
- const char* expected_error) {
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_base64_content, &length));
- CHECK(dex_bytes != nullptr);
- // Note: `dex_file` will be destroyed before `dex_bytes`.
- std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
- f(dex_file.get());
- FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
-
- static constexpr bool kVerifyChecksum = true;
- std::string error_msg;
- bool success = DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- location,
- kVerifyChecksum,
- &error_msg);
- if (expected_error == nullptr) {
- EXPECT_TRUE(success) << error_msg;
- } else {
- EXPECT_FALSE(success) << "Expected " << expected_error;
- if (!success) {
- EXPECT_NE(error_msg.find(expected_error), std::string::npos) << error_msg;
- }
- }
- }
-};
-
-static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
- const char* location,
- std::string* error_msg) {
- // decode base64
- CHECK(base64 != nullptr);
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length));
- CHECK(dex_bytes.get() != nullptr);
-
- // read dex
- std::vector<std::unique_ptr<const DexFile>> tmp;
- const DexFileLoader dex_file_loader;
- bool success = dex_file_loader.OpenAll(dex_bytes.get(),
- length,
- location,
- /* verify */ true,
- /* verify_checksum */ true,
- error_msg,
- &tmp);
- CHECK(success) << *error_msg;
- EXPECT_EQ(1U, tmp.size());
- std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
- return dex_file;
-}
-
-// To generate a base64 encoded Dex file (such as kGoodTestDex, below)
-// from Smali files, use:
-//
-// smali assemble -o classes.dex class1.smali [class2.smali ...]
-// base64 classes.dex >classes.dex.base64
-
-// For reference.
-static const char kGoodTestDex[] =
- "ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN"
- "AAAAcAAAAAYAAACkAAAAAgAAALwAAAABAAAA1AAAAAQAAADcAAAAAQAAAPwAAACIAQAAHAEAAFoB"
- "AABiAQAAagEAAIEBAACVAQAAqQEAAL0BAADDAQAAzgEAANEBAADVAQAA2gEAAN8BAAABAAAAAgAA"
- "AAMAAAAEAAAABQAAAAgAAAAIAAAABQAAAAAAAAAJAAAABQAAAFQBAAAEAAEACwAAAAAAAAAAAAAA"
- "AAAAAAoAAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAcAAAAAAAAA8wEAAAAAAAAB"
- "AAEAAQAAAOgBAAAEAAAAcBADAAAADgACAAAAAgAAAO0BAAAIAAAAYgAAABoBBgBuIAIAEAAOAAEA"
- "AAADAAY8aW5pdD4ABkxUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09i"
- "amVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AARUZXN0AAlUZXN0"
- "LmphdmEAAVYAAlZMAANmb28AA291dAAHcHJpbnRsbgABAAcOAAMABw54AAAAAgAAgYAEnAIBCbQC"
- "AAAADQAAAAAAAAABAAAAAAAAAAEAAAANAAAAcAAAAAIAAAAGAAAApAAAAAMAAAACAAAAvAAAAAQA"
- "AAABAAAA1AAAAAUAAAAEAAAA3AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA"
- "AAIgAAANAAAAWgEAAAMgAAACAAAA6AEAAAAgAAABAAAA8wEAAAAQAAABAAAABAIAAA==";
-
-TEST_F(DexFileVerifierTest, GoodDex) {
- std::string error_msg;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex,
- kLocationString,
- &error_msg));
- ASSERT_TRUE(raw.get() != nullptr) << error_msg;
-}
-
-TEST_F(DexFileVerifierTest, MethodId) {
- // Class idx error.
- VerifyModification(
- kGoodTestDex,
- "method_id_class_idx",
- [](DexFile* dex_file) {
- DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
- method_id->class_idx_ = dex::TypeIndex(0xFF);
- },
- "could not find declaring class for direct method index 0");
-
- // Proto idx error.
- VerifyModification(
- kGoodTestDex,
- "method_id_proto_idx",
- [](DexFile* dex_file) {
- DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
- method_id->proto_idx_ = 0xFF;
- },
- "inter_method_id_item proto_idx");
-
- // Name idx error.
- VerifyModification(
- kGoodTestDex,
- "method_id_name_idx",
- [](DexFile* dex_file) {
- DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
- method_id->name_idx_ = dex::StringIndex(0xFF);
- },
- "String index not available for method flags verification");
-}
-
-// Method flags test class generated from the following smali code. The declared-synchronized
-// flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout
-// the code, but we need to remove them before doing tests.
-//
-// .class public LMethodFlags;
-// .super Ljava/lang/Object;
-//
-// .method public static constructor <clinit>()V
-// .registers 1
-// return-void
-// .end method
-//
-// .method public constructor <init>()V
-// .registers 1
-// return-void
-// .end method
-//
-// .method private declared-synchronized foo()V
-// .registers 1
-// return-void
-// .end method
-//
-// .method public declared-synchronized bar()V
-// .registers 1
-// return-void
-// .end method
-
-static const char kMethodFlagsTestDex[] =
- "ZGV4CjAzNQCyOQrJaDBwiIWv5MIuYKXhxlLLsQcx5SwgAgAAcAAAAHhWNBIAAAAAAAAAAJgBAAAH"
- "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAA8AQAA5AAAAOQA"
- "AADuAAAA9gAAAAUBAAAZAQAAHAEAACEBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA"
- "AAAAAAABAAAAAAAAAAUAAAAAAAAABgAAAAAAAAABAAAAAQAAAAAAAAD/////AAAAAHoBAAAAAAAA"
- "CDxjbGluaXQ+AAY8aW5pdD4ADUxNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAD"
- "YmFyAANmb28AAAAAAAAAAQAAAAAAAAAAAAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAEA"
- "AQAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAADAQCJgASsAgGBgATAAgKCgAjU"
- "AgKBgAjoAgAACwAAAAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAADAAAAjAAAAAMAAAABAAAA"
- "mAAAAAUAAAAEAAAApAAAAAYAAAABAAAAxAAAAAIgAAAHAAAA5AAAAAMQAAABAAAAKAEAAAEgAAAE"
- "AAAALAEAAAAgAAABAAAAegEAAAAQAAABAAAAmAEAAA==";
-
-// Find the method data for the first method with the given name (from class 0). Note: the pointer
-// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
-// delta.
-static const uint8_t* FindMethodData(const DexFile* dex_file,
- const char* name,
- /*out*/ uint32_t* method_idx = nullptr) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
- const uint8_t* class_data = dex_file->GetClassData(class_def);
-
- ClassDataItemIterator it(*dex_file, class_data);
-
- const uint8_t* trailing = class_data;
- // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
- // element has already been loaded into the iterator.
- DecodeUnsignedLeb128(&trailing);
- DecodeUnsignedLeb128(&trailing);
- DecodeUnsignedLeb128(&trailing);
- DecodeUnsignedLeb128(&trailing);
-
- // Skip all fields.
- while (it.HasNextStaticField() || it.HasNextInstanceField()) {
- trailing = it.DataPointer();
- it.Next();
- }
-
- while (it.HasNextMethod()) {
- uint32_t method_index = it.GetMemberIndex();
- dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_;
- const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
- const char* str = dex_file->GetStringData(string_id);
- if (strcmp(name, str) == 0) {
- if (method_idx != nullptr) {
- *method_idx = method_index;
- }
- DecodeUnsignedLeb128(&trailing);
- return trailing;
- }
-
- trailing = it.DataPointer();
- it.Next();
- }
-
- return nullptr;
-}
-
-// Set the method flags to the given value.
-static void SetMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
- uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
- CHECK(method_flags_ptr != nullptr) << method;
-
- // Unroll this, as we only have three bytes, anyways.
- uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
- *(method_flags_ptr++) = (base1 | 0x80);
- mask >>= 7;
-
- uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
- *(method_flags_ptr++) = (base2 | 0x80);
- mask >>= 7;
-
- uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
- *method_flags_ptr = base3;
-}
-
-static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) {
- const uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
- CHECK(method_flags_ptr != nullptr) << method;
- return DecodeUnsignedLeb128(&method_flags_ptr);
-}
-
-// Apply the given mask to method flags.
-static void ApplyMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
- uint32_t value = GetMethodFlags(dex_file, method);
- value &= mask;
- SetMethodFlags(dex_file, method, value);
-}
-
-// Apply the given mask to method flags.
-static void OrMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
- uint32_t value = GetMethodFlags(dex_file, method);
- value |= mask;
- SetMethodFlags(dex_file, method, value);
-}
-
-// Set code_off to 0 for the method.
-static void RemoveCode(DexFile* dex_file, const char* method) {
- const uint8_t* ptr = FindMethodData(dex_file, method);
- // Next is flags, pass.
- DecodeUnsignedLeb128(&ptr);
-
- // Figure out how many bytes the code_off is.
- const uint8_t* tmp = ptr;
- DecodeUnsignedLeb128(&tmp);
- size_t bytes = tmp - ptr;
-
- uint8_t* mod = const_cast<uint8_t*>(ptr);
- for (size_t i = 1; i < bytes; ++i) {
- *(mod++) = 0x80;
- }
- *mod = 0x00;
-}
-
-TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) {
- // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_ok",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
- },
- nullptr);
-}
-
-TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
- // Make sure we still accept constructors without their flags.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_missing_constructor_tag_ok",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccConstructor);
- ApplyMaskToMethodFlags(dex_file, "<clinit>", ~kAccConstructor);
- },
- nullptr);
-
- constexpr const char* kConstructors[] = { "<clinit>", "<init>"};
- for (size_t i = 0; i < 2; ++i) {
- // Constructor with code marked native.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_constructor_native",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
- },
- "has code, but is marked native or abstract");
- // Constructor with code marked abstract.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_constructor_abstract",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
- },
- "has code, but is marked native or abstract");
- // Constructor as-is without code.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_constructor_nocode",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- RemoveCode(dex_file, kConstructors[i]);
- },
- "has no code, but is not marked native or abstract");
- // Constructor without code marked native.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_constructor_native_nocode",
- [&](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
- RemoveCode(dex_file, kConstructors[i]);
- },
- "must not be abstract or native");
- // Constructor without code marked abstract.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_constructor_abstract_nocode",
- [&](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
- RemoveCode(dex_file, kConstructors[i]);
- },
- "must not be abstract or native");
- }
- // <init> may only have (modulo ignored):
- // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic
- static constexpr uint32_t kInitAllowed[] = {
- 0,
- kAccPrivate,
- kAccProtected,
- kAccPublic,
- kAccStrict,
- kAccVarargs,
- kAccSynthetic
- };
- for (size_t i = 0; i < arraysize(kInitAllowed); ++i) {
- VerifyModification(
- kMethodFlagsTestDex,
- "init_allowed_flags",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]);
- },
- nullptr);
- }
- // Only one of public-private-protected.
- for (size_t i = 1; i < 8; ++i) {
- if (POPCOUNT(i) < 2) {
- continue;
- }
- // Technically the flags match, but just be defensive here.
- uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) |
- ((i & 2) != 0 ? kAccProtected : 0) |
- ((i & 4) != 0 ? kAccPublic : 0);
- VerifyModification(
- kMethodFlagsTestDex,
- "init_one_of_ppp",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "<init>", mask);
- },
- "Method may have only one of public/protected/private");
- }
- // <init> doesn't allow
- // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge
- // Need to handle static separately as it has its own error message.
- VerifyModification(
- kMethodFlagsTestDex,
- "init_not_allowed_flags",
- [&](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "<init>", kAccStatic);
- },
- "Constructor 1(LMethodFlags;.<init>) is not flagged correctly wrt/ static");
- static constexpr uint32_t kInitNotAllowed[] = {
- kAccFinal,
- kAccSynchronized,
- kAccBridge
- };
- for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) {
- VerifyModification(
- kMethodFlagsTestDex,
- "init_not_allowed_flags",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]);
- },
- "Constructor 1(LMethodFlags;.<init>) flagged inappropriately");
- }
-}
-
-TEST_F(DexFileVerifierTest, MethodAccessFlagsMethods) {
- constexpr const char* kMethods[] = { "foo", "bar"};
- for (size_t i = 0; i < arraysize(kMethods); ++i) {
- // Make sure we reject non-constructors marked as constructors.
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_non_constructor",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kMethods[i], kAccConstructor);
- },
- "is marked constructor, but doesn't match name");
-
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_native_with_code",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kMethods[i], kAccNative);
- },
- "has code, but is marked native or abstract");
-
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_abstract_with_code",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract);
- },
- "has code, but is marked native or abstract");
-
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_non_abstract_native_no_code",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- RemoveCode(dex_file, kMethods[i]);
- },
- "has no code, but is not marked native or abstract");
-
- // Abstract methods may not have the following flags.
- constexpr uint32_t kAbstractDisallowed[] = {
- kAccPrivate,
- kAccStatic,
- kAccFinal,
- kAccNative,
- kAccStrict,
- kAccSynchronized,
- };
- for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) {
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_abstract_and_disallowed_no_code",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- RemoveCode(dex_file, kMethods[i]);
-
- // Can't check private and static with foo, as it's in the virtual list and gives a
- // different error.
- if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) &&
- ((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) {
- // Use another breaking flag.
- OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal);
- } else {
- OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]);
- }
- },
- "has disallowed access flags");
- }
-
- // Only one of public-private-protected.
- for (size_t j = 1; j < 8; ++j) {
- if (POPCOUNT(j) < 2) {
- continue;
- }
- // Technically the flags match, but just be defensive here.
- uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) |
- ((j & 2) != 0 ? kAccProtected : 0) |
- ((j & 4) != 0 ? kAccPublic : 0);
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_one_of_ppp",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic);
- OrMaskToMethodFlags(dex_file, kMethods[i], mask);
- },
- "Method may have only one of public/protected/private");
- }
- }
-}
-
-TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) {
- constexpr const char* kMethods[] = { "<clinit>", "<init>", "foo", "bar"};
- for (size_t i = 0; i < arraysize(kMethods); ++i) {
- // All interesting method flags, other flags are to be ignored.
- constexpr uint32_t kAllMethodFlags =
- kAccPublic |
- kAccPrivate |
- kAccProtected |
- kAccStatic |
- kAccFinal |
- kAccSynchronized |
- kAccBridge |
- kAccVarargs |
- kAccNative |
- kAccAbstract |
- kAccStrict |
- kAccSynthetic;
- constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF;
- VerifyModification(
- kMethodFlagsTestDex,
- "method_flags_ignored",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, kMethods[i], kIgnoredMask);
- },
- nullptr);
- }
-}
-
-TEST_F(DexFileVerifierTest, B28552165) {
- // Regression test for bad error string retrieval in different situations.
- // Using invalid access flags to trigger the error.
- VerifyModification(
- kMethodFlagsTestDex,
- "b28552165",
- [](DexFile* dex_file) {
- OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected);
- },
- "Method may have only one of public/protected/private, LMethodFlags;.foo");
-}
-
-// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
-// just easier to break up bad cases.
-
-// Standard interface. Use declared-synchronized again for 3B encoding.
-//
-// .class public interface LInterfaceMethodFlags;
-// .super Ljava/lang/Object;
-//
-// .method public static constructor <clinit>()V
-// .registers 1
-// return-void
-// .end method
-//
-// .method public abstract declared-synchronized foo()V
-// .end method
-static const char kMethodFlagsInterface[] =
- "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
- "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA"
- "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA"
- "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0"
- "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB"
- "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA"
- "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA"
- "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA";
-
-// To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply
-// to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on.
-static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) {
- uint32_t result = 0;
- uint32_t mask_index = 0;
- while (src_value != 0) {
- uint32_t index = CTZ(src_value);
- if (((src_value & (1 << index)) != 0) &&
- ((mask & (1 << mask_index)) != 0)) {
- result |= (1 << index);
- }
- src_value &= ~(1 << index);
- mask_index++;
- }
- return result;
-}
-
-TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_ok",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- },
- nullptr);
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_ok37",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- },
- nullptr);
-
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_non_public",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_non_public",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- },
- "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public");
-
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_non_abstract",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract);
- },
- "Method 1(LInterfaceMethodFlags;.foo) has no code, but is not marked native or abstract");
-
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_static",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- OrMaskToMethodFlags(dex_file, "foo", kAccStatic);
- },
- "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_private",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "foo", kAccPrivate);
- },
- "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
-
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_non_public",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_non_public",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- },
- "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public");
-
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_protected",
- [](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_protected",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
- },
- "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public");
-
- constexpr uint32_t kAllMethodFlags =
- kAccPublic |
- kAccPrivate |
- kAccProtected |
- kAccStatic |
- kAccFinal |
- kAccSynchronized |
- kAccBridge |
- kAccVarargs |
- kAccNative |
- kAccAbstract |
- kAccStrict |
- kAccSynthetic;
- constexpr uint32_t kInterfaceMethodFlags =
- kAccPublic | kAccAbstract | kAccVarargs | kAccBridge | kAccSynthetic;
- constexpr uint32_t kInterfaceDisallowed = kAllMethodFlags &
- ~kInterfaceMethodFlags &
- // Already tested, needed to be separate.
- ~kAccStatic &
- ~kAccPrivate &
- ~kAccProtected;
- static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
-
- uint32_t bits = POPCOUNT(kInterfaceDisallowed);
- for (uint32_t i = 1; i < (1u << bits); ++i) {
- VerifyModification(
- kMethodFlagsInterface,
- "method_flags_interface_non_abstract",
- [&](DexFile* dex_file) {
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
- if ((mask & kAccProtected) != 0) {
- mask &= ~kAccProtected;
- ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
- }
- OrMaskToMethodFlags(dex_file, "foo", mask);
- },
- "Abstract method 1(LInterfaceMethodFlags;.foo) has disallowed access flags");
- }
-}
-
-///////////////////////////////////////////////////////////////////
-
-// Field flags.
-
-// Find the method data for the first method with the given name (from class 0). Note: the pointer
-// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
-// delta.
-static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
- const uint8_t* class_data = dex_file->GetClassData(class_def);
-
- ClassDataItemIterator it(*dex_file, class_data);
-
- const uint8_t* trailing = class_data;
- // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
- // element has already been loaded into the iterator.
- DecodeUnsignedLeb128(&trailing);
- DecodeUnsignedLeb128(&trailing);
- DecodeUnsignedLeb128(&trailing);
- DecodeUnsignedLeb128(&trailing);
-
- while (it.HasNextStaticField() || it.HasNextInstanceField()) {
- uint32_t field_index = it.GetMemberIndex();
- dex::StringIndex name_index = dex_file->GetFieldId(field_index).name_idx_;
- const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
- const char* str = dex_file->GetStringData(string_id);
- if (strcmp(name, str) == 0) {
- DecodeUnsignedLeb128(&trailing);
- return trailing;
- }
-
- trailing = it.DataPointer();
- it.Next();
- }
-
- return nullptr;
-}
-
-// Set the method flags to the given value.
-static void SetFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
- uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
- CHECK(field_flags_ptr != nullptr) << field;
-
- // Unroll this, as we only have three bytes, anyways.
- uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
- *(field_flags_ptr++) = (base1 | 0x80);
- mask >>= 7;
-
- uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
- *(field_flags_ptr++) = (base2 | 0x80);
- mask >>= 7;
-
- uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
- *field_flags_ptr = base3;
-}
-
-static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) {
- const uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
- CHECK(field_flags_ptr != nullptr) << field;
- return DecodeUnsignedLeb128(&field_flags_ptr);
-}
-
-// Apply the given mask to method flags.
-static void ApplyMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
- uint32_t value = GetFieldFlags(dex_file, field);
- value &= mask;
- SetFieldFlags(dex_file, field, value);
-}
-
-// Apply the given mask to method flags.
-static void OrMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
- uint32_t value = GetFieldFlags(dex_file, field);
- value |= mask;
- SetFieldFlags(dex_file, field, value);
-}
-
-// Standard class. Use declared-synchronized again for 3B encoding.
-//
-// .class public LFieldFlags;
-// .super Ljava/lang/Object;
-//
-// .field declared-synchronized public foo:I
-//
-// .field declared-synchronized public static bar:I
-
-static const char kFieldFlagsTestDex[] =
- "ZGV4CjAzNQBtLw7hydbfv4TdXidZyzAB70W7w3vnYJRwAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAF"
- "AAAAcAAAAAMAAACEAAAAAAAAAAAAAAACAAAAkAAAAAAAAAAAAAAAAQAAAKAAAACwAAAAwAAAAMAA"
- "AADDAAAA0QAAAOUAAADqAAAAAAAAAAEAAAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAABAAAAAgAA"
- "AAAAAAD/////AAAAAPQAAAAAAAAAAUkADExGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7"
- "AANiYXIAA2ZvbwAAAAAAAAEBAAAAiYAIAYGACAkAAAAAAAAAAQAAAAAAAAABAAAABQAAAHAAAAAC"
- "AAAAAwAAAIQAAAAEAAAAAgAAAJAAAAAGAAAAAQAAAKAAAAACIAAABQAAAMAAAAADEAAAAQAAAPAA"
- "AAAAIAAAAQAAAPQAAAAAEAAAAQAAAAABAAA=";
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) {
- // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_ok",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
- },
- nullptr);
-}
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) {
- // Mark the field so that it should appear in the opposite list (instance vs static).
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_wrong_list",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToFieldFlags(dex_file, "foo", kAccStatic);
- },
- "Static/instance field not in expected list");
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_wrong_list",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic);
- },
- "Static/instance field not in expected list");
-}
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) {
- static const char* kFields[] = { "foo", "bar" };
- for (size_t i = 0; i < arraysize(kFields); ++i) {
- // Should be OK to remove public.
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_non_public",
- [&](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
- },
- nullptr);
- constexpr uint32_t kAccFlags = kAccPublic | kAccPrivate | kAccProtected;
- uint32_t bits = POPCOUNT(kAccFlags);
- for (uint32_t j = 1; j < (1u << bits); ++j) {
- if (POPCOUNT(j) < 2) {
- continue;
- }
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_ppp",
- [&](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
- uint32_t mask = ApplyMaskShifted(kAccFlags, j);
- OrMaskToFieldFlags(dex_file, kFields[i], mask);
- },
- "Field may have only one of public/protected/private");
- }
- }
-}
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) {
- constexpr const char* kFields[] = { "foo", "bar"};
- for (size_t i = 0; i < arraysize(kFields); ++i) {
- // All interesting method flags, other flags are to be ignored.
- constexpr uint32_t kAllFieldFlags =
- kAccPublic |
- kAccPrivate |
- kAccProtected |
- kAccStatic |
- kAccFinal |
- kAccVolatile |
- kAccTransient |
- kAccSynthetic |
- kAccEnum;
- constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF;
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_ignored",
- [&](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToFieldFlags(dex_file, kFields[i], kIgnoredMask);
- },
- nullptr);
- }
-}
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsVolatileFinal) {
- constexpr const char* kFields[] = { "foo", "bar"};
- for (size_t i = 0; i < arraysize(kFields); ++i) {
- VerifyModification(
- kFieldFlagsTestDex,
- "field_flags_final_and_volatile",
- [&](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
-
- OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal);
- },
- "Fields may not be volatile and final");
- }
-}
-
-// Standard interface. Needs to be separate from class as interfaces do not allow instance fields.
-// Use declared-synchronized again for 3B encoding.
-//
-// .class public interface LInterfaceFieldFlags;
-// .super Ljava/lang/Object;
-//
-// .field declared-synchronized public static final foo:I
-
-static const char kFieldFlagsInterfaceTestDex[] =
- "ZGV4CjAzNQCVMHfEimR1zZPk6hl6O9GPAYqkl3u0umFkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
- "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
- "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
- "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
- "b28AAAAAAAABAAAAAJmACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
- "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
- "AAAAEAAAAQAAAPQAAAA=";
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsInterface) {
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- },
- nullptr);
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- },
- nullptr);
-
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_non_public",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_non_public",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- },
- "Interface field is not public final static");
-
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_non_final",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_non_final",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
- },
- "Interface field is not public final static");
-
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_protected",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_protected",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
- },
- "Interface field is not public final static");
-
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_private",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_private",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
- },
- "Interface field is not public final static");
-
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_synthetic",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- OrMaskToFieldFlags(dex_file, "foo", kAccSynthetic);
- },
- nullptr);
-
- constexpr uint32_t kAllFieldFlags =
- kAccPublic |
- kAccPrivate |
- kAccProtected |
- kAccStatic |
- kAccFinal |
- kAccVolatile |
- kAccTransient |
- kAccSynthetic |
- kAccEnum;
- constexpr uint32_t kInterfaceFieldFlags = kAccPublic | kAccStatic | kAccFinal | kAccSynthetic;
- constexpr uint32_t kInterfaceDisallowed = kAllFieldFlags &
- ~kInterfaceFieldFlags &
- ~kAccProtected &
- ~kAccPrivate;
- static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
-
- uint32_t bits = POPCOUNT(kInterfaceDisallowed);
- for (uint32_t i = 1; i < (1u << bits); ++i) {
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_disallowed",
- [&](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
- if ((mask & kAccProtected) != 0) {
- mask &= ~kAccProtected;
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- }
- OrMaskToFieldFlags(dex_file, "foo", mask);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kFieldFlagsInterfaceTestDex,
- "field_flags_interface_disallowed",
- [&](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
-
- uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
- if ((mask & kAccProtected) != 0) {
- mask &= ~kAccProtected;
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
- }
- OrMaskToFieldFlags(dex_file, "foo", mask);
- },
- "Interface field has disallowed flag");
- }
-}
-
-// Standard bad interface. Needs to be separate from class as interfaces do not allow instance
-// fields. Use declared-synchronized again for 3B encoding.
-//
-// .class public interface LInterfaceFieldFlags;
-// .super Ljava/lang/Object;
-//
-// .field declared-synchronized public final foo:I
-
-static const char kFieldFlagsInterfaceBadTestDex[] =
- "ZGV4CjAzNQByMUnqYKHBkUpvvNp+9CnZ2VyDkKnRN6VkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
- "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
- "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
- "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
- "b28AAAAAAAAAAQAAAJGACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
- "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
- "AAAAEAAAAQAAAPQAAAA=";
-
-TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) {
- VerifyModification(
- kFieldFlagsInterfaceBadTestDex,
- "field_flags_interface_non_static",
- [](DexFile* dex_file) {
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- },
- nullptr); // Should be allowed in older dex versions for backwards compatibility.
- VerifyModification(
- kFieldFlagsInterfaceBadTestDex,
- "field_flags_interface_non_static",
- [](DexFile* dex_file) {
- MakeDexVersion37(dex_file);
- ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
- },
- "Interface field is not public final static");
-}
-
-// Generated from:
-//
-// .class public LTest;
-// .super Ljava/lang/Object;
-// .source "Test.java"
-//
-// .method public constructor <init>()V
-// .registers 1
-//
-// .prologue
-// .line 1
-// invoke-direct {p0}, Ljava/lang/Object;-><init>()V
-//
-// return-void
-// .end method
-//
-// .method public static main()V
-// .registers 2
-//
-// const-string v0, "a"
-// const-string v0, "b"
-// const-string v0, "c"
-// const-string v0, "d"
-// const-string v0, "e"
-// const-string v0, "f"
-// const-string v0, "g"
-// const-string v0, "h"
-// const-string v0, "i"
-// const-string v0, "j"
-// const-string v0, "k"
-//
-// .local v1, "local_var":Ljava/lang/String;
-// const-string v1, "test"
-// .end method
-
-static const char kDebugInfoTestDex[] =
- "ZGV4CjAzNQCHRkHix2eIMQgvLD/0VGrlllZLo0Rb6VyUAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAU"
- "AAAAcAAAAAQAAADAAAAAAQAAANAAAAAAAAAAAAAAAAMAAADcAAAAAQAAAPQAAACAAQAAFAEAABQB"
- "AAAcAQAAJAEAADgBAABMAQAAVwEAAFoBAABdAQAAYAEAAGMBAABmAQAAaQEAAGwBAABvAQAAcgEA"
- "AHUBAAB4AQAAewEAAIYBAACMAQAAAQAAAAIAAAADAAAABQAAAAUAAAADAAAAAAAAAAAAAAAAAAAA"
- "AAAAABIAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAEAAAAAAAAAPwBAAAAAAAABjxpbml0PgAG"
- "TFRlc3Q7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAJVGVzdC5qYXZh"
- "AAFWAAFhAAFiAAFjAAFkAAFlAAFmAAFnAAFoAAFpAAFqAAFrAAlsb2NhbF92YXIABG1haW4ABHRl"
- "c3QAAAABAAcOAAAAARYDARIDAAAAAQABAAEAAACUAQAABAAAAHAQAgAAAA4AAgAAAAAAAACZAQAA"
- "GAAAABoABgAaAAcAGgAIABoACQAaAAoAGgALABoADAAaAA0AGgAOABoADwAaABAAGgETAAAAAgAA"
- "gYAEpAMBCbwDAAALAAAAAAAAAAEAAAAAAAAAAQAAABQAAABwAAAAAgAAAAQAAADAAAAAAwAAAAEA"
- "AADQAAAABQAAAAMAAADcAAAABgAAAAEAAAD0AAAAAiAAABQAAAAUAQAAAyAAAAIAAACUAQAAASAA"
- "AAIAAACkAQAAACAAAAEAAAD8AQAAABAAAAEAAAAMAgAA";
-
-TEST_F(DexFileVerifierTest, DebugInfoTypeIdxTest) {
- {
- // The input dex file should be good before modification.
- std::string error_msg;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kDebugInfoTestDex,
- kLocationString,
- &error_msg));
- ASSERT_TRUE(raw.get() != nullptr) << error_msg;
- }
-
- // Modify the debug information entry.
- VerifyModification(
- kDebugInfoTestDex,
- "debug_start_type_idx",
- [](DexFile* dex_file) {
- *(const_cast<uint8_t*>(dex_file->Begin()) + 416) = 0x14U;
- },
- "DBG_START_LOCAL type_idx");
-}
-
-TEST_F(DexFileVerifierTest, SectionAlignment) {
- {
- // The input dex file should be good before modification. Any file is fine, as long as it
- // uses all sections.
- std::string error_msg;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex,
- kLocationString,
- &error_msg));
- ASSERT_TRUE(raw.get() != nullptr) << error_msg;
- }
-
- // Modify all section offsets to be unaligned.
- constexpr size_t kSections = 7;
- for (size_t i = 0; i < kSections; ++i) {
- VerifyModification(
- kGoodTestDex,
- "section_align",
- [&](DexFile* dex_file) {
- DexFile::Header* header = const_cast<DexFile::Header*>(
- reinterpret_cast<const DexFile::Header*>(dex_file->Begin()));
- uint32_t* off_ptr;
- switch (i) {
- case 0:
- off_ptr = &header->map_off_;
- break;
- case 1:
- off_ptr = &header->string_ids_off_;
- break;
- case 2:
- off_ptr = &header->type_ids_off_;
- break;
- case 3:
- off_ptr = &header->proto_ids_off_;
- break;
- case 4:
- off_ptr = &header->field_ids_off_;
- break;
- case 5:
- off_ptr = &header->method_ids_off_;
- break;
- case 6:
- off_ptr = &header->class_defs_off_;
- break;
-
- static_assert(kSections == 7, "kSections is wrong");
- default:
- LOG(FATAL) << "Unexpected section";
- UNREACHABLE();
- }
- ASSERT_TRUE(off_ptr != nullptr);
- ASSERT_NE(*off_ptr, 0U) << i; // Should already contain a value (in use).
- (*off_ptr)++; // Add one, which should misalign it (all the sections
- // above are aligned by 4).
- },
- "should be aligned by 4 for");
- }
-}
-
-// Generated from
-//
-// .class LOverloading;
-//
-// .super Ljava/lang/Object;
-//
-// .method public static foo()V
-// .registers 1
-// return-void
-// .end method
-//
-// .method public static foo(I)V
-// .registers 1
-// return-void
-// .end method
-static const char kProtoOrderingTestDex[] =
- "ZGV4CjAzNQA1L+ABE6voQ9Lr4Ci//efB53oGnDr5PinsAQAAcAAAAHhWNBIAAAAAAAAAAFgBAAAG"
- "AAAAcAAAAAQAAACIAAAAAgAAAJgAAAAAAAAAAAAAAAIAAACwAAAAAQAAAMAAAAAMAQAA4AAAAOAA"
- "AADjAAAA8gAAAAYBAAAJAQAADQEAAAAAAAABAAAAAgAAAAMAAAADAAAAAwAAAAAAAAAEAAAAAwAA"
- "ABQBAAABAAAABQAAAAEAAQAFAAAAAQAAAAAAAAACAAAAAAAAAP////8AAAAASgEAAAAAAAABSQAN"
- "TE92ZXJsb2FkaW5nOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAJWSQADZm9vAAAAAQAAAAAAAAAA"
- "AAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAACAAAJpAIBCbgC"
- "AAAMAAAAAAAAAAEAAAAAAAAAAQAAAAYAAABwAAAAAgAAAAQAAACIAAAAAwAAAAIAAACYAAAABQAA"
- "AAIAAACwAAAABgAAAAEAAADAAAAAAiAAAAYAAADgAAAAARAAAAEAAAAUAQAAAxAAAAIAAAAcAQAA"
- "ASAAAAIAAAAkAQAAACAAAAEAAABKAQAAABAAAAEAAABYAQAA";
-
-TEST_F(DexFileVerifierTest, ProtoOrdering) {
- {
- // The input dex file should be good before modification.
- std::string error_msg;
- std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kProtoOrderingTestDex,
- kLocationString,
- &error_msg));
- ASSERT_TRUE(raw.get() != nullptr) << error_msg;
- }
-
- // Modify the order of the ProtoIds for two overloads of "foo" with the
- // same return type and one having longer parameter list than the other.
- for (size_t i = 0; i != 2; ++i) {
- VerifyModification(
- kProtoOrderingTestDex,
- "proto_ordering",
- [i](DexFile* dex_file) {
- uint32_t method_idx;
- const uint8_t* data = FindMethodData(dex_file, "foo", &method_idx);
- CHECK(data != nullptr);
- // There should be 2 methods called "foo".
- CHECK_LT(method_idx + 1u, dex_file->NumMethodIds());
- CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_,
- dex_file->GetMethodId(method_idx + 1).name_idx_);
- CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u,
- dex_file->GetMethodId(method_idx + 1).proto_idx_);
- // Their return types should be the same.
- uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
- const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx);
- const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u);
- CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_);
- // And the first should not have any parameters while the second should have some.
- CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext());
- CHECK(DexFileParameterIterator(*dex_file, proto2).HasNext());
- if (i == 0) {
- // Swap the proto parameters and shorties to break the ordering.
- std::swap(const_cast<uint32_t&>(proto1.parameters_off_),
- const_cast<uint32_t&>(proto2.parameters_off_));
- std::swap(const_cast<dex::StringIndex&>(proto1.shorty_idx_),
- const_cast<dex::StringIndex&>(proto2.shorty_idx_));
- } else {
- // Copy the proto parameters and shorty to create duplicate proto id.
- const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_;
- const_cast<dex::StringIndex&>(proto1.shorty_idx_) = proto2.shorty_idx_;
- }
- },
- "Out-of-order proto_id arguments");
- }
-}
-
-// To generate a base64 encoded Dex file version 037 from Smali files, use:
-//
-// smali assemble --api 24 -o classes.dex class1.smali [class2.smali ...]
-// base64 classes.dex >classes.dex.base64
-
-// Dex file version 037 generated from:
-//
-// .class public LB28685551;
-// .super LB28685551;
-
-static const char kClassExtendsItselfTestDex[] =
- "ZGV4CjAzNwDeGbgRg1kb6swszpcTWrrOAALB++F4OPT0AAAAcAAAAHhWNBIAAAAAAAAAAKgAAAAB"
- "AAAAcAAAAAEAAAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAHgAAABcAAAAmAAAAJgA"
- "AAAAAAAAAAAAAAEAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAALTEIyODY4NTU1MTsAAAAABgAA"
- "AAAAAAABAAAAAAAAAAEAAAABAAAAcAAAAAIAAAABAAAAdAAAAAYAAAABAAAAeAAAAAIgAAABAAAA"
- "mAAAAAAQAAABAAAAqAAAAA==";
-
-TEST_F(DexFileVerifierTest, ClassExtendsItself) {
- VerifyModification(
- kClassExtendsItselfTestDex,
- "class_extends_itself",
- [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
- "Class with same type idx as its superclass: '0'");
-}
-
-// Dex file version 037 generated from:
-//
-// .class public LFoo;
-// .super LBar;
-//
-// and:
-//
-// .class public LBar;
-// .super LFoo;
-
-static const char kClassesExtendOneAnotherTestDex[] =
- "ZGV4CjAzNwBXHSrwpDMwRBkg+L+JeQCuFNRLhQ86duEcAQAAcAAAAHhWNBIAAAAAAAAAANAAAAAC"
- "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAABcAAAAwAAAAMAA"
- "AADHAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAABAAAAAQAA"
- "AAAAAAD/////AAAAAAAAAAAAAAAABUxCYXI7AAVMRm9vOwAAAAYAAAAAAAAAAQAAAAAAAAABAAAA"
- "AgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAgAAAIAAAAACIAAAAgAAAMAAAAAAEAAAAQAAANAAAAA=";
-
-TEST_F(DexFileVerifierTest, ClassesExtendOneAnother) {
- VerifyModification(
- kClassesExtendOneAnotherTestDex,
- "classes_extend_one_another",
- [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
- "Invalid class definition ordering: class with type idx: '1' defined before"
- " superclass with type idx: '0'");
-}
-
-// Dex file version 037 generated from:
-//
-// .class public LAll;
-// .super LYour;
-//
-// and:
-//
-// .class public LYour;
-// .super LBase;
-//
-// and:
-//
-// .class public LBase;
-// .super LAll;
-
-static const char kCircularClassInheritanceTestDex[] =
- "ZGV4CjAzNwBMJxgP0SJz6oLXnKfl+J7lSEORLRwF5LNMAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAD"
- "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAIgAAABkAAAA6AAAAOgA"
- "AADvAAAA9wAAAAAAAAABAAAAAgAAAAEAAAABAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAgAA"
- "AAEAAAABAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAAAAAA/////wAAAAAAAAAA"
- "AAAAAAVMQWxsOwAGTEJhc2U7AAZMWW91cjsAAAYAAAAAAAAAAQAAAAAAAAABAAAAAwAAAHAAAAAC"
- "AAAAAwAAAHwAAAAGAAAAAwAAAIgAAAACIAAAAwAAAOgAAAAAEAAAAQAAAAABAAA=";
-
-TEST_F(DexFileVerifierTest, CircularClassInheritance) {
- VerifyModification(
- kCircularClassInheritanceTestDex,
- "circular_class_inheritance",
- [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
- "Invalid class definition ordering: class with type idx: '1' defined before"
- " superclass with type idx: '0'");
-}
-
-// Dex file version 037 generated from:
-//
-// .class public abstract interface LInterfaceImplementsItself;
-// .super Ljava/lang/Object;
-// .implements LInterfaceImplementsItself;
-
-static const char kInterfaceImplementsItselfTestDex[] =
- "ZGV4CjAzNwCKKrjatp8XbXl5S/bEVJnqaBhjZkQY4440AQAAcAAAAHhWNBIAAAAAAAAAANwAAAAC"
- "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAIAAAACUAAAAoAAAAKAA"
- "AAC9AAAAAAAAAAEAAAAAAAAAAQYAAAEAAADUAAAA/////wAAAAAAAAAAAAAAABtMSW50ZXJmYWNl"
- "SW1wbGVtZW50c0l0c2VsZjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAAABAAAAAAAAAAcAAAAAAAAA"
- "AQAAAAAAAAABAAAAAgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAQAAAIAAAAACIAAAAgAAAKAAAAAB"
- "EAAAAQAAANQAAAAAEAAAAQAAANwAAAA=";
-
-TEST_F(DexFileVerifierTest, InterfaceImplementsItself) {
- VerifyModification(
- kInterfaceImplementsItselfTestDex,
- "interface_implements_itself",
- [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
- "Class with same type idx as implemented interface: '0'");
-}
-
-// Dex file version 037 generated from:
-//
-// .class public abstract interface LPing;
-// .super Ljava/lang/Object;
-// .implements LPong;
-//
-// and:
-//
-// .class public abstract interface LPong;
-// .super Ljava/lang/Object;
-// .implements LPing;
-
-static const char kInterfacesImplementOneAnotherTestDex[] =
- "ZGV4CjAzNwD0Kk9sxlYdg3Dy1Cff0gQCuJAQfEP6ohZUAQAAcAAAAHhWNBIAAAAAAAAAAPwAAAAD"
- "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIgAAACMAAAAyAAAAMgA"
- "AADQAAAA2AAAAAAAAAABAAAAAgAAAAEAAAABBgAAAgAAAOwAAAD/////AAAAAAAAAAAAAAAAAAAA"
- "AAEGAAACAAAA9AAAAP////8AAAAAAAAAAAAAAAAGTFBpbmc7AAZMUG9uZzsAEkxqYXZhL2xhbmcv"
- "T2JqZWN0OwABAAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAADAAAAcAAAAAIAAAAD"
- "AAAAfAAAAAYAAAACAAAAiAAAAAIgAAADAAAAyAAAAAEQAAACAAAA7AAAAAAQAAABAAAA/AAAAA==";
-
-TEST_F(DexFileVerifierTest, InterfacesImplementOneAnother) {
- VerifyModification(
- kInterfacesImplementOneAnotherTestDex,
- "interfaces_implement_one_another",
- [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
- "Invalid class definition ordering: class with type idx: '1' defined before"
- " implemented interface with type idx: '0'");
-}
-
-// Dex file version 037 generated from:
-//
-// .class public abstract interface LA;
-// .super Ljava/lang/Object;
-// .implements LB;
-//
-// and:
-//
-// .class public abstract interface LB;
-// .super Ljava/lang/Object;
-// .implements LC;
-//
-// and:
-//
-// .class public abstract interface LC;
-// .super Ljava/lang/Object;
-// .implements LA;
-
-static const char kCircularInterfaceImplementationTestDex[] =
- "ZGV4CjAzNwCzKmD5Fol6XAU6ichYHcUTIP7Z7MdTcEmEAQAAcAAAAHhWNBIAAAAAAAAAACwBAAAE"
- "AAAAcAAAAAQAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAJAAAACUAAAA8AAAAPAA"
- "AAD1AAAA+gAAAP8AAAAAAAAAAQAAAAIAAAADAAAAAgAAAAEGAAADAAAAHAEAAP////8AAAAAAAAA"
- "AAAAAAABAAAAAQYAAAMAAAAUAQAA/////wAAAAAAAAAAAAAAAAAAAAABBgAAAwAAACQBAAD/////"
- "AAAAAAAAAAAAAAAAA0xBOwADTEI7AANMQzsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAQAAAAIAAAAB"
- "AAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAAEAAAAcAAAAAIAAAAEAAAAgAAAAAYA"
- "AAADAAAAkAAAAAIgAAAEAAAA8AAAAAEQAAADAAAAFAEAAAAQAAABAAAALAEAAA==";
-
-TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) {
- VerifyModification(
- kCircularInterfaceImplementationTestDex,
- "circular_interface_implementation",
- [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
- "Invalid class definition ordering: class with type idx: '2' defined before"
- " implemented interface with type idx: '0'");
-}
-
-TEST_F(DexFileVerifierTest, Checksum) {
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &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;
-
- // Good checksum: all pass.
- EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "good checksum, no verify",
- /*verify_checksum*/ false,
- &error_msg));
- EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "good checksum, verify",
- /*verify_checksum*/ true,
- &error_msg));
-
- // Bad checksum: !verify_checksum passes verify_checksum fails.
- DexFile::Header* header = reinterpret_cast<DexFile::Header*>(
- const_cast<uint8_t*>(dex_file->Begin()));
- header->checksum_ = 0;
- EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad checksum, no verify",
- /*verify_checksum*/ false,
- &error_msg));
- EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad checksum, verify",
- /*verify_checksum*/ true,
- &error_msg));
- EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg;
-}
-
-TEST_F(DexFileVerifierTest, BadStaticMethodName) {
- // Generated DEX file version (037) from:
- //
- // .class public LBadName;
- // .super Ljava/lang/Object;
- //
- // .method public static <bad_name> (II)V
- // .registers 2
- // .prologue
- // return-void
- // .end method
- //
- // .method public constructor <init>()V
- // .registers 1
- // .prologue
- // .line 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // return-void
- // .end method
- //
- static const char kDexBase64[] =
- "ZGV4CjAzNwC2NYlwyxEc/h6hv+hMeUVQPtiX6MQBcfgwAgAAcAAAAHhWNBIAAAAAAAAAAJABAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABAAQAA8AAAAPAA"
- "AAD8AAAABAEAABIBAAAVAQAAIAEAADQBAAA3AQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
- "AAcAAAADAAAAPAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
- "AAAAAIABAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgAMQmFkTmFtZS5qYXZhAAFJAAlMQmFkTmFt"
- "ZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgADVklJAAIAAAAAAAAAAAAAAAACAAAHAAEABw4AAAIA"
- "AgAAAAAASAEAAAEAAAAOAAAAAQABAAEAAABOAQAABAAAAHAQAgAAAA4AAAACAAAJ1AIBgYAE6AIA"
- "AA0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAAAgAAAKAAAAAFAAAA"
- "AwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAADwBAAADEAAAAQAAAEQBAAAD"
- "IAAAAgAAAEgBAAABIAAAAgAAAFQBAAAAIAAAAQAAAIABAAAAEAAAAQAAAJABAAA=";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad static method name",
- /*verify_checksum*/ true,
- &error_msg));
-}
-
-TEST_F(DexFileVerifierTest, BadVirtualMethodName) {
- // Generated DEX file version (037) from:
- //
- // .class public LBadVirtualName;
- // .super Ljava/lang/Object;
- //
- // .method public <bad_name> (II)V
- // .registers 2
- // return-void
- // .end method
- //
- // .method public constructor <init>()V
- // .registers 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // return-void
- // .end method
- //
- static const char kDexBase64[] =
- "ZGV4CjAzNwDcPC8B2E7kYTZmeHX2u2IqrpWV9EXBHpE8AgAAcAAAAHhWNBIAAAAAAAAAAJwBAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABMAQAA8AAAAPAA"
- "AAD8AAAABAEAABkBAAAcAQAALgEAAEIBAABFAQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
- "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
- "AAAAAI4BAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgATQmFkVmlydHVhbE5hbWUuamF2YQABSQAQ"
- "TEJhZFZpcnR1YWxOYW1lOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAANWSUkAAAACAAAAAAAAAAAA"
- "AAABAAcOAAACAAAHAAABAAEAAQAAAFgBAAAEAAAAcBACAAAADgADAAMAAAAAAF0BAAABAAAADgAA"
- "AAEBAYGABOQCAAH8Ag0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAA"
- "AgAAAKAAAAAFAAAAAwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAAEwBAAAD"
- "EAAAAQAAAFQBAAADIAAAAgAAAFgBAAABIAAAAgAAAGQBAAAAIAAAAQAAAI4BAAAAEAAAAQAAAJwB"
- "AAA=";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad virtual method name",
- /*verify_checksum*/ true,
- &error_msg));
-}
-
-TEST_F(DexFileVerifierTest, BadClinitSignature) {
- // Generated DEX file version (037) from:
- //
- // .class public LOneClinitBadSig;
- // .super Ljava/lang/Object;
- //
- // .method public static constructor <clinit>(II)V
- // .registers 2
- // return-void
- // .end method
- //
- // .method public constructor <init>()V
- // .registers 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // return-void
- // .end method
- //
- static const char kDexBase64[] =
- "ZGV4CjAzNwBNOwTbfJmWq5eMOlxUY4EICGiEGJMVg8RAAgAAcAAAAHhWNBIAAAAAAAAAAKABAAAI"
- "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABQAQAA8AAAAPAA"
- "AAD6AAAAAgEAAAUBAAAYAQAALAEAAEIBAABFAQAAAgAAAAMAAAAEAAAABgAAAAYAAAADAAAAAAAA"
- "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAAFAAAA"
- "AAAAAJABAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AAUkAEUxPbmVDbGluaXRCYWRTaWc7ABJMamF2"
- "YS9sYW5nL09iamVjdDsAFE9uZUNsaW5pdEJhZFNpZy5qYXZhAAFWAANWSUkAAAACAAAAAAAAAAAA"
- "AAAAAgAABwABAAcOAAACAAIAAAAAAFgBAAABAAAADgAAAAEAAQABAAAAXgEAAAQAAABwEAIAAAAO"
- "AAAAAgAAiYAE5AIBgYAE+AINAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAA"
- "AwAAAAIAAACgAAAABQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABM"
- "AQAAAxAAAAEAAABUAQAAAyAAAAIAAABYAQAAASAAAAIAAABkAQAAACAAAAEAAACQAQAAABAAAAEA"
- "AACgAQAA";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad clinit signature",
- /*verify_checksum*/ true,
- &error_msg));
-}
-
-TEST_F(DexFileVerifierTest, BadClinitSignatureAgain) {
- // Generated DEX file version (037) from:
- //
- // .class public LOneClinitBadSigAgain;
- // .super Ljava/lang/Object;
- //
- // .method public static constructor <clinit>()I
- // .registers 1
- // const/4 v0, 1
- // return v0
- // .end method
- //
- // .method public constructor <init>()V
- // .registers 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // return-void
- // .end method
- //
- static const char kDexBase64[] =
- "ZGV4CjAzNwBfPcPu5NVwKUqZIu/YR8xqVlVD5UzTk0gEAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH"
- "AAAAcAAAAAQAAACMAAAAAgAAAJwAAAAAAAAAAAAAAAMAAAC0AAAAAQAAAMwAAAAYAQAA7AAAAOwA"
- "AAD2AAAA/gAAAAEBAAAZAQAALQEAAEgBAAACAAAAAwAAAAQAAAAGAAAAAgAAAAAAAAAAAAAABgAA"
- "AAMAAAAAAAAAAQAAAAAAAAABAAEAAQAAAAIAAQABAAAAAQAAAAEAAAACAAAAAAAAAAUAAAAAAAAA"
- "eAEAAAAAAAAIPGNsaW5pdD4ABjxpbml0PgABSQAWTE9uZUNsaW5pdEJhZFNpZ0FnYWluOwASTGph"
- "dmEvbGFuZy9PYmplY3Q7ABlPbmVDbGluaXRCYWRTaWdBZ2Fpbi5qYXZhAAFWAAABAAAAAAAAAAAA"
- "AAACAAAAEhAPAAEAAQABAAAAAAAAAAQAAABwEAIAAAAOAAAAAgAAiYAEzAIBgYAE4AIKAAAAAAAA"
- "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAIAAACcAAAABQAAAAMAAAC0AAAA"
- "BgAAAAEAAADMAAAAAiAAAAcAAADsAAAAASAAAAIAAABMAQAAACAAAAEAAAB4AQAAABAAAAEAAACI"
- "AQAA";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad clinit signature",
- /*verify_checksum*/ true,
- &error_msg));
-}
-
-TEST_F(DexFileVerifierTest, BadInitSignature) {
- // Generated DEX file version (037) from:
- //
- // .class public LBadInitSig;
- // .super Ljava/lang/Object;
- //
- // .method public constructor <init>()I
- // .registers 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // const v0, 1
- // return v0
- // .end method
- //
- static const char kDexBase64[] =
- "ZGV4CjAzNwCdMdeh1KoHWamF2Prq32LF39YZ78fV7q+wAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
- "AAAAcAAAAAQAAACEAAAAAgAAAJQAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAADUAAAA3AAAANwA"
- "AADkAAAA5wAAAPUAAAAJAQAAAQAAAAIAAAADAAAABAAAAAEAAAAAAAAAAAAAAAQAAAADAAAAAAAA"
- "AAEAAAAAAAAAAgABAAAAAAABAAAAAQAAAAIAAAAAAAAA/////wAAAAAqAQAAAAAAAAY8aW5pdD4A"
- "AUkADExCYWRJbml0U2lnOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEAAQABAAAAAAAAAAcAAABw"
- "EAEAAAAUAAEAAAAPAAAAAQAAgYAEjAIKAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAQA"
- "AACEAAAAAwAAAAIAAACUAAAABQAAAAIAAACsAAAABgAAAAEAAAC8AAAAAiAAAAUAAADcAAAAASAA"
- "AAEAAAAMAQAAACAAAAEAAAAqAQAAABAAAAEAAAA0AQAA";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad init signature",
- /*verify_checksum*/ true,
- &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)
- }
-}
-
-TEST_F(DexFileVerifierTest, BadStaticFieldInitialValuesArray) {
- // Generated DEX file version (037) from:
- //
- // .class public LBadStaticFieldInitialValuesArray;
- // .super Ljava/lang/Object;
- //
- // # static fields
- // .field static final c:C = 'c'
- // .field static final i:I = 0x1
- // .field static final s:Ljava/lang/String; = "s"
- //
- // # direct methods
- // .method public constructor <init>()V
- // .registers 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // return-void
- // .end method
- //
- // Output file was hex edited so that static field "i" has string typing in initial values array.
- static const char kDexBase64[] =
- "ZGV4CjAzNQBrMi4cCPcMvvXNRw0uI6RRubwMPwgEYXIsAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAL"
- "AAAAcAAAAAYAAACcAAAAAQAAALQAAAADAAAAwAAAAAIAAADYAAAAAQAAAOgAAAAkAQAACAEAACAB"
- "AAAoAQAAMAEAADMBAAA2AQAAOwEAAE8BAABjAQAAZgEAAGkBAABsAQAAAgAAAAMAAAAEAAAABQAA"
- "AAYAAAAHAAAABwAAAAUAAAAAAAAAAgAAAAgAAAACAAEACQAAAAIABAAKAAAAAgAAAAAAAAADAAAA"
- "AAAAAAIAAAABAAAAAwAAAAAAAAABAAAAAAAAAHsBAAB0AQAAAQABAAEAAABvAQAABAAAAHAQAQAA"
- "AA4ABjxpbml0PgAGQS5qYXZhAAFDAAFJAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv"
- "bGFuZy9TdHJpbmc7AAFWAAFjAAFpAAFzAAEABw4AAwNjFwoXCgMAAQAAGAEYARgAgYAEiAIADQAA"
- "AAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAGAAAAnAAAAAMAAAABAAAAtAAAAAQAAAADAAAA"
- "wAAAAAUAAAACAAAA2AAAAAYAAAABAAAA6AAAAAEgAAABAAAACAEAAAIgAAALAAAAIAEAAAMgAAAB"
- "AAAAbwEAAAUgAAABAAAAdAEAAAAgAAABAAAAewEAAAAQAAABAAAAjAEAAA==";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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_FALSE(DexFileVerifier::Verify(dex_file.get(),
- dex_file->Begin(),
- dex_file->Size(),
- "bad static field initial values array",
- /*verify_checksum*/ true,
- &error_msg));
-}
-
-TEST_F(DexFileVerifierTest, GoodStaticFieldInitialValuesArray) {
- // Generated DEX file version (037) from:
- //
- // .class public LGoodStaticFieldInitialValuesArray;
- // .super Ljava/lang/Object;
- //
- // # static fields
- // .field static final b:B = 0x1t
- // .field static final c:C = 'c'
- // .field static final d:D = 0.6
- // .field static final f:F = 0.5f
- // .field static final i:I = 0x3
- // .field static final j:J = 0x4L
- // .field static final l1:Ljava/lang/String;
- // .field static final l2:Ljava/lang/String; = "s"
- // .field static final l3:Ljava/lang/Class; = Ljava/lang/String;
- // .field static final s:S = 0x2s
- // .field static final z:Z = true
- //
- // # direct methods
- // .method public constructor <init>()V
- // .registers 1
- // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- // return-void
- // .end method
- static const char kDexBase64[] =
- "ZGV4CjAzNQAwWxLbdhFa1NGiFWjsy5fhUCHxe5QHtPY8AwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAZ"
- "AAAAcAAAAA0AAADUAAAAAQAAAAgBAAALAAAAFAEAAAIAAABsAQAAAQAAAHwBAACgAQAAnAEAAJwB"
- "AACkAQAApwEAAKoBAACtAQAAsAEAALMBAAC2AQAA2wEAAO4BAAACAgAAFgIAABkCAAAcAgAAHwIA"
- "ACICAAAlAgAAKAIAACsCAAAuAgAAMQIAADUCAAA5AgAAPQIAAEACAAABAAAAAgAAAAMAAAAEAAAA"
- "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADAAAAAsAAAAAAAAABgAAAA4AAAAG"
- "AAEADwAAAAYAAgAQAAAABgADABEAAAAGAAQAEgAAAAYABQATAAAABgAJABQAAAAGAAkAFQAAAAYA"
- "BwAWAAAABgAKABcAAAAGAAwAGAAAAAYAAAAAAAAACAAAAAAAAAAGAAAAAQAAAAgAAAAAAAAA////"
- "/wAAAAB8AgAARAIAAAY8aW5pdD4AAUIAAUMAAUQAAUYAAUkAAUoAI0xHb29kU3RhdGljRmllbGRJ"
- "bml0aWFsVmFsdWVzQXJyYXk7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7"
- "ABJMamF2YS9sYW5nL1N0cmluZzsAAVMAAVYAAVoAAWIAAWMAAWQAAWYAAWkAAWoAAmwxAAJsMgAC"
- "bDMAAXMAAXoAAAsAAQNj8TMzMzMzM+M/ED8EAwYEHhcXGAkCAj8AAAAAAQABAAEAAAAAAAAABAAA"
- "AHAQAQAAAA4ACwABAAAYARgBGAEYARgBGAEYARgBGAEYARgAgYAE5AQNAAAAAAAAAAEAAAAAAAAA"
- "AQAAABkAAABwAAAAAgAAAA0AAADUAAAAAwAAAAEAAAAIAQAABAAAAAsAAAAUAQAABQAAAAIAAABs"
- "AQAABgAAAAEAAAB8AQAAAiAAABkAAACcAQAABSAAAAEAAABEAgAAAxAAAAEAAABgAgAAASAAAAEA"
- "AABkAgAAACAAAAEAAAB8AgAAABAAAAEAAACcAgAA";
-
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &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 static field initial values array",
- /*verify_checksum*/ true,
- &error_msg));
-}
-
-} // namespace art
diff --git a/runtime/dex/dex_instruction-inl.h b/runtime/dex/dex_instruction-inl.h
deleted file mode 100644
index a6b8414e62..0000000000
--- a/runtime/dex/dex_instruction-inl.h
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DEX_INSTRUCTION_INL_H_
-#define ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_
-
-#include "dex_instruction.h"
-
-namespace art {
-
-//------------------------------------------------------------------------------
-// VRegA
-//------------------------------------------------------------------------------
-inline bool Instruction::HasVRegA() const {
- switch (FormatOf(Opcode())) {
- case k10t: return true;
- case k10x: return true;
- case k11n: return true;
- case k11x: return true;
- case k12x: return true;
- case k20t: return true;
- case k21c: return true;
- case k21h: return true;
- case k21s: return true;
- case k21t: return true;
- case k22b: return true;
- case k22c: return true;
- case k22s: return true;
- case k22t: return true;
- case k22x: return true;
- case k23x: return true;
- case k30t: return true;
- case k31c: return true;
- case k31i: return true;
- case k31t: return true;
- case k32x: return true;
- case k35c: return true;
- case k3rc: return true;
- case k45cc: return true;
- case k4rcc: return true;
- case k51l: return true;
- default: return false;
- }
-}
-
-inline int32_t Instruction::VRegA() const {
- switch (FormatOf(Opcode())) {
- case k10t: return VRegA_10t();
- case k10x: return VRegA_10x();
- case k11n: return VRegA_11n();
- case k11x: return VRegA_11x();
- case k12x: return VRegA_12x();
- case k20t: return VRegA_20t();
- case k21c: return VRegA_21c();
- case k21h: return VRegA_21h();
- case k21s: return VRegA_21s();
- case k21t: return VRegA_21t();
- case k22b: return VRegA_22b();
- case k22c: return VRegA_22c();
- case k22s: return VRegA_22s();
- case k22t: return VRegA_22t();
- case k22x: return VRegA_22x();
- case k23x: return VRegA_23x();
- case k30t: return VRegA_30t();
- case k31c: return VRegA_31c();
- case k31i: return VRegA_31i();
- case k31t: return VRegA_31t();
- case k32x: return VRegA_32x();
- case k35c: return VRegA_35c();
- case k3rc: return VRegA_3rc();
- case k45cc: return VRegA_45cc();
- case k4rcc: return VRegA_4rcc();
- case k51l: return VRegA_51l();
- default:
- LOG(FATAL) << "Tried to access vA of instruction " << Name() << " which has no A operand.";
- exit(EXIT_FAILURE);
- }
-}
-
-inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k10t);
- return static_cast<int8_t>(InstAA(inst_data));
-}
-
-inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k10x);
- return InstAA(inst_data);
-}
-
-inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k11n);
- return InstA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k11x);
- return InstAA(inst_data);
-}
-
-inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k12x);
- return InstA(inst_data);
-}
-
-inline int16_t Instruction::VRegA_20t() const {
- DCHECK_EQ(FormatOf(Opcode()), k20t);
- return static_cast<int16_t>(Fetch16(1));
-}
-
-inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k21c);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k21h);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k21s);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k21t);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22b);
- return InstAA(inst_data);
-}
-
-inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22c);
- return InstA(inst_data);
-}
-
-inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22s);
- return InstA(inst_data);
-}
-
-inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22t);
- return InstA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22x);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k23x);
- return InstAA(inst_data);
-}
-
-inline int32_t Instruction::VRegA_30t() const {
- DCHECK_EQ(FormatOf(Opcode()), k30t);
- return static_cast<int32_t>(Fetch32(1));
-}
-
-inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k31c);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k31i);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k31t);
- return InstAA(inst_data);
-}
-
-inline uint16_t Instruction::VRegA_32x() const {
- DCHECK_EQ(FormatOf(Opcode()), k32x);
- return Fetch16(1);
-}
-
-inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k35c);
- return InstB(inst_data); // This is labeled A in the spec.
-}
-
-inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k3rc);
- return InstAA(inst_data);
-}
-
-inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k51l);
- return InstAA(inst_data);
-}
-
-inline uint4_t Instruction::VRegA_45cc(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k45cc);
- return InstB(inst_data); // This is labeled A in the spec.
-}
-
-inline uint8_t Instruction::VRegA_4rcc(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k4rcc);
- return InstAA(inst_data);
-}
-
-//------------------------------------------------------------------------------
-// VRegB
-//------------------------------------------------------------------------------
-inline bool Instruction::HasVRegB() const {
- switch (FormatOf(Opcode())) {
- case k11n: return true;
- case k12x: return true;
- case k21c: return true;
- case k21h: return true;
- case k21s: return true;
- case k21t: return true;
- case k22b: return true;
- case k22c: return true;
- case k22s: return true;
- case k22t: return true;
- case k22x: return true;
- case k23x: return true;
- case k31c: return true;
- case k31i: return true;
- case k31t: return true;
- case k32x: return true;
- case k35c: return true;
- case k3rc: return true;
- case k45cc: return true;
- case k4rcc: return true;
- case k51l: return true;
- default: return false;
- }
-}
-
-inline bool Instruction::HasWideVRegB() const {
- return FormatOf(Opcode()) == k51l;
-}
-
-inline int32_t Instruction::VRegB() const {
- switch (FormatOf(Opcode())) {
- case k11n: return VRegB_11n();
- case k12x: return VRegB_12x();
- case k21c: return VRegB_21c();
- case k21h: return VRegB_21h();
- case k21s: return VRegB_21s();
- case k21t: return VRegB_21t();
- case k22b: return VRegB_22b();
- case k22c: return VRegB_22c();
- case k22s: return VRegB_22s();
- case k22t: return VRegB_22t();
- case k22x: return VRegB_22x();
- case k23x: return VRegB_23x();
- case k31c: return VRegB_31c();
- case k31i: return VRegB_31i();
- case k31t: return VRegB_31t();
- case k32x: return VRegB_32x();
- case k35c: return VRegB_35c();
- case k3rc: return VRegB_3rc();
- case k45cc: return VRegB_45cc();
- case k4rcc: return VRegB_4rcc();
- case k51l: return VRegB_51l();
- default:
- LOG(FATAL) << "Tried to access vB of instruction " << Name() << " which has no B operand.";
- exit(EXIT_FAILURE);
- }
-}
-
-inline uint64_t Instruction::WideVRegB() const {
- return VRegB_51l();
-}
-
-inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k11n);
- return static_cast<int4_t>((InstB(inst_data) << 28) >> 28);
-}
-
-inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k12x);
- return InstB(inst_data);
-}
-
-inline uint16_t Instruction::VRegB_21c() const {
- DCHECK_EQ(FormatOf(Opcode()), k21c);
- return Fetch16(1);
-}
-
-inline uint16_t Instruction::VRegB_21h() const {
- DCHECK_EQ(FormatOf(Opcode()), k21h);
- return Fetch16(1);
-}
-
-inline int16_t Instruction::VRegB_21s() const {
- DCHECK_EQ(FormatOf(Opcode()), k21s);
- return static_cast<int16_t>(Fetch16(1));
-}
-
-inline int16_t Instruction::VRegB_21t() const {
- DCHECK_EQ(FormatOf(Opcode()), k21t);
- return static_cast<int16_t>(Fetch16(1));
-}
-
-inline uint8_t Instruction::VRegB_22b() const {
- DCHECK_EQ(FormatOf(Opcode()), k22b);
- return static_cast<uint8_t>(Fetch16(1) & 0xff);
-}
-
-inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22c);
- return InstB(inst_data);
-}
-
-inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22s);
- return InstB(inst_data);
-}
-
-inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const {
- DCHECK_EQ(FormatOf(Opcode()), k22t);
- return InstB(inst_data);
-}
-
-inline uint16_t Instruction::VRegB_22x() const {
- DCHECK_EQ(FormatOf(Opcode()), k22x);
- return Fetch16(1);
-}
-
-inline uint8_t Instruction::VRegB_23x() const {
- DCHECK_EQ(FormatOf(Opcode()), k23x);
- return static_cast<uint8_t>(Fetch16(1) & 0xff);
-}
-
-inline uint32_t Instruction::VRegB_31c() const {
- DCHECK_EQ(FormatOf(Opcode()), k31c);
- return Fetch32(1);
-}
-
-inline int32_t Instruction::VRegB_31i() const {
- DCHECK_EQ(FormatOf(Opcode()), k31i);
- return static_cast<int32_t>(Fetch32(1));
-}
-
-inline int32_t Instruction::VRegB_31t() const {
- DCHECK_EQ(FormatOf(Opcode()), k31t);
- return static_cast<int32_t>(Fetch32(1));
-}
-
-inline uint16_t Instruction::VRegB_32x() const {
- DCHECK_EQ(FormatOf(Opcode()), k32x);
- return Fetch16(2);
-}
-
-inline uint16_t Instruction::VRegB_35c() const {
- DCHECK_EQ(FormatOf(Opcode()), k35c);
- return Fetch16(1);
-}
-
-inline uint16_t Instruction::VRegB_3rc() const {
- DCHECK_EQ(FormatOf(Opcode()), k3rc);
- return Fetch16(1);
-}
-
-inline uint16_t Instruction::VRegB_45cc() const {
- DCHECK_EQ(FormatOf(Opcode()), k45cc);
- return Fetch16(1);
-}
-
-inline uint16_t Instruction::VRegB_4rcc() const {
- DCHECK_EQ(FormatOf(Opcode()), k4rcc);
- return Fetch16(1);
-}
-
-inline uint64_t Instruction::VRegB_51l() const {
- DCHECK_EQ(FormatOf(Opcode()), k51l);
- uint64_t vB_wide = Fetch32(1) | ((uint64_t) Fetch32(3) << 32);
- return vB_wide;
-}
-
-//------------------------------------------------------------------------------
-// VRegC
-//------------------------------------------------------------------------------
-inline bool Instruction::HasVRegC() const {
- switch (FormatOf(Opcode())) {
- case k22b: return true;
- case k22c: return true;
- case k22s: return true;
- case k22t: return true;
- case k23x: return true;
- case k35c: return true;
- case k3rc: return true;
- case k45cc: return true;
- case k4rcc: return true;
- default: return false;
- }
-}
-
-inline int32_t Instruction::VRegC() const {
- switch (FormatOf(Opcode())) {
- case k22b: return VRegC_22b();
- case k22c: return VRegC_22c();
- case k22s: return VRegC_22s();
- case k22t: return VRegC_22t();
- case k23x: return VRegC_23x();
- case k35c: return VRegC_35c();
- case k3rc: return VRegC_3rc();
- case k45cc: return VRegC_45cc();
- case k4rcc: return VRegC_4rcc();
- default:
- LOG(FATAL) << "Tried to access vC of instruction " << Name() << " which has no C operand.";
- exit(EXIT_FAILURE);
- }
-}
-
-inline int8_t Instruction::VRegC_22b() const {
- DCHECK_EQ(FormatOf(Opcode()), k22b);
- return static_cast<int8_t>(Fetch16(1) >> 8);
-}
-
-inline uint16_t Instruction::VRegC_22c() const {
- DCHECK_EQ(FormatOf(Opcode()), k22c);
- return Fetch16(1);
-}
-
-inline int16_t Instruction::VRegC_22s() const {
- DCHECK_EQ(FormatOf(Opcode()), k22s);
- return static_cast<int16_t>(Fetch16(1));
-}
-
-inline int16_t Instruction::VRegC_22t() const {
- DCHECK_EQ(FormatOf(Opcode()), k22t);
- return static_cast<int16_t>(Fetch16(1));
-}
-
-inline uint8_t Instruction::VRegC_23x() const {
- DCHECK_EQ(FormatOf(Opcode()), k23x);
- return static_cast<uint8_t>(Fetch16(1) >> 8);
-}
-
-inline uint4_t Instruction::VRegC_35c() const {
- DCHECK_EQ(FormatOf(Opcode()), k35c);
- return static_cast<uint4_t>(Fetch16(2) & 0x0f);
-}
-
-inline uint16_t Instruction::VRegC_3rc() const {
- DCHECK_EQ(FormatOf(Opcode()), k3rc);
- return Fetch16(2);
-}
-
-inline uint4_t Instruction::VRegC_45cc() const {
- DCHECK_EQ(FormatOf(Opcode()), k45cc);
- return static_cast<uint4_t>(Fetch16(2) & 0x0f);
-}
-
-inline uint16_t Instruction::VRegC_4rcc() const {
- DCHECK_EQ(FormatOf(Opcode()), k4rcc);
- return Fetch16(2);
-}
-
-//------------------------------------------------------------------------------
-// VRegH
-//------------------------------------------------------------------------------
-inline bool Instruction::HasVRegH() const {
- switch (FormatOf(Opcode())) {
- case k45cc: return true;
- case k4rcc: return true;
- default : return false;
- }
-}
-
-inline int32_t Instruction::VRegH() const {
- switch (FormatOf(Opcode())) {
- case k45cc: return VRegH_45cc();
- case k4rcc: return VRegH_4rcc();
- default :
- LOG(FATAL) << "Tried to access vH of instruction " << Name() << " which has no H operand.";
- exit(EXIT_FAILURE);
- }
-}
-
-inline uint16_t Instruction::VRegH_45cc() const {
- DCHECK_EQ(FormatOf(Opcode()), k45cc);
- return Fetch16(3);
-}
-
-inline uint16_t Instruction::VRegH_4rcc() const {
- DCHECK_EQ(FormatOf(Opcode()), k4rcc);
- return Fetch16(3);
-}
-
-inline bool Instruction::HasVarArgs() const {
- return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc);
-}
-
-inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const {
- DCHECK(HasVarArgs());
-
- /*
- * Note that the fields mentioned in the spec don't appear in
- * their "usual" positions here compared to most formats. This
- * was done so that the field names for the argument count and
- * reference index match between this format and the corresponding
- * range formats (3rc and friends).
- *
- * Bottom line: The argument count is always in vA, and the
- * method constant (or equivalent) is always in vB.
- */
- uint16_t regList = Fetch16(2);
- uint4_t count = InstB(inst_data); // This is labeled A in the spec.
- DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")";
-
- /*
- * Copy the argument registers into the arg[] array, and
- * also copy the first argument (if any) into vC. (The
- * DecodedInstruction structure doesn't have separate
- * fields for {vD, vE, vF, vG}, so there's no need to make
- * copies of those.) Note that cases 5..2 fall through.
- */
- switch (count) {
- case 5:
- arg[4] = InstA(inst_data);
- FALLTHROUGH_INTENDED;
- case 4:
- arg[3] = (regList >> 12) & 0x0f;
- FALLTHROUGH_INTENDED;
- case 3:
- arg[2] = (regList >> 8) & 0x0f;
- FALLTHROUGH_INTENDED;
- case 2:
- arg[1] = (regList >> 4) & 0x0f;
- FALLTHROUGH_INTENDED;
- case 1:
- arg[0] = regList & 0x0f;
- break;
- default: // case 0
- break; // Valid, but no need to do anything.
- }
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_INL_H_
diff --git a/runtime/dex/dex_instruction.cc b/runtime/dex/dex_instruction.cc
deleted file mode 100644
index b84791ffae..0000000000
--- a/runtime/dex/dex_instruction.cc
+++ /dev/null
@@ -1,561 +0,0 @@
-/*
- * Copyright (C) 2011 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 "dex_instruction-inl.h"
-
-#include <inttypes.h>
-
-#include <iomanip>
-#include <sstream>
-
-#include "android-base/stringprintf.h"
-
-#include "dex_file-inl.h"
-#include "utf.h"
-
-namespace art {
-
-using android::base::StringPrintf;
-
-const char* const Instruction::kInstructionNames[] = {
-#define INSTRUCTION_NAME(o, c, pname, f, i, a, e, v) pname,
-#include "dex_instruction_list.h"
- DEX_INSTRUCTION_LIST(INSTRUCTION_NAME)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_NAME
-};
-
-static_assert(sizeof(Instruction::InstructionDescriptor) == 8u, "Unexpected descriptor size");
-
-static constexpr int8_t InstructionSizeInCodeUnitsByOpcode(Instruction::Code opcode,
- Instruction::Format format) {
- if (opcode == Instruction::Code::NOP) {
- return -1;
- } else if ((format >= Instruction::Format::k10x) && (format <= Instruction::Format::k10t)) {
- return 1;
- } else if ((format >= Instruction::Format::k20t) && (format <= Instruction::Format::k22c)) {
- return 2;
- } else if ((format >= Instruction::Format::k32x) && (format <= Instruction::Format::k3rc)) {
- return 3;
- } else if ((format >= Instruction::Format::k45cc) && (format <= Instruction::Format::k4rcc)) {
- return 4;
- } else if (format == Instruction::Format::k51l) {
- return 5;
- } else {
- return -1;
- }
-}
-
-Instruction::InstructionDescriptor const Instruction::kInstructionDescriptors[] = {
-#define INSTRUCTION_DESCR(opcode, c, p, format, index, flags, eflags, vflags) \
- { vflags, \
- format, \
- index, \
- flags, \
- InstructionSizeInCodeUnitsByOpcode((c), (format)), \
- },
-#include "dex_instruction_list.h"
- DEX_INSTRUCTION_LIST(INSTRUCTION_DESCR)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_DESCR
-};
-
-int32_t Instruction::GetTargetOffset() const {
- switch (FormatOf(Opcode())) {
- // Cases for conditional branches follow.
- case k22t: return VRegC_22t();
- case k21t: return VRegB_21t();
- // Cases for unconditional branches follow.
- case k10t: return VRegA_10t();
- case k20t: return VRegA_20t();
- case k30t: return VRegA_30t();
- default: LOG(FATAL) << "Tried to access the branch offset of an instruction " << Name() <<
- " which does not have a target operand.";
- }
- return 0;
-}
-
-bool Instruction::CanFlowThrough() const {
- const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
- uint16_t insn = *insns;
- Code opcode = static_cast<Code>(insn & 0xFF);
- return FlagsOf(opcode) & Instruction::kContinue;
-}
-
-size_t Instruction::SizeInCodeUnitsComplexOpcode() const {
- const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
- // Handle special NOP encoded variable length sequences.
- switch (*insns) {
- case kPackedSwitchSignature:
- return (4 + insns[1] * 2);
- case kSparseSwitchSignature:
- return (2 + insns[1] * 4);
- case kArrayDataSignature: {
- uint16_t element_size = insns[1];
- uint32_t length = insns[2] | (((uint32_t)insns[3]) << 16);
- // The plus 1 is to round up for odd size and width.
- return (4 + (element_size * length + 1) / 2);
- }
- default:
- if ((*insns & 0xFF) == 0) {
- return 1; // NOP.
- } else {
- LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
- UNREACHABLE();
- }
- }
-}
-
-size_t Instruction::CodeUnitsRequiredForSizeOfComplexOpcode() const {
- const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
- // Handle special NOP encoded variable length sequences.
- switch (*insns) {
- case kPackedSwitchSignature:
- FALLTHROUGH_INTENDED;
- case kSparseSwitchSignature:
- return 2;
- case kArrayDataSignature:
- return 4;
- default:
- if ((*insns & 0xFF) == 0) {
- return 1; // NOP.
- } else {
- LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
- UNREACHABLE();
- }
- }
-}
-
-std::string Instruction::DumpHex(size_t code_units) const {
- size_t inst_length = SizeInCodeUnits();
- if (inst_length > code_units) {
- inst_length = code_units;
- }
- std::ostringstream os;
- const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
- for (size_t i = 0; i < inst_length; i++) {
- os << StringPrintf("0x%04x", insn[i]) << " ";
- }
- for (size_t i = inst_length; i < code_units; i++) {
- os << " ";
- }
- return os.str();
-}
-
-std::string Instruction::DumpHexLE(size_t instr_code_units) const {
- size_t inst_length = SizeInCodeUnits();
- if (inst_length > instr_code_units) {
- inst_length = instr_code_units;
- }
- std::ostringstream os;
- const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
- for (size_t i = 0; i < inst_length; i++) {
- os << StringPrintf("%02x%02x", static_cast<uint8_t>(insn[i] & 0x00FF),
- static_cast<uint8_t>((insn[i] & 0xFF00) >> 8)) << " ";
- }
- for (size_t i = inst_length; i < instr_code_units; i++) {
- os << " ";
- }
- return os.str();
-}
-
-std::string Instruction::DumpString(const DexFile* file) const {
- std::ostringstream os;
- const char* opcode = kInstructionNames[Opcode()];
- switch (FormatOf(Opcode())) {
- case k10x: os << opcode; break;
- case k12x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_12x(), VRegB_12x()); break;
- case k11n: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_11n(), VRegB_11n()); break;
- case k11x: os << StringPrintf("%s v%d", opcode, VRegA_11x()); break;
- case k10t: os << StringPrintf("%s %+d", opcode, VRegA_10t()); break;
- case k20t: os << StringPrintf("%s %+d", opcode, VRegA_20t()); break;
- case k22x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_22x(), VRegB_22x()); break;
- case k21t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_21t(), VRegB_21t()); break;
- case k21s: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_21s(), VRegB_21s()); break;
- case k21h: {
- // op vAA, #+BBBB0000[00000000]
- if (Opcode() == CONST_HIGH16) {
- uint32_t value = VRegB_21h() << 16;
- os << StringPrintf("%s v%d, #int %+d // 0x%x", opcode, VRegA_21h(), value, value);
- } else {
- uint64_t value = static_cast<uint64_t>(VRegB_21h()) << 48;
- os << StringPrintf("%s v%d, #long %+" PRId64 " // 0x%" PRIx64, opcode, VRegA_21h(),
- value, value);
- }
- }
- break;
- case k21c: {
- switch (Opcode()) {
- case CONST_STRING:
- if (file != nullptr) {
- uint32_t string_idx = VRegB_21c();
- if (string_idx < file->NumStringIds()) {
- os << StringPrintf(
- "const-string v%d, %s // string@%d",
- VRegA_21c(),
- PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(),
- string_idx);
- } else {
- os << StringPrintf("const-string v%d, <<invalid-string-idx-%d>> // string@%d",
- VRegA_21c(),
- string_idx,
- string_idx);
- }
- break;
- }
- FALLTHROUGH_INTENDED;
- case CHECK_CAST:
- case CONST_CLASS:
- case NEW_INSTANCE:
- if (file != nullptr) {
- dex::TypeIndex type_idx(VRegB_21c());
- os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", "
- << file->PrettyType(type_idx) << " // type@" << type_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case SGET:
- case SGET_WIDE:
- case SGET_OBJECT:
- case SGET_BOOLEAN:
- case SGET_BYTE:
- case SGET_CHAR:
- case SGET_SHORT:
- if (file != nullptr) {
- uint32_t field_idx = VRegB_21c();
- os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true)
- << " // field@" << field_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case SPUT:
- case SPUT_WIDE:
- case SPUT_OBJECT:
- case SPUT_BOOLEAN:
- case SPUT_BYTE:
- case SPUT_CHAR:
- case SPUT_SHORT:
- if (file != nullptr) {
- uint32_t field_idx = VRegB_21c();
- os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true)
- << " // field@" << field_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- default:
- os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_21c(), VRegB_21c());
- break;
- }
- break;
- }
- case k23x: os << StringPrintf("%s v%d, v%d, v%d", opcode, VRegA_23x(), VRegB_23x(), VRegC_23x()); break;
- case k22b: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22b(), VRegB_22b(), VRegC_22b()); break;
- case k22t: os << StringPrintf("%s v%d, v%d, %+d", opcode, VRegA_22t(), VRegB_22t(), VRegC_22t()); break;
- case k22s: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22s(), VRegB_22s(), VRegC_22s()); break;
- case k22c: {
- switch (Opcode()) {
- case IGET:
- case IGET_WIDE:
- case IGET_OBJECT:
- case IGET_BOOLEAN:
- case IGET_BYTE:
- case IGET_CHAR:
- case IGET_SHORT:
- if (file != nullptr) {
- uint32_t field_idx = VRegC_22c();
- os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
- << file->PrettyField(field_idx, true) << " // field@" << field_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case IGET_QUICK:
- case IGET_OBJECT_QUICK:
- if (file != nullptr) {
- uint32_t field_idx = VRegC_22c();
- os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
- << "// offset@" << field_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case IPUT:
- case IPUT_WIDE:
- case IPUT_OBJECT:
- case IPUT_BOOLEAN:
- case IPUT_BYTE:
- case IPUT_CHAR:
- case IPUT_SHORT:
- if (file != nullptr) {
- uint32_t field_idx = VRegC_22c();
- os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
- << file->PrettyField(field_idx, true) << " // field@" << field_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case IPUT_QUICK:
- case IPUT_OBJECT_QUICK:
- if (file != nullptr) {
- uint32_t field_idx = VRegC_22c();
- os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
- << "// offset@" << field_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case INSTANCE_OF:
- if (file != nullptr) {
- dex::TypeIndex type_idx(VRegC_22c());
- os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
- << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
- << " // type@" << type_idx.index_;
- break;
- }
- FALLTHROUGH_INTENDED;
- case NEW_ARRAY:
- if (file != nullptr) {
- dex::TypeIndex type_idx(VRegC_22c());
- os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
- << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
- << " // type@" << type_idx.index_;
- break;
- }
- FALLTHROUGH_INTENDED;
- default:
- os << StringPrintf("%s v%d, v%d, thing@%d", opcode, VRegA_22c(), VRegB_22c(), VRegC_22c());
- break;
- }
- break;
- }
- case k32x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_32x(), VRegB_32x()); break;
- case k30t: os << StringPrintf("%s %+d", opcode, VRegA_30t()); break;
- case k31t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_31t(), VRegB_31t()); break;
- case k31i: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_31i(), VRegB_31i()); break;
- case k31c:
- if (Opcode() == CONST_STRING_JUMBO) {
- uint32_t string_idx = VRegB_31c();
- if (file != nullptr) {
- if (string_idx < file->NumStringIds()) {
- os << StringPrintf(
- "%s v%d, %s // string@%d",
- opcode,
- VRegA_31c(),
- PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(),
- string_idx);
- } else {
- os << StringPrintf("%s v%d, <<invalid-string-idx-%d>> // string@%d",
- opcode,
- VRegA_31c(),
- string_idx,
- string_idx);
- }
- } else {
- os << StringPrintf("%s v%d, string@%d", opcode, VRegA_31c(), string_idx);
- }
- } else {
- os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break;
- }
- break;
- case k35c: {
- uint32_t arg[kMaxVarArgRegs];
- GetVarArgs(arg);
- auto DumpArgs = [&](size_t count) {
- for (size_t i = 0; i < count; ++i) {
- if (i != 0) {
- os << ", ";
- }
- os << "v" << arg[i];
- }
- };
- switch (Opcode()) {
- case FILLED_NEW_ARRAY:
- {
- os << opcode << " {";
- DumpArgs(VRegA_35c());
- os << "}, type@" << VRegB_35c();
- }
- break;
-
- case INVOKE_VIRTUAL:
- case INVOKE_SUPER:
- case INVOKE_DIRECT:
- case INVOKE_STATIC:
- case INVOKE_INTERFACE:
- if (file != nullptr) {
- os << opcode << " {";
- uint32_t method_idx = VRegB_35c();
- DumpArgs(VRegA_35c());
- os << "}, " << file->PrettyMethod(method_idx) << " // method@" << method_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case INVOKE_VIRTUAL_QUICK:
- if (file != nullptr) {
- os << opcode << " {";
- uint32_t method_idx = VRegB_35c();
- DumpArgs(VRegA_35c());
- os << "}, // vtable@" << method_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case INVOKE_CUSTOM:
- if (file != nullptr) {
- os << opcode << " {";
- uint32_t call_site_idx = VRegB_35c();
- DumpArgs(VRegA_35c());
- os << "}, // call_site@" << call_site_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- default:
- os << opcode << " {";
- DumpArgs(VRegA_35c());
- os << "}, thing@" << VRegB_35c();
- break;
- }
- 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:
- case INVOKE_DIRECT_RANGE:
- case INVOKE_STATIC_RANGE:
- case INVOKE_INTERFACE_RANGE:
- if (file != nullptr) {
- uint32_t method_idx = VRegB_3rc();
- os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
- << file->PrettyMethod(method_idx) << " // method@" << method_idx;
- break;
- }
- FALLTHROUGH_INTENDED;
- case INVOKE_VIRTUAL_RANGE_QUICK:
- if (file != nullptr) {
- uint32_t method_idx = VRegB_3rc();
- 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}, ", opcode, first_reg, last_reg)
- << "thing@" << VRegB_3rc();
- break;
- }
- break;
- }
- case k45cc: {
- uint32_t arg[kMaxVarArgRegs];
- GetVarArgs(arg);
- uint32_t method_idx = VRegB_45cc();
- uint32_t proto_idx = VRegH_45cc();
- os << opcode << " {";
- for (int i = 0; i < VRegA_45cc(); ++i) {
- if (i != 0) {
- os << ", ";
- }
- os << "v" << arg[i];
- }
- os << "}";
- if (file != nullptr) {
- os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
- << " // ";
- } else {
- os << ", ";
- }
- os << "method@" << method_idx << ", proto@" << proto_idx;
- break;
- }
- case k4rcc:
- switch (Opcode()) {
- case INVOKE_POLYMORPHIC_RANGE: {
- if (file != nullptr) {
- uint32_t method_idx = VRegB_4rcc();
- uint32_t proto_idx = VRegH_4rcc();
- os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
- << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
- << " // method@" << method_idx << ", proto@" << proto_idx;
- break;
- }
- }
- FALLTHROUGH_INTENDED;
- default: {
- uint32_t method_idx = VRegB_4rcc();
- uint32_t proto_idx = VRegH_4rcc();
- os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
- << "}, method@" << method_idx << ", proto@" << proto_idx;
- }
- }
- break;
- case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break;
- }
- return os.str();
-}
-
-// Add some checks that ensure the flags make sense. We need a subclass to be in the context of
-// Instruction. Otherwise the flags from the instruction list don't work.
-struct InstructionStaticAsserts : private Instruction {
- #define IMPLIES(a, b) (!(a) || (b))
-
- #define VAR_ARGS_CHECK(o, c, pname, f, i, a, e, v) \
- static_assert(IMPLIES((f) == k35c || (f) == k45cc, \
- ((v) & (kVerifyVarArg | kVerifyVarArgNonZero)) != 0), \
- "Missing var-arg verification");
- #include "dex_instruction_list.h"
- DEX_INSTRUCTION_LIST(VAR_ARGS_CHECK)
- #undef DEX_INSTRUCTION_LIST
- #undef VAR_ARGS_CHECK
-
- #define VAR_ARGS_RANGE_CHECK(o, c, pname, f, i, a, e, v) \
- static_assert(IMPLIES((f) == k3rc || (f) == k4rcc, \
- ((v) & (kVerifyVarArgRange | kVerifyVarArgRangeNonZero)) != 0), \
- "Missing var-arg verification");
- #include "dex_instruction_list.h"
- DEX_INSTRUCTION_LIST(VAR_ARGS_RANGE_CHECK)
- #undef DEX_INSTRUCTION_LIST
- #undef VAR_ARGS_RANGE_CHECK
-
- #define EXPERIMENTAL_CHECK(o, c, pname, f, i, a, e, v) \
- static_assert(kHaveExperimentalInstructions || (((a) & kExperimental) == 0), \
- "Unexpected experimental instruction.");
- #include "dex_instruction_list.h"
- DEX_INSTRUCTION_LIST(EXPERIMENTAL_CHECK)
- #undef DEX_INSTRUCTION_LIST
- #undef EXPERIMENTAL_CHECK
-};
-
-std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) {
- return os << Instruction::Name(code);
-}
-
-uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const {
- DCHECK_LT(operand_index, GetNumberOfOperands());
- return first_operand_ + operand_index;
-}
-
-uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const {
- DCHECK_LT(operand_index, GetNumberOfOperands());
- return operands_[operand_index];
-}
-
-} // namespace art
diff --git a/runtime/dex/dex_instruction.h b/runtime/dex/dex_instruction.h
deleted file mode 100644
index 8b1a5ce670..0000000000
--- a/runtime/dex/dex_instruction.h
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DEX_INSTRUCTION_H_
-#define ART_RUNTIME_DEX_DEX_INSTRUCTION_H_
-
-#include <android-base/logging.h>
-
-#include "base/macros.h"
-#include "globals.h"
-
-typedef uint8_t uint4_t;
-typedef int8_t int4_t;
-
-namespace art {
-
-class DexFile;
-
-enum {
- kNumPackedOpcodes = 0x100
-};
-
-class Instruction {
- public:
- // NOP-encoded switch-statement signatures.
- enum Signatures {
- kPackedSwitchSignature = 0x0100,
- kSparseSwitchSignature = 0x0200,
- kArrayDataSignature = 0x0300,
- };
-
- struct PACKED(4) PackedSwitchPayload {
- const uint16_t ident;
- const uint16_t case_count;
- const int32_t first_key;
- const int32_t targets[];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PackedSwitchPayload);
- };
-
- struct PACKED(4) SparseSwitchPayload {
- const uint16_t ident;
- const uint16_t case_count;
- const int32_t keys_and_targets[];
-
- public:
- const int32_t* GetKeys() const {
- return keys_and_targets;
- }
-
- const int32_t* GetTargets() const {
- return keys_and_targets + case_count;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(SparseSwitchPayload);
- };
-
- struct PACKED(4) ArrayDataPayload {
- const uint16_t ident;
- const uint16_t element_width;
- const uint32_t element_count;
- const uint8_t data[];
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload);
- };
-
- enum Code { // private marker to avoid generate-operator-out.py from processing.
-#define INSTRUCTION_ENUM(opcode, cname, p, f, i, a, e, v) cname = (opcode),
-#include "dex_instruction_list.h"
- DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_ENUM
- RSUB_INT_LIT16 = RSUB_INT,
- };
-
- enum Format : uint8_t {
- k10x, // op
- k12x, // op vA, vB
- k11n, // op vA, #+B
- k11x, // op vAA
- k10t, // op +AA
- k20t, // op +AAAA
- k22x, // op vAA, vBBBB
- k21t, // op vAA, +BBBB
- k21s, // op vAA, #+BBBB
- k21h, // op vAA, #+BBBB00000[00000000]
- k21c, // op vAA, thing@BBBB
- k23x, // op vAA, vBB, vCC
- k22b, // op vAA, vBB, #+CC
- k22t, // op vA, vB, +CCCC
- k22s, // op vA, vB, #+CCCC
- k22c, // op vA, vB, thing@CCCC
- k32x, // op vAAAA, vBBBB
- k30t, // op +AAAAAAAA
- k31t, // op vAA, +BBBBBBBB
- k31i, // op vAA, #+BBBBBBBB
- k31c, // op vAA, thing@BBBBBBBB
- k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
- k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
-
- // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count)
- // format: AG op BBBB FEDC HHHH
- k45cc,
-
- // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
- // format: AA op BBBB CCCC HHHH
- k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
-
- k51l, // op vAA, #+BBBBBBBBBBBBBBBB
- };
-
- enum IndexType : uint8_t {
- 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)
- kIndexCallSiteRef, // call site reference index
- kIndexMethodHandleRef, // constant method handle reference index
- kIndexProtoRef, // prototype reference index
- };
-
- enum Flags : uint8_t {
- kBranch = 0x01, // conditional or unconditional branch
- kContinue = 0x02, // flow can continue to next statement
- kSwitch = 0x04, // switch statement
- kThrow = 0x08, // could cause an exception to be thrown
- kReturn = 0x10, // returns, no additional statements
- kInvoke = 0x20, // a flavor of invoke
- kUnconditional = 0x40, // unconditional branch
- kExperimental = 0x80, // is an experimental opcode
- };
-
- // Old flags. Keeping them around in case we might need them again some day.
- enum ExtendedFlags : uint32_t {
- kAdd = 0x0000080, // addition
- kSubtract = 0x0000100, // subtract
- kMultiply = 0x0000200, // multiply
- kDivide = 0x0000400, // division
- kRemainder = 0x0000800, // remainder
- kAnd = 0x0001000, // and
- kOr = 0x0002000, // or
- kXor = 0x0004000, // xor
- kShl = 0x0008000, // shl
- kShr = 0x0010000, // shr
- kUshr = 0x0020000, // ushr
- kCast = 0x0040000, // cast
- kStore = 0x0080000, // store opcode
- kLoad = 0x0100000, // load opcode
- kClobber = 0x0200000, // clobbers memory in a big way (not just a write)
- kRegCFieldOrConstant = 0x0400000, // is the third virtual register a field or literal constant (vC)
- kRegBFieldOrConstant = 0x0800000, // is the second virtual register a field or literal constant (vB)
- };
-
- enum VerifyFlag : uint32_t {
- 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,
- kVerifyRegBMethodHandle = 0x2000000,
- kVerifyRegBPrototype = 0x4000000,
- };
-
- // Collect the enums in a struct for better locality.
- struct InstructionDescriptor {
- uint32_t verify_flags; // Set of VerifyFlag.
- Format format;
- IndexType index_type;
- uint8_t flags; // Set of Flags.
- int8_t size_in_code_units;
- };
-
- static constexpr uint32_t kMaxVarArgRegs = 5;
-
- static constexpr bool kHaveExperimentalInstructions = false;
-
- // Returns the size (in 2 byte code units) of this instruction.
- size_t SizeInCodeUnits() const {
- int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units;
- if (UNLIKELY(result < 0)) {
- return SizeInCodeUnitsComplexOpcode();
- } else {
- return static_cast<size_t>(result);
- }
- }
-
- // Code units required to calculate the size of the instruction.
- size_t CodeUnitsRequiredForSizeComputation() const {
- const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units;
- return UNLIKELY(result < 0) ? CodeUnitsRequiredForSizeOfComplexOpcode() : 1;
- }
-
- // Reads an instruction out of the stream at the specified address.
- static const Instruction* At(const uint16_t* code) {
- DCHECK(code != nullptr);
- return reinterpret_cast<const Instruction*>(code);
- }
-
- // Reads an instruction out of the stream from the current address plus an offset.
- const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED {
- return At(reinterpret_cast<const uint16_t*>(this) + offset);
- }
-
- // Returns a pointer to the next instruction in the stream.
- const Instruction* Next() const {
- return RelativeAt(SizeInCodeUnits());
- }
-
- // Returns a pointer to the instruction after this 1xx instruction in the stream.
- const Instruction* Next_1xx() const {
- DCHECK(FormatOf(Opcode()) >= k10x && FormatOf(Opcode()) <= k10t);
- return RelativeAt(1);
- }
-
- // Returns a pointer to the instruction after this 2xx instruction in the stream.
- const Instruction* Next_2xx() const {
- DCHECK(FormatOf(Opcode()) >= k20t && FormatOf(Opcode()) <= k22c);
- return RelativeAt(2);
- }
-
- // Returns a pointer to the instruction after this 3xx instruction in the stream.
- const Instruction* Next_3xx() const {
- DCHECK(FormatOf(Opcode()) >= k32x && FormatOf(Opcode()) <= k3rc);
- return RelativeAt(3);
- }
-
- // Returns a pointer to the instruction after this 4xx instruction in the stream.
- const Instruction* Next_4xx() const {
- DCHECK(FormatOf(Opcode()) >= k45cc && FormatOf(Opcode()) <= k4rcc);
- return RelativeAt(4);
- }
-
- // Returns a pointer to the instruction after this 51l instruction in the stream.
- const Instruction* Next_51l() const {
- DCHECK(FormatOf(Opcode()) == k51l);
- return RelativeAt(5);
- }
-
- // Returns the name of this instruction's opcode.
- const char* Name() const {
- return Instruction::Name(Opcode());
- }
-
- // Returns the name of the given opcode.
- static const char* Name(Code opcode) {
- return kInstructionNames[opcode];
- }
-
- // VRegA
- bool HasVRegA() const;
- ALWAYS_INLINE int32_t VRegA() const;
-
- int8_t VRegA_10t() const {
- return VRegA_10t(Fetch16(0));
- }
- uint8_t VRegA_10x() const {
- return VRegA_10x(Fetch16(0));
- }
- uint4_t VRegA_11n() const {
- return VRegA_11n(Fetch16(0));
- }
- uint8_t VRegA_11x() const {
- return VRegA_11x(Fetch16(0));
- }
- uint4_t VRegA_12x() const {
- return VRegA_12x(Fetch16(0));
- }
- int16_t VRegA_20t() const;
- uint8_t VRegA_21c() const {
- return VRegA_21c(Fetch16(0));
- }
- uint8_t VRegA_21h() const {
- return VRegA_21h(Fetch16(0));
- }
- uint8_t VRegA_21s() const {
- return VRegA_21s(Fetch16(0));
- }
- uint8_t VRegA_21t() const {
- return VRegA_21t(Fetch16(0));
- }
- uint8_t VRegA_22b() const {
- return VRegA_22b(Fetch16(0));
- }
- uint4_t VRegA_22c() const {
- return VRegA_22c(Fetch16(0));
- }
- uint4_t VRegA_22s() const {
- return VRegA_22s(Fetch16(0));
- }
- uint4_t VRegA_22t() const {
- return VRegA_22t(Fetch16(0));
- }
- uint8_t VRegA_22x() const {
- return VRegA_22x(Fetch16(0));
- }
- uint8_t VRegA_23x() const {
- return VRegA_23x(Fetch16(0));
- }
- int32_t VRegA_30t() const;
- uint8_t VRegA_31c() const {
- return VRegA_31c(Fetch16(0));
- }
- uint8_t VRegA_31i() const {
- return VRegA_31i(Fetch16(0));
- }
- uint8_t VRegA_31t() const {
- return VRegA_31t(Fetch16(0));
- }
- uint16_t VRegA_32x() const;
- uint4_t VRegA_35c() const {
- return VRegA_35c(Fetch16(0));
- }
- uint8_t VRegA_3rc() const {
- return VRegA_3rc(Fetch16(0));
- }
- uint8_t VRegA_51l() const {
- return VRegA_51l(Fetch16(0));
- }
- uint4_t VRegA_45cc() const {
- return VRegA_45cc(Fetch16(0));
- }
- uint8_t VRegA_4rcc() const {
- return VRegA_4rcc(Fetch16(0));
- }
-
- // The following methods return the vA operand for various instruction formats. The "inst_data"
- // parameter holds the first 16 bits of instruction which the returned value is decoded from.
- int8_t VRegA_10t(uint16_t inst_data) const;
- uint8_t VRegA_10x(uint16_t inst_data) const;
- uint4_t VRegA_11n(uint16_t inst_data) const;
- uint8_t VRegA_11x(uint16_t inst_data) const;
- uint4_t VRegA_12x(uint16_t inst_data) const;
- uint8_t VRegA_21c(uint16_t inst_data) const;
- uint8_t VRegA_21h(uint16_t inst_data) const;
- uint8_t VRegA_21s(uint16_t inst_data) const;
- uint8_t VRegA_21t(uint16_t inst_data) const;
- uint8_t VRegA_22b(uint16_t inst_data) const;
- uint4_t VRegA_22c(uint16_t inst_data) const;
- uint4_t VRegA_22s(uint16_t inst_data) const;
- uint4_t VRegA_22t(uint16_t inst_data) const;
- uint8_t VRegA_22x(uint16_t inst_data) const;
- uint8_t VRegA_23x(uint16_t inst_data) const;
- uint8_t VRegA_31c(uint16_t inst_data) const;
- uint8_t VRegA_31i(uint16_t inst_data) const;
- uint8_t VRegA_31t(uint16_t inst_data) const;
- uint4_t VRegA_35c(uint16_t inst_data) const;
- uint8_t VRegA_3rc(uint16_t inst_data) const;
- uint8_t VRegA_51l(uint16_t inst_data) const;
- uint4_t VRegA_45cc(uint16_t inst_data) const;
- uint8_t VRegA_4rcc(uint16_t inst_data) const;
-
- // VRegB
- bool HasVRegB() const;
- int32_t VRegB() const;
-
- bool HasWideVRegB() const;
- uint64_t WideVRegB() const;
-
- int4_t VRegB_11n() const {
- return VRegB_11n(Fetch16(0));
- }
- uint4_t VRegB_12x() const {
- return VRegB_12x(Fetch16(0));
- }
- uint16_t VRegB_21c() const;
- uint16_t VRegB_21h() const;
- int16_t VRegB_21s() const;
- int16_t VRegB_21t() const;
- uint8_t VRegB_22b() const;
- uint4_t VRegB_22c() const {
- return VRegB_22c(Fetch16(0));
- }
- uint4_t VRegB_22s() const {
- return VRegB_22s(Fetch16(0));
- }
- uint4_t VRegB_22t() const {
- return VRegB_22t(Fetch16(0));
- }
- uint16_t VRegB_22x() const;
- uint8_t VRegB_23x() const;
- uint32_t VRegB_31c() const;
- int32_t VRegB_31i() const;
- int32_t VRegB_31t() const;
- uint16_t VRegB_32x() const;
- uint16_t VRegB_35c() const;
- uint16_t VRegB_3rc() const;
- uint64_t VRegB_51l() const; // vB_wide
- uint16_t VRegB_45cc() const;
- uint16_t VRegB_4rcc() const;
-
- // The following methods return the vB operand for all instruction formats where it is encoded in
- // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned
- // value is decoded from it.
- int4_t VRegB_11n(uint16_t inst_data) const;
- uint4_t VRegB_12x(uint16_t inst_data) const;
- uint4_t VRegB_22c(uint16_t inst_data) const;
- uint4_t VRegB_22s(uint16_t inst_data) const;
- uint4_t VRegB_22t(uint16_t inst_data) const;
-
- // VRegC
- bool HasVRegC() const;
- int32_t VRegC() const;
-
- int8_t VRegC_22b() const;
- uint16_t VRegC_22c() const;
- int16_t VRegC_22s() const;
- int16_t VRegC_22t() const;
- uint8_t VRegC_23x() const;
- uint4_t VRegC_35c() const;
- uint16_t VRegC_3rc() const;
- uint4_t VRegC_45cc() const;
- uint16_t VRegC_4rcc() const;
-
-
- // VRegH
- bool HasVRegH() const;
- int32_t VRegH() const;
- uint16_t VRegH_45cc() const;
- uint16_t VRegH_4rcc() const;
-
- // Fills the given array with the 'arg' array of the instruction.
- bool HasVarArgs() const;
- void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const;
- void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const {
- return GetVarArgs(args, Fetch16(0));
- }
-
- // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first
- // 16 bits of instruction.
- Code Opcode(uint16_t inst_data) const {
- DCHECK_EQ(inst_data, Fetch16(0));
- return static_cast<Code>(inst_data & 0xFF);
- }
-
- // Returns the opcode field of the instruction from the first 16 bits of instruction.
- Code Opcode() const {
- return Opcode(Fetch16(0));
- }
-
- void SetOpcode(Code opcode) {
- DCHECK_LT(static_cast<uint16_t>(opcode), 256u);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode);
- }
-
- void SetVRegA_10x(uint8_t val) {
- DCHECK(FormatOf(Opcode()) == k10x);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[0] = (val << 8) | (insns[0] & 0x00ff);
- }
-
- void SetVRegB_3rc(uint16_t val) {
- DCHECK(FormatOf(Opcode()) == k3rc);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[1] = val;
- }
-
- void SetVRegB_35c(uint16_t val) {
- DCHECK(FormatOf(Opcode()) == k35c);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[1] = val;
- }
-
- void SetVRegC_22c(uint16_t val) {
- DCHECK(FormatOf(Opcode()) == k22c);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[1] = val;
- }
-
- void SetVRegA_21c(uint8_t val) {
- DCHECK(FormatOf(Opcode()) == k21c);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[0] = (val << 8) | (insns[0] & 0x00ff);
- }
-
- void SetVRegB_21c(uint16_t val) {
- DCHECK(FormatOf(Opcode()) == k21c);
- uint16_t* insns = reinterpret_cast<uint16_t*>(this);
- insns[1] = val;
- }
-
- // Returns the format of the given opcode.
- static Format FormatOf(Code opcode) {
- return kInstructionDescriptors[opcode].format;
- }
-
- // Returns the index type of the given opcode.
- static IndexType IndexTypeOf(Code opcode) {
- return kInstructionDescriptors[opcode].index_type;
- }
-
- // Returns the flags for the given opcode.
- static uint8_t FlagsOf(Code opcode) {
- return kInstructionDescriptors[opcode].flags;
- }
-
- // Return the verify flags for the given opcode.
- static uint32_t VerifyFlagsOf(Code opcode) {
- return kInstructionDescriptors[opcode].verify_flags;
- }
-
- // Returns true if this instruction is a branch.
- bool IsBranch() const {
- return (kInstructionDescriptors[Opcode()].flags & kBranch) != 0;
- }
-
- // Returns true if this instruction is a unconditional branch.
- bool IsUnconditional() const {
- return (kInstructionDescriptors[Opcode()].flags & kUnconditional) != 0;
- }
-
- // Returns the branch offset if this instruction is a branch.
- int32_t GetTargetOffset() const;
-
- // Returns true if the instruction allows control flow to go to the following instruction.
- bool CanFlowThrough() const;
-
- // Returns true if the instruction is a quickened instruction.
- bool IsQuickened() const {
- return (kInstructionDescriptors[Opcode()].index_type == kIndexFieldOffset) ||
- (kInstructionDescriptors[Opcode()].index_type == kIndexVtableOffset);
- }
-
- // Returns true if this instruction is a switch.
- bool IsSwitch() const {
- return (kInstructionDescriptors[Opcode()].flags & kSwitch) != 0;
- }
-
- // Returns true if this instruction can throw.
- bool IsThrow() const {
- return (kInstructionDescriptors[Opcode()].flags & kThrow) != 0;
- }
-
- // Determine if the instruction is any of 'return' instructions.
- bool IsReturn() const {
- return (kInstructionDescriptors[Opcode()].flags & kReturn) != 0;
- }
-
- // Determine if this instruction ends execution of its basic block.
- bool IsBasicBlockEnd() const {
- return IsBranch() || IsReturn() || Opcode() == THROW;
- }
-
- // Determine if this instruction is an invoke.
- bool IsInvoke() const {
- return (kInstructionDescriptors[Opcode()].flags & kInvoke) != 0;
- }
-
- // Determine if this instruction is experimental.
- bool IsExperimental() const {
- return (kInstructionDescriptors[Opcode()].flags & kExperimental) != 0;
- }
-
- int GetVerifyTypeArgumentA() const {
- return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegA | kVerifyRegAWide));
- }
-
- int GetVerifyTypeArgumentB() const {
- return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegB | kVerifyRegBField |
- kVerifyRegBMethod | kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType |
- kVerifyRegBWide));
- }
-
- int GetVerifyTypeArgumentC() const {
- return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegC | kVerifyRegCField |
- kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide));
- }
-
- int GetVerifyTypeArgumentH() const {
- return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRegHPrototype);
- }
-
- int GetVerifyExtraFlags() const {
- return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyArrayData |
- kVerifyBranchTarget | kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgNonZero |
- kVerifyVarArgRange | kVerifyVarArgRangeNonZero | kVerifyError));
- }
-
- bool GetVerifyIsRuntimeOnly() const {
- return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRuntimeOnly) != 0;
- }
-
- // Get the dex PC of this instruction as a offset in code units from the beginning of insns.
- uint32_t GetDexPc(const uint16_t* insns) const {
- return (reinterpret_cast<const uint16_t*>(this) - insns);
- }
-
- // Dump decoded version of instruction
- std::string DumpString(const DexFile*) const;
-
- // Dump code_units worth of this instruction, padding to code_units for shorter instructions
- std::string DumpHex(size_t code_units) const;
-
- // Little-endian dump code_units worth of this instruction, padding to code_units for
- // shorter instructions
- std::string DumpHexLE(size_t instr_code_units) const;
-
- uint16_t Fetch16(size_t offset) const {
- const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
- return insns[offset];
- }
-
- private:
- size_t SizeInCodeUnitsComplexOpcode() const;
-
- // Return how many code unit words are required to compute the size of the opcode.
- size_t CodeUnitsRequiredForSizeOfComplexOpcode() const;
-
- uint32_t Fetch32(size_t offset) const {
- return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16));
- }
-
- uint4_t InstA() const {
- return InstA(Fetch16(0));
- }
-
- uint4_t InstB() const {
- return InstB(Fetch16(0));
- }
-
- uint8_t InstAA() const {
- return InstAA(Fetch16(0));
- }
-
- uint4_t InstA(uint16_t inst_data) const {
- DCHECK_EQ(inst_data, Fetch16(0));
- return static_cast<uint4_t>((inst_data >> 8) & 0x0f);
- }
-
- uint4_t InstB(uint16_t inst_data) const {
- DCHECK_EQ(inst_data, Fetch16(0));
- return static_cast<uint4_t>(inst_data >> 12);
- }
-
- uint8_t InstAA(uint16_t inst_data) const {
- DCHECK_EQ(inst_data, Fetch16(0));
- return static_cast<uint8_t>(inst_data >> 8);
- }
-
- static const char* const kInstructionNames[];
-
- static const InstructionDescriptor kInstructionDescriptors[];
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
-};
-std::ostream& operator<<(std::ostream& os, const Instruction::Code& code);
-std::ostream& operator<<(std::ostream& os, const Instruction::Format& format);
-std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags);
-std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags);
-
-// Base class for accessing instruction operands. Unifies operand
-// access for instructions that have range and varargs forms
-// (e.g. invoke-polymoprhic/range and invoke-polymorphic).
-class InstructionOperands {
- public:
- explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {}
- virtual ~InstructionOperands() {}
- virtual uint32_t GetOperand(size_t index) const = 0;
- size_t GetNumberOfOperands() const { return num_operands_; }
-
- private:
- size_t num_operands_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands);
-};
-
-// Class for accessing operands for instructions with a range format
-// (e.g. 3rc and 4rcc).
-class RangeInstructionOperands FINAL : public InstructionOperands {
- public:
- RangeInstructionOperands(uint32_t first_operand, size_t num_operands)
- : InstructionOperands(num_operands), first_operand_(first_operand) {}
- ~RangeInstructionOperands() {}
- uint32_t GetOperand(size_t operand_index) const OVERRIDE;
-
- private:
- const uint32_t first_operand_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands);
-};
-
-// Class for accessing operands for instructions with a variable
-// number of arguments format (e.g. 35c and 45cc).
-class VarArgsInstructionOperands FINAL : public InstructionOperands {
- public:
- VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs],
- size_t num_operands)
- : InstructionOperands(num_operands), operands_(operands) {}
- ~VarArgsInstructionOperands() {}
- uint32_t GetOperand(size_t operand_index) const OVERRIDE;
-
- private:
- const uint32_t (&operands_)[Instruction::kMaxVarArgRegs];
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands);
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_H_
diff --git a/runtime/dex/dex_instruction_iterator.h b/runtime/dex/dex_instruction_iterator.h
deleted file mode 100644
index c1b3118f85..0000000000
--- a/runtime/dex/dex_instruction_iterator.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * 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_DEX_DEX_INSTRUCTION_ITERATOR_H_
-#define ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_
-
-#include <iterator>
-
-#include <android-base/logging.h>
-
-#include "base/macros.h"
-#include "dex_instruction.h"
-
-namespace art {
-
-class DexInstructionPcPair {
- public:
- ALWAYS_INLINE const Instruction& Inst() const {
- return *Instruction::At(instructions_ + DexPc());
- }
-
- ALWAYS_INLINE const Instruction* operator->() const {
- return &Inst();
- }
-
- ALWAYS_INLINE uint32_t DexPc() const {
- return dex_pc_;
- }
-
- ALWAYS_INLINE const uint16_t* Instructions() const {
- return instructions_;
- }
-
- protected:
- explicit DexInstructionPcPair(const uint16_t* instructions, uint32_t dex_pc)
- : instructions_(instructions), dex_pc_(dex_pc) {}
-
- const uint16_t* instructions_ = nullptr;
- uint32_t dex_pc_ = 0;
-
- friend class DexInstructionIteratorBase;
- friend class DexInstructionIterator;
- friend class SafeDexInstructionIterator;
-};
-
-// Base helper class to prevent duplicated comparators.
-class DexInstructionIteratorBase : public
- std::iterator<std::forward_iterator_tag, DexInstructionPcPair> {
- public:
- using value_type = std::iterator<std::forward_iterator_tag, DexInstructionPcPair>::value_type;
- using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type;
-
- DexInstructionIteratorBase() = default;
- explicit DexInstructionIteratorBase(const Instruction* inst, uint32_t dex_pc)
- : data_(reinterpret_cast<const uint16_t*>(inst), dex_pc) {}
-
- const Instruction& Inst() const {
- return data_.Inst();
- }
-
- // Return the dex pc for an iterator compared to the code item begin.
- ALWAYS_INLINE uint32_t DexPc() const {
- return data_.DexPc();
- }
-
- // Instructions from the start of the code item.
- ALWAYS_INLINE const uint16_t* Instructions() const {
- return data_.Instructions();
- }
-
- protected:
- DexInstructionPcPair data_;
-};
-
-static ALWAYS_INLINE inline bool operator==(const DexInstructionIteratorBase& lhs,
- const DexInstructionIteratorBase& rhs) {
- DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items.";
- return lhs.DexPc() == rhs.DexPc();
-}
-
-static inline bool operator!=(const DexInstructionIteratorBase& lhs,
- const DexInstructionIteratorBase& rhs) {
- return !(lhs == rhs);
-}
-
-static inline bool operator<(const DexInstructionIteratorBase& lhs,
- const DexInstructionIteratorBase& rhs) {
- DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items.";
- return lhs.DexPc() < rhs.DexPc();
-}
-
-static inline bool operator>(const DexInstructionIteratorBase& lhs,
- const DexInstructionIteratorBase& rhs) {
- return rhs < lhs;
-}
-
-static inline bool operator<=(const DexInstructionIteratorBase& lhs,
- const DexInstructionIteratorBase& rhs) {
- return !(rhs < lhs);
-}
-
-static inline bool operator>=(const DexInstructionIteratorBase& lhs,
- const DexInstructionIteratorBase& rhs) {
- return !(lhs < rhs);
-}
-
-// A helper class for a code_item's instructions using range based for loop syntax.
-class DexInstructionIterator : public DexInstructionIteratorBase {
- public:
- using DexInstructionIteratorBase::DexInstructionIteratorBase;
-
- explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc)
- : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {}
-
- explicit DexInstructionIterator(const DexInstructionPcPair& pair)
- : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {}
-
- // Value after modification.
- DexInstructionIterator& operator++() {
- data_.dex_pc_ += Inst().SizeInCodeUnits();
- return *this;
- }
-
- // Value before modification.
- DexInstructionIterator operator++(int) {
- DexInstructionIterator temp = *this;
- ++*this;
- return temp;
- }
-
- const value_type& operator*() const {
- return data_;
- }
-
- const Instruction* operator->() const {
- return &data_.Inst();
- }
-
- // Return the dex pc for the iterator.
- ALWAYS_INLINE uint32_t DexPc() const {
- return data_.DexPc();
- }
-};
-
-// A safe version of DexInstructionIterator that is guaranteed to not go past the end of the code
-// item.
-class SafeDexInstructionIterator : public DexInstructionIteratorBase {
- public:
- explicit SafeDexInstructionIterator(const DexInstructionIteratorBase& start,
- const DexInstructionIteratorBase& end)
- : DexInstructionIteratorBase(&start.Inst(), start.DexPc())
- , num_code_units_(end.DexPc()) {
- DCHECK_EQ(start.Instructions(), end.Instructions())
- << "start and end must be in the same code item.";
- }
-
- // Value after modification, does not read past the end of the allowed region. May increment past
- // the end of the code item though.
- SafeDexInstructionIterator& operator++() {
- AssertValid();
- const size_t size_code_units = Inst().CodeUnitsRequiredForSizeComputation();
- const size_t available = NumCodeUnits() - DexPc();
- if (UNLIKELY(size_code_units > available)) {
- error_state_ = true;
- return *this;
- }
- const size_t instruction_code_units = Inst().SizeInCodeUnits();
- if (UNLIKELY(instruction_code_units > available)) {
- error_state_ = true;
- return *this;
- }
- data_.dex_pc_ += instruction_code_units;
- return *this;
- }
-
- // Value before modification.
- SafeDexInstructionIterator operator++(int) {
- SafeDexInstructionIterator temp = *this;
- ++*this;
- return temp;
- }
-
- const value_type& operator*() const {
- AssertValid();
- return data_;
- }
-
- const Instruction* operator->() const {
- AssertValid();
- return &data_.Inst();
- }
-
- // Return the current instruction of the iterator.
- ALWAYS_INLINE const Instruction& Inst() const {
- return data_.Inst();
- }
-
- const uint16_t* Instructions() const {
- return data_.Instructions();
- }
-
- // Returns true if the iterator is in an error state. This occurs when an instruction couldn't
- // have its size computed without reading past the end iterator.
- bool IsErrorState() const {
- return error_state_;
- }
-
- private:
- ALWAYS_INLINE void AssertValid() const {
- DCHECK(!IsErrorState());
- DCHECK_LT(DexPc(), NumCodeUnits());
- }
-
- ALWAYS_INLINE uint32_t NumCodeUnits() const {
- return num_code_units_;
- }
-
- const uint32_t num_code_units_ = 0;
- bool error_state_ = false;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_ITERATOR_H_
diff --git a/runtime/dex/dex_instruction_list.h b/runtime/dex/dex_instruction_list.h
deleted file mode 100644
index aa63fadb66..0000000000
--- a/runtime/dex/dex_instruction_list.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_DEX_INSTRUCTION_LIST_H_
-#define ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_
-
-// V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags);
-#define DEX_INSTRUCTION_LIST(V) \
- V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \
- V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x04, MOVE_WIDE, "move-wide", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x05, MOVE_WIDE_FROM16, "move-wide/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x06, MOVE_WIDE_16, "move-wide/16", k32x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x07, MOVE_OBJECT, "move-object", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x08, MOVE_OBJECT_FROM16, "move-object/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x09, MOVE_OBJECT_16, "move-object/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x0A, MOVE_RESULT, "move-result", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
- V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \
- V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
- V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
- V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
- V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
- V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \
- V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
- V(0x12, CONST_4, "const/4", k11n, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
- V(0x13, CONST_16, "const/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
- V(0x14, CONST, "const", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
- V(0x15, CONST_HIGH16, "const/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
- V(0x16, CONST_WIDE_16, "const-wide/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
- V(0x17, CONST_WIDE_32, "const-wide/32", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
- V(0x18, CONST_WIDE, "const-wide", k51l, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
- V(0x19, CONST_WIDE_HIGH16, "const-wide/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
- V(0x1A, CONST_STRING, "const-string", k21c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \
- V(0x1B, CONST_STRING_JUMBO, "const-string/jumbo", k31c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \
- V(0x1C, CONST_CLASS, "const-class", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \
- V(0x1D, MONITOR_ENTER, "monitor-enter", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \
- V(0x1E, MONITOR_EXIT, "monitor-exit", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \
- V(0x1F, CHECK_CAST, "check-cast", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \
- V(0x20, INSTANCE_OF, "instance-of", k22c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB | kVerifyRegCType) \
- V(0x21, ARRAY_LENGTH, "array-length", k12x, kIndexNone, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB) \
- V(0x22, NEW_INSTANCE, "new-instance", k21c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegBNewInstance) \
- V(0x23, NEW_ARRAY, "new-array", k22c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegB | kVerifyRegCNewArray) \
- V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArg) \
- V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArgRange) \
- V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyArrayData) \
- V(0x27, THROW, "throw", k11x, kIndexNone, kThrow, 0, kVerifyRegA) \
- V(0x28, GOTO, "goto", k10t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
- V(0x29, GOTO_16, "goto/16", k20t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
- V(0x2A, GOTO_32, "goto/32", k30t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
- V(0x2B, PACKED_SWITCH, "packed-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \
- V(0x2C, SPARSE_SWITCH, "sparse-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \
- V(0x2D, CMPL_FLOAT, "cmpl-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x2E, CMPG_FLOAT, "cmpg-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x2F, CMPL_DOUBLE, "cmpl-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x30, CMPG_DOUBLE, "cmpg-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x31, CMP_LONG, "cmp-long", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x32, IF_EQ, "if-eq", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
- V(0x33, IF_NE, "if-ne", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
- V(0x34, IF_LT, "if-lt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
- V(0x35, IF_GE, "if-ge", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
- V(0x36, IF_GT, "if-gt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
- V(0x37, IF_LE, "if-le", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
- V(0x38, IF_EQZ, "if-eqz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
- V(0x39, IF_NEZ, "if-nez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
- V(0x3A, IF_LTZ, "if-ltz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
- V(0x3B, IF_GEZ, "if-gez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
- V(0x3C, IF_GTZ, "if-gtz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
- V(0x3D, IF_LEZ, "if-lez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
- V(0x3E, UNUSED_3E, "unused-3e", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x3F, UNUSED_3F, "unused-3f", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x40, UNUSED_40, "unused-40", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x41, UNUSED_41, "unused-41", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x42, UNUSED_42, "unused-42", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x43, UNUSED_43, "unused-43", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x44, AGET, "aget", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x45, AGET_WIDE, "aget-wide", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
- V(0x46, AGET_OBJECT, "aget-object", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x47, AGET_BOOLEAN, "aget-boolean", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x48, AGET_BYTE, "aget-byte", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x49, AGET_CHAR, "aget-char", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x4A, AGET_SHORT, "aget-short", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x4B, APUT, "aput", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x4C, APUT_WIDE, "aput-wide", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
- V(0x4D, APUT_OBJECT, "aput-object", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x4E, APUT_BOOLEAN, "aput-boolean", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x4F, APUT_BYTE, "aput-byte", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x50, APUT_CHAR, "aput-char", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x51, APUT_SHORT, "aput-short", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x52, IGET, "iget", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x53, IGET_WIDE, "iget-wide", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
- V(0x54, IGET_OBJECT, "iget-object", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x55, IGET_BOOLEAN, "iget-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x56, IGET_BYTE, "iget-byte", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x57, IGET_CHAR, "iget-char", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x58, IGET_SHORT, "iget-short", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x59, IPUT, "iput", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x5A, IPUT_WIDE, "iput-wide", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
- V(0x5B, IPUT_OBJECT, "iput-object", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x5C, IPUT_BOOLEAN, "iput-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x5D, IPUT_BYTE, "iput-byte", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x5E, IPUT_CHAR, "iput-char", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x5F, IPUT_SHORT, "iput-short", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
- V(0x60, SGET, "sget", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x61, SGET_WIDE, "sget-wide", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \
- V(0x62, SGET_OBJECT, "sget-object", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x63, SGET_BOOLEAN, "sget-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x64, SGET_BYTE, "sget-byte", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x65, SGET_CHAR, "sget-char", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x66, SGET_SHORT, "sget-short", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x67, SPUT, "sput", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x68, SPUT_WIDE, "sput-wide", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \
- V(0x69, SPUT_OBJECT, "sput-object", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x6A, SPUT_BOOLEAN, "sput-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x6B, SPUT_BYTE, "sput-byte", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x6C, SPUT_CHAR, "sput-char", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x6D, SPUT_SHORT, "sput-short", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
- V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
- V(0x6F, INVOKE_SUPER, "invoke-super", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
- V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
- V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \
- V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
- V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
- V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
- V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
- V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
- V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRange) \
- V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
- V(0x79, UNUSED_79, "unused-79", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x7A, UNUSED_7A, "unused-7a", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0x7B, NEG_INT, "neg-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x7C, NOT_INT, "not-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x7D, NEG_LONG, "neg-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x7E, NOT_LONG, "not-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x7F, NEG_FLOAT, "neg-float", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
- V(0x80, NEG_DOUBLE, "neg-double", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x81, INT_TO_LONG, "int-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
- V(0x82, INT_TO_FLOAT, "int-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
- V(0x83, INT_TO_DOUBLE, "int-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
- V(0x84, LONG_TO_INT, "long-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
- V(0x85, LONG_TO_FLOAT, "long-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
- V(0x86, LONG_TO_DOUBLE, "long-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x87, FLOAT_TO_INT, "float-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
- V(0x88, FLOAT_TO_LONG, "float-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
- V(0x89, FLOAT_TO_DOUBLE, "float-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
- V(0x8A, DOUBLE_TO_INT, "double-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
- V(0x8B, DOUBLE_TO_LONG, "double-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \
- V(0x8C, DOUBLE_TO_FLOAT, "double-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
- V(0x8D, INT_TO_BYTE, "int-to-byte", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
- V(0x8E, INT_TO_CHAR, "int-to-char", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
- V(0x8F, INT_TO_SHORT, "int-to-short", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
- V(0x90, ADD_INT, "add-int", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x91, SUB_INT, "sub-int", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x92, MUL_INT, "mul-int", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x93, DIV_INT, "div-int", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x94, REM_INT, "rem-int", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x95, AND_INT, "and-int", k23x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x96, OR_INT, "or-int", k23x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x97, XOR_INT, "xor-int", k23x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x98, SHL_INT, "shl-int", k23x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x99, SHR_INT, "shr-int", k23x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x9A, USHR_INT, "ushr-int", k23x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0x9B, ADD_LONG, "add-long", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x9C, SUB_LONG, "sub-long", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x9D, MUL_LONG, "mul-long", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x9E, DIV_LONG, "div-long", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0x9F, REM_LONG, "rem-long", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xA0, AND_LONG, "and-long", k23x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xA1, OR_LONG, "or-long", k23x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xA2, XOR_LONG, "xor-long", k23x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xA3, SHL_LONG, "shl-long", k23x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
- V(0xA4, SHR_LONG, "shr-long", k23x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
- V(0xA5, USHR_LONG, "ushr-long", k23x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
- V(0xA6, ADD_FLOAT, "add-float", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0xA7, SUB_FLOAT, "sub-float", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0xA8, MUL_FLOAT, "mul-float", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0xA9, DIV_FLOAT, "div-float", k23x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0xAA, REM_FLOAT, "rem-float", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
- V(0xAB, ADD_DOUBLE, "add-double", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xAC, SUB_DOUBLE, "sub-double", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xAD, MUL_DOUBLE, "mul-double", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xAE, DIV_DOUBLE, "div-double", k23x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xAF, REM_DOUBLE, "rem-double", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
- V(0xB0, ADD_INT_2ADDR, "add-int/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \
- V(0xB1, SUB_INT_2ADDR, "sub-int/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \
- V(0xB2, MUL_INT_2ADDR, "mul-int/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \
- V(0xB3, DIV_INT_2ADDR, "div-int/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB) \
- V(0xB4, REM_INT_2ADDR, "rem-int/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB) \
- V(0xB5, AND_INT_2ADDR, "and-int/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB) \
- V(0xB6, OR_INT_2ADDR, "or-int/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB) \
- V(0xB7, XOR_INT_2ADDR, "xor-int/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB) \
- V(0xB8, SHL_INT_2ADDR, "shl-int/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB) \
- V(0xB9, SHR_INT_2ADDR, "shr-int/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB) \
- V(0xBA, USHR_INT_2ADDR, "ushr-int/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB) \
- V(0xBB, ADD_LONG_2ADDR, "add-long/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xBC, SUB_LONG_2ADDR, "sub-long/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xBD, MUL_LONG_2ADDR, "mul-long/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xBE, DIV_LONG_2ADDR, "div-long/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xBF, REM_LONG_2ADDR, "rem-long/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xC0, AND_LONG_2ADDR, "and-long/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xC1, OR_LONG_2ADDR, "or-long/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xC2, XOR_LONG_2ADDR, "xor-long/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xC3, SHL_LONG_2ADDR, "shl-long/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegB) \
- V(0xC4, SHR_LONG_2ADDR, "shr-long/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegB) \
- V(0xC5, USHR_LONG_2ADDR, "ushr-long/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegB) \
- V(0xC6, ADD_FLOAT_2ADDR, "add-float/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \
- V(0xC7, SUB_FLOAT_2ADDR, "sub-float/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \
- V(0xC8, MUL_FLOAT_2ADDR, "mul-float/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \
- V(0xC9, DIV_FLOAT_2ADDR, "div-float/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB) \
- V(0xCA, REM_FLOAT_2ADDR, "rem-float/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB) \
- V(0xCB, ADD_DOUBLE_2ADDR, "add-double/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xCC, SUB_DOUBLE_2ADDR, "sub-double/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xCD, MUL_DOUBLE_2ADDR, "mul-double/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xCE, DIV_DOUBLE_2ADDR, "div-double/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xCF, REM_DOUBLE_2ADDR, "rem-double/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \
- V(0xD0, ADD_INT_LIT16, "add-int/lit16", k22s, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD1, RSUB_INT, "rsub-int", k22s, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD2, MUL_INT_LIT16, "mul-int/lit16", k22s, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD3, DIV_INT_LIT16, "div-int/lit16", k22s, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD4, REM_INT_LIT16, "rem-int/lit16", k22s, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD5, AND_INT_LIT16, "and-int/lit16", k22s, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD6, OR_INT_LIT16, "or-int/lit16", k22s, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD7, XOR_INT_LIT16, "xor-int/lit16", k22s, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD8, ADD_INT_LIT8, "add-int/lit8", k22b, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xD9, RSUB_INT_LIT8, "rsub-int/lit8", k22b, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xDA, MUL_INT_LIT8, "mul-int/lit8", k22b, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xDB, DIV_INT_LIT8, "div-int/lit8", k22b, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xDC, REM_INT_LIT8, "rem-int/lit8", k22b, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xDD, AND_INT_LIT8, "and-int/lit8", k22b, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xDE, OR_INT_LIT8, "or-int/lit8", k22b, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xDF, XOR_INT_LIT8, "xor-int/lit8", k22b, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, kIndexNone, kContinue, kShl | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, kIndexNone, kContinue, kShr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, kIndexNone, kContinue, kUshr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
- V(0xE3, IGET_QUICK, "iget-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xE6, IPUT_QUICK, "iput-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \
- V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \
- V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xEF, IGET_BOOLEAN_QUICK, "iget-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xF0, IGET_BYTE_QUICK, "iget-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xF1, IGET_CHAR_QUICK, "iget-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
- V(0xF3, UNUSED_F3, "unused-f3", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xF4, UNUSED_F4, "unused-f4", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xF5, UNUSED_F5, "unused-f5", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xF6, UNUSED_F6, "unused-f6", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xF7, UNUSED_F7, "unused-f7", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xF8, UNUSED_F8, "unused-f8", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, 0, kVerifyError) \
- V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
- V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
- V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArg) \
- V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArgRange) \
- V(0xFE, CONST_METHOD_HANDLE, "const-method-handle", k21c, kIndexMethodHandleRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBMethodHandle) \
- V(0xFF, CONST_METHOD_TYPE, "const-method-type", k21c, kIndexProtoRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBPrototype)
-
-#define DEX_INSTRUCTION_FORMAT_LIST(V) \
- V(k10x) \
- V(k12x) \
- V(k11n) \
- V(k11x) \
- V(k10t) \
- V(k20t) \
- V(k22x) \
- V(k21t) \
- V(k21s) \
- V(k21h) \
- V(k21c) \
- V(k23x) \
- V(k22b) \
- V(k22t) \
- V(k22s) \
- V(k22c) \
- V(k32x) \
- V(k30t) \
- V(k31t) \
- V(k31i) \
- V(k31c) \
- V(k35c) \
- V(k3rc) \
- V(k45cc) \
- V(k4rcc) \
- V(k51l)
-
-#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_
-#undef ART_RUNTIME_DEX_DEX_INSTRUCTION_LIST_H_ // the guard in this file is just for cpplint
diff --git a/runtime/dex/dex_instruction_test.cc b/runtime/dex/dex_instruction_test.cc
deleted file mode 100644
index c944085b9e..0000000000
--- a/runtime/dex/dex_instruction_test.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2015 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 "dex_instruction-inl.h"
-#include "dex_instruction_iterator.h"
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StaticGetters, PropertiesOfNopTest) {
- Instruction::Code nop = Instruction::NOP;
- EXPECT_STREQ("nop", Instruction::Name(nop));
- EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop));
- EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop));
- EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop));
- EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop));
-}
-
-static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx,
- uint16_t arg_regs, uint16_t* out) {
- // A = num argument registers
- // B = method_idx
- // C - G = argument registers
- // H = proto_idx
- //
- // op = 0xFA
- //
- // format:
- // AG op BBBB FEDC HHHH
- out[0] = 0;
- out[0] |= (num_args << 12);
- out[0] |= 0x00FA;
-
- out[1] = method_idx;
- out[2] = arg_regs;
- out[3] = proto_idx;
-}
-
-static void Build4rcc(uint16_t num_args, uint16_t method_idx, uint16_t proto_idx,
- uint16_t arg_regs_start, uint16_t* out) {
- // A = num argument registers
- // B = method_idx
- // C = first argument register
- // H = proto_idx
- //
- // op = 0xFB
- //
- // format:
- // AA op BBBB CCCC HHHH
- out[0] = 0;
- out[0] |= (num_args << 8);
- out[0] |= 0x00FB;
-
- out[1] = method_idx;
- out[2] = arg_regs_start;
- out[3] = proto_idx;
-}
-
-TEST(Instruction, PropertiesOf45cc) {
- uint16_t instruction[4];
- Build45cc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
- 0xcafe /* arg_regs */, instruction);
-
- DexInstructionIterator ins(instruction, /*dex_pc*/ 0u);
- ASSERT_EQ(4u, ins->SizeInCodeUnits());
-
- ASSERT_TRUE(ins->HasVRegA());
- ASSERT_EQ(4, ins->VRegA());
- ASSERT_EQ(4u, ins->VRegA_45cc());
- ASSERT_EQ(4u, ins->VRegA_45cc(instruction[0]));
-
- ASSERT_TRUE(ins->HasVRegB());
- ASSERT_EQ(16, ins->VRegB());
- ASSERT_EQ(16u, ins->VRegB_45cc());
-
- ASSERT_TRUE(ins->HasVRegC());
- ASSERT_EQ(0xe, ins->VRegC());
- ASSERT_EQ(0xe, ins->VRegC_45cc());
-
- ASSERT_TRUE(ins->HasVRegH());
- ASSERT_EQ(32, ins->VRegH());
- ASSERT_EQ(32, ins->VRegH_45cc());
-
- ASSERT_TRUE(ins->HasVarArgs());
-
- uint32_t arg_regs[Instruction::kMaxVarArgRegs];
- ins->GetVarArgs(arg_regs);
- ASSERT_EQ(0xeu, arg_regs[0]);
- ASSERT_EQ(0xfu, arg_regs[1]);
- ASSERT_EQ(0xau, arg_regs[2]);
- ASSERT_EQ(0xcu, arg_regs[3]);
-}
-
-TEST(Instruction, PropertiesOf4rcc) {
- uint16_t instruction[4];
- Build4rcc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
- 0xcafe /* arg_regs */, instruction);
-
- DexInstructionIterator ins(instruction, /*dex_pc*/ 0u);
- ASSERT_EQ(4u, ins->SizeInCodeUnits());
-
- ASSERT_TRUE(ins->HasVRegA());
- ASSERT_EQ(4, ins->VRegA());
- ASSERT_EQ(4u, ins->VRegA_4rcc());
- ASSERT_EQ(4u, ins->VRegA_4rcc(instruction[0]));
-
- ASSERT_TRUE(ins->HasVRegB());
- ASSERT_EQ(16, ins->VRegB());
- ASSERT_EQ(16u, ins->VRegB_4rcc());
-
- ASSERT_TRUE(ins->HasVRegC());
- ASSERT_EQ(0xcafe, ins->VRegC());
- ASSERT_EQ(0xcafe, ins->VRegC_4rcc());
-
- ASSERT_TRUE(ins->HasVRegH());
- ASSERT_EQ(32, ins->VRegH());
- ASSERT_EQ(32, ins->VRegH_4rcc());
-
- ASSERT_FALSE(ins->HasVarArgs());
-}
-
-static void Build35c(uint16_t* out,
- Instruction::Code code,
- uint16_t method_idx,
- std::vector<uint16_t> args) {
- out[0] = 0;
- out[0] |= (args.size() << 12);
- out[0] |= static_cast<uint16_t>(code);
- out[1] = method_idx;
- size_t i = 0;
- out[2] = 0;
- for (; i < 4 && i < args.size(); ++i) {
- out[2] |= args[i] << (i * 4);
- }
- if (args.size() == 5) {
- out[0] |= args[4] << 8;
- }
-}
-
-static std::string DumpInst35c(Instruction::Code code,
- uint16_t method_idx,
- std::vector<uint16_t> args) {
- uint16_t inst[6] = {};
- Build35c(inst, code, method_idx, args);
- return Instruction::At(inst)->DumpString(nullptr);
-}
-
-TEST(Instruction, DumpString) {
- EXPECT_EQ(DumpInst35c(Instruction::FILLED_NEW_ARRAY, 1234, {3, 2}),
- "filled-new-array {v3, v2}, type@1234");
- EXPECT_EQ(DumpInst35c(Instruction::INVOKE_VIRTUAL, 1234, {3, 2, 1, 5, 6}),
- "invoke-virtual {v3, v2, v1, v5, v6}, thing@1234");
- EXPECT_EQ(DumpInst35c(Instruction::INVOKE_VIRTUAL_QUICK, 1234, {3, 2, 1, 5}),
- "invoke-virtual-quick {v3, v2, v1, v5}, thing@1234");
- EXPECT_EQ(DumpInst35c(Instruction::INVOKE_CUSTOM, 1234, {3, 2, 1}),
- "invoke-custom {v3, v2, v1}, thing@1234");
-}
-
-} // namespace art
diff --git a/runtime/dex/dex_instruction_utils.h b/runtime/dex/dex_instruction_utils.h
deleted file mode 100644
index 27501927e7..0000000000
--- a/runtime/dex/dex_instruction_utils.h
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_
-#define ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_
-
-#include "dex_instruction.h"
-
-namespace art {
-
-// Dex invoke type corresponds to the ordering of INVOKE instructions;
-// this order is the same for range and non-range invokes.
-enum DexInvokeType : uint8_t {
- kDexInvokeVirtual = 0, // invoke-virtual, invoke-virtual-range
- kDexInvokeSuper, // invoke-super, invoke-super-range
- kDexInvokeDirect, // invoke-direct, invoke-direct-range
- kDexInvokeStatic, // invoke-static, invoke-static-range
- kDexInvokeInterface, // invoke-interface, invoke-interface-range
- kDexInvokeTypeCount
-};
-
-// Dex instruction memory access types correspond to the ordering of GET/PUT instructions;
-// this order is the same for IGET, IPUT, SGET, SPUT, AGET and APUT.
-enum DexMemAccessType : uint8_t {
- kDexMemAccessWord = 0, // op 0; int or float, the actual type is not encoded.
- kDexMemAccessWide, // op_WIDE 1; long or double, the actual type is not encoded.
- kDexMemAccessObject, // op_OBJECT 2; the actual reference type is not encoded.
- kDexMemAccessBoolean, // op_BOOLEAN 3
- kDexMemAccessByte, // op_BYTE 4
- kDexMemAccessChar, // op_CHAR 5
- kDexMemAccessShort, // op_SHORT 6
- kDexMemAccessTypeCount
-};
-
-std::ostream& operator<<(std::ostream& os, const DexMemAccessType& type);
-
-// NOTE: The following functions disregard quickened instructions.
-
-// By "direct" const we mean to exclude const-string and const-class
-// which load data from somewhere else, i.e. indirectly.
-constexpr bool IsInstructionDirectConst(Instruction::Code opcode) {
- return Instruction::CONST_4 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16;
-}
-
-constexpr bool IsInstructionConstWide(Instruction::Code opcode) {
- return Instruction::CONST_WIDE_16 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16;
-}
-
-constexpr bool IsInstructionReturn(Instruction::Code opcode) {
- return Instruction::RETURN_VOID <= opcode && opcode <= Instruction::RETURN_OBJECT;
-}
-
-constexpr bool IsInstructionInvoke(Instruction::Code opcode) {
- return Instruction::INVOKE_VIRTUAL <= opcode && opcode <= Instruction::INVOKE_INTERFACE_RANGE &&
- opcode != Instruction::RETURN_VOID_NO_BARRIER;
-}
-
-constexpr bool IsInstructionQuickInvoke(Instruction::Code opcode) {
- return opcode == Instruction::INVOKE_VIRTUAL_QUICK ||
- opcode == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
-}
-
-constexpr bool IsInstructionInvokeStatic(Instruction::Code opcode) {
- return opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE;
-}
-
-constexpr bool IsInstructionGoto(Instruction::Code opcode) {
- return Instruction::GOTO <= opcode && opcode <= Instruction::GOTO_32;
-}
-
-constexpr bool IsInstructionIfCc(Instruction::Code opcode) {
- return Instruction::IF_EQ <= opcode && opcode <= Instruction::IF_LE;
-}
-
-constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) {
- return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ;
-}
-
-constexpr bool IsInstructionIGet(Instruction::Code code) {
- return Instruction::IGET <= code && code <= Instruction::IGET_SHORT;
-}
-
-constexpr bool IsInstructionIPut(Instruction::Code code) {
- return Instruction::IPUT <= code && code <= Instruction::IPUT_SHORT;
-}
-
-constexpr bool IsInstructionSGet(Instruction::Code code) {
- return Instruction::SGET <= code && code <= Instruction::SGET_SHORT;
-}
-
-constexpr bool IsInstructionSPut(Instruction::Code code) {
- return Instruction::SPUT <= code && code <= Instruction::SPUT_SHORT;
-}
-
-constexpr bool IsInstructionAGet(Instruction::Code code) {
- return Instruction::AGET <= code && code <= Instruction::AGET_SHORT;
-}
-
-constexpr bool IsInstructionAPut(Instruction::Code code) {
- return Instruction::APUT <= code && code <= Instruction::APUT_SHORT;
-}
-
-constexpr bool IsInstructionIGetOrIPut(Instruction::Code code) {
- return Instruction::IGET <= code && code <= Instruction::IPUT_SHORT;
-}
-
-constexpr bool IsInstructionIGetQuickOrIPutQuick(Instruction::Code code) {
- return (code >= Instruction::IGET_QUICK && code <= Instruction::IPUT_OBJECT_QUICK) ||
- (code >= Instruction::IPUT_BOOLEAN_QUICK && code <= Instruction::IGET_SHORT_QUICK);
-}
-
-constexpr bool IsInstructionSGetOrSPut(Instruction::Code code) {
- return Instruction::SGET <= code && code <= Instruction::SPUT_SHORT;
-}
-
-constexpr bool IsInstructionAGetOrAPut(Instruction::Code code) {
- return Instruction::AGET <= code && code <= Instruction::APUT_SHORT;
-}
-
-constexpr bool IsInstructionBinOp2Addr(Instruction::Code code) {
- return Instruction::ADD_INT_2ADDR <= code && code <= Instruction::REM_DOUBLE_2ADDR;
-}
-
-constexpr bool IsInvokeInstructionRange(Instruction::Code opcode) {
- DCHECK(IsInstructionInvoke(opcode));
- return opcode >= Instruction::INVOKE_VIRTUAL_RANGE;
-}
-
-constexpr DexInvokeType InvokeInstructionType(Instruction::Code opcode) {
- DCHECK(IsInstructionInvoke(opcode));
- return static_cast<DexInvokeType>(IsInvokeInstructionRange(opcode)
- ? (opcode - Instruction::INVOKE_VIRTUAL_RANGE)
- : (opcode - Instruction::INVOKE_VIRTUAL));
-}
-
-constexpr DexMemAccessType IGetMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionIGet(code));
- return static_cast<DexMemAccessType>(code - Instruction::IGET);
-}
-
-constexpr DexMemAccessType IPutMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionIPut(code));
- return static_cast<DexMemAccessType>(code - Instruction::IPUT);
-}
-
-constexpr DexMemAccessType SGetMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionSGet(code));
- return static_cast<DexMemAccessType>(code - Instruction::SGET);
-}
-
-constexpr DexMemAccessType SPutMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionSPut(code));
- return static_cast<DexMemAccessType>(code - Instruction::SPUT);
-}
-
-constexpr DexMemAccessType AGetMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionAGet(code));
- return static_cast<DexMemAccessType>(code - Instruction::AGET);
-}
-
-constexpr DexMemAccessType APutMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionAPut(code));
- return static_cast<DexMemAccessType>(code - Instruction::APUT);
-}
-
-constexpr DexMemAccessType IGetOrIPutMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionIGetOrIPut(code));
- return (code >= Instruction::IPUT) ? IPutMemAccessType(code) : IGetMemAccessType(code);
-}
-
-inline DexMemAccessType IGetQuickOrIPutQuickMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionIGetQuickOrIPutQuick(code));
- switch (code) {
- case Instruction::IGET_QUICK: case Instruction::IPUT_QUICK:
- return kDexMemAccessWord;
- case Instruction::IGET_WIDE_QUICK: case Instruction::IPUT_WIDE_QUICK:
- return kDexMemAccessWide;
- case Instruction::IGET_OBJECT_QUICK: case Instruction::IPUT_OBJECT_QUICK:
- return kDexMemAccessObject;
- case Instruction::IGET_BOOLEAN_QUICK: case Instruction::IPUT_BOOLEAN_QUICK:
- return kDexMemAccessBoolean;
- case Instruction::IGET_BYTE_QUICK: case Instruction::IPUT_BYTE_QUICK:
- return kDexMemAccessByte;
- case Instruction::IGET_CHAR_QUICK: case Instruction::IPUT_CHAR_QUICK:
- return kDexMemAccessChar;
- case Instruction::IGET_SHORT_QUICK: case Instruction::IPUT_SHORT_QUICK:
- return kDexMemAccessShort;
- default:
- LOG(FATAL) << code;
- UNREACHABLE();
- }
-}
-
-constexpr DexMemAccessType SGetOrSPutMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionSGetOrSPut(code));
- return (code >= Instruction::SPUT) ? SPutMemAccessType(code) : SGetMemAccessType(code);
-}
-
-constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) {
- DCHECK(IsInstructionAGetOrAPut(code));
- return (code >= Instruction::APUT) ? APutMemAccessType(code) : AGetMemAccessType(code);
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_DEX_INSTRUCTION_UTILS_H_
diff --git a/runtime/dex/invoke_type.h b/runtime/dex/invoke_type.h
deleted file mode 100644
index 726d269a3e..0000000000
--- a/runtime/dex/invoke_type.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_INVOKE_TYPE_H_
-#define ART_RUNTIME_DEX_INVOKE_TYPE_H_
-
-#include <iosfwd>
-
-namespace art {
-
-enum InvokeType : uint32_t {
- kStatic, // <<static>>
- kDirect, // <<direct>>
- kVirtual, // <<virtual>>
- kSuper, // <<super>>
- kInterface, // <<interface>>
- kPolymorphic, // <<polymorphic>>
- kMaxInvokeType = kPolymorphic
-};
-
-std::ostream& operator<<(std::ostream& os, const InvokeType& rhs);
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_INVOKE_TYPE_H_
diff --git a/runtime/dex/modifiers.cc b/runtime/dex/modifiers.cc
deleted file mode 100644
index 30daefb172..0000000000
--- a/runtime/dex/modifiers.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2011 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 <string>
-
-#include "modifiers.h"
-
-namespace art {
-
-std::string PrettyJavaAccessFlags(uint32_t access_flags) {
- std::string result;
- if ((access_flags & kAccPublic) != 0) {
- result += "public ";
- }
- if ((access_flags & kAccProtected) != 0) {
- result += "protected ";
- }
- if ((access_flags & kAccPrivate) != 0) {
- result += "private ";
- }
- if ((access_flags & kAccFinal) != 0) {
- result += "final ";
- }
- if ((access_flags & kAccStatic) != 0) {
- result += "static ";
- }
- if ((access_flags & kAccAbstract) != 0) {
- result += "abstract ";
- }
- if ((access_flags & kAccInterface) != 0) {
- result += "interface ";
- }
- if ((access_flags & kAccTransient) != 0) {
- result += "transient ";
- }
- if ((access_flags & kAccVolatile) != 0) {
- result += "volatile ";
- }
- if ((access_flags & kAccSynchronized) != 0) {
- result += "synchronized ";
- }
- return result;
-}
-
-} // namespace art
diff --git a/runtime/dex/modifiers.h b/runtime/dex/modifiers.h
deleted file mode 100644
index 2998f602d4..0000000000
--- a/runtime/dex/modifiers.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2012 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_DEX_MODIFIERS_H_
-#define ART_RUNTIME_DEX_MODIFIERS_H_
-
-#include <stdint.h>
-
-namespace art {
-
-static constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
-static constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
-static constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
-static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
-static constexpr uint32_t kAccFinal = 0x0010; // class, field, method, ic
-static constexpr uint32_t kAccSynchronized = 0x0020; // method (only allowed on natives)
-static constexpr uint32_t kAccSuper = 0x0020; // class (not used in dex)
-static constexpr uint32_t kAccVolatile = 0x0040; // field
-static constexpr uint32_t kAccBridge = 0x0040; // method (1.5)
-static constexpr uint32_t kAccTransient = 0x0080; // field
-static constexpr uint32_t kAccVarargs = 0x0080; // method (1.5)
-static constexpr uint32_t kAccNative = 0x0100; // method
-static constexpr uint32_t kAccInterface = 0x0200; // class, ic
-static constexpr uint32_t kAccAbstract = 0x0400; // class, method, ic
-static constexpr uint32_t kAccStrict = 0x0800; // method
-static constexpr uint32_t kAccSynthetic = 0x1000; // class, field, method, ic
-static constexpr uint32_t kAccAnnotation = 0x2000; // class, ic (1.5)
-static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5)
-
-static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16)
-
-// The following flags are used to insert hidden API access flags into boot
-// class path dex files. They are decoded by DexFile::ClassDataItemIterator and
-// removed from the access flags before used by the runtime.
-static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native)
-static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native)
-
-static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
-static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
-static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
-// Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its
-// declaring class. This flag may only be applied to methods.
-static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime)
-// Used by a method to denote that its execution does not need to go through slow path interpreter.
-static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native)
-// Used by a class to denote that the verifier has attempted to check it at least once.
-static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
-// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
-// that it was copied from its declaring class into another class. All methods marked kAccMiranda
-// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
-// array of a concrete class will also have this bit set.
-static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime)
-static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native)
-static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
-// Native method flags are set when linking the methods based on the presence of the
-// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility.
-// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods.
-static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only)
-static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only)
-
-// Set by the JIT when clearing profiling infos to denote that a method was previously warm.
-static constexpr uint32_t kAccPreviouslyWarm = 0x00800000; // method (runtime)
-
-// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
-// if any particular method needs to be a default conflict. Used to figure out at runtime if
-// invoking this method will throw an exception.
-static constexpr uint32_t kAccDefaultConflict = 0x01000000; // method (runtime)
-
-// Set by the verifier for a method we do not want the compiler to compile.
-static constexpr uint32_t kAccCompileDontBother = 0x02000000; // method (runtime)
-
-// Set by the verifier for a method that could not be verified to follow structured locking.
-static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (runtime)
-
-// Set by the class linker for a method that has only one implementation for a
-// virtual call.
-static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime)
-
-static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method
-
-// Not currently used, except for intrinsic methods where these bits
-// are part of the intrinsic ordinal.
-static constexpr uint32_t kAccMayBeUnusedBits = 0x40000000;
-
-// Set by the compiler driver when compiling boot classes with instrinsic methods.
-static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime)
-
-// Special runtime-only flags.
-// Interface and all its super-interfaces with default methods have been recursively initialized.
-static constexpr uint32_t kAccRecursivelyInitialized = 0x20000000;
-// Interface declares some default method.
-static constexpr uint32_t kAccHasDefaultMethod = 0x40000000;
-// class/ancestor overrides finalize()
-static constexpr uint32_t kAccClassIsFinalizable = 0x80000000;
-
-// Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags
-// which overlap are not valid when kAccIntrinsic is set.
-static constexpr uint32_t kAccIntrinsicBits = kAccMayBeUnusedBits | kAccHiddenApiBits |
- kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict |
- kAccPreviouslyWarm;
-
-// Valid (meaningful) bits for a field.
-static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected |
- kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum;
-
-// Valid (meaningful) bits for a method.
-static constexpr uint32_t kAccValidMethodFlags = kAccPublic | kAccPrivate | kAccProtected |
- kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative |
- kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized;
-static_assert(((kAccIntrinsic | kAccIntrinsicBits) & kAccValidMethodFlags) == 0,
- "Intrinsic bits and valid dex file method access flags must not overlap.");
-
-// Valid (meaningful) bits for a class (not interface).
-// Note 1. These are positive bits. Other bits may have to be zero.
-// Note 2. Inner classes can expose more access flags to Java programs. That is handled by libcore.
-static constexpr uint32_t kAccValidClassFlags = kAccPublic | kAccFinal | kAccSuper |
- kAccAbstract | kAccSynthetic | kAccEnum;
-
-// Valid (meaningful) bits for an interface.
-// Note 1. Annotations are interfaces.
-// Note 2. These are positive bits. Other bits may have to be zero.
-// Note 3. Inner classes can expose more access flags to Java programs. That is handled by libcore.
-static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface |
- kAccAbstract | kAccSynthetic | kAccAnnotation;
-
-static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected;
-
-// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
-// (note the trailing whitespace).
-std::string PrettyJavaAccessFlags(uint32_t access_flags);
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_MODIFIERS_H_
-
diff --git a/runtime/dex/standard_dex_file.cc b/runtime/dex/standard_dex_file.cc
deleted file mode 100644
index f7317eb997..0000000000
--- a/runtime/dex/standard_dex_file.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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 "standard_dex_file.h"
-
-#include "base/casts.h"
-#include "code_item_accessors-inl.h"
-#include "dex_file-inl.h"
-#include "leb128.h"
-
-namespace art {
-
-const uint8_t StandardDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
-const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersions]
- [StandardDexFile::kDexVersionLen] = {
- {'0', '3', '5', '\0'},
- // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
- // files with that version number would erroneously be accepted and run.
- {'0', '3', '7', '\0'},
- // Dex version 038: Android "O" and beyond.
- {'0', '3', '8', '\0'},
- // Dex verion 039: Beyond Android "O".
- {'0', '3', '9', '\0'},
-};
-
-void StandardDexFile::WriteMagic(uint8_t* magic) {
- std::copy_n(kDexMagic, kDexMagicSize, magic);
-}
-
-void StandardDexFile::WriteCurrentVersion(uint8_t* magic) {
- std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1],
- kDexVersionLen,
- magic + kDexMagicSize);
-}
-
-bool StandardDexFile::IsMagicValid(const uint8_t* magic) {
- return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
-}
-
-bool StandardDexFile::IsVersionValid(const uint8_t* magic) {
- const uint8_t* version = &magic[sizeof(kDexMagic)];
- for (uint32_t i = 0; i < kNumDexVersions; i++) {
- if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) {
- return true;
- }
- }
- return false;
-}
-
-bool StandardDexFile::IsMagicValid() const {
- return IsMagicValid(header_->magic_);
-}
-
-bool StandardDexFile::IsVersionValid() const {
- return IsVersionValid(header_->magic_);
-}
-
-bool StandardDexFile::SupportsDefaultMethods() const {
- return GetDexVersion() >= DexFile::kDefaultMethodsVersion;
-}
-
-uint32_t StandardDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const {
- DCHECK(IsInDataSection(&item));
- return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) -
- reinterpret_cast<uintptr_t>(&item);
-}
-
-} // namespace art
diff --git a/runtime/dex/standard_dex_file.h b/runtime/dex/standard_dex_file.h
deleted file mode 100644
index e0e9f2f11c..0000000000
--- a/runtime/dex/standard_dex_file.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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_DEX_STANDARD_DEX_FILE_H_
-#define ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_
-
-#include <iosfwd>
-
-#include "dex_file.h"
-
-namespace art {
-
-class OatDexFile;
-
-// Standard dex file. This is the format that is packaged in APKs and produced by tools.
-class StandardDexFile : public DexFile {
- public:
- class Header : public DexFile::Header {
- // Same for now.
- };
-
- struct CodeItem : public DexFile::CodeItem {
- static constexpr size_t kAlignment = 4;
-
- private:
- CodeItem() = default;
-
- uint16_t registers_size_; // the number of registers used by this code
- // (locals + parameters)
- uint16_t ins_size_; // the number of words of incoming arguments to the method
- // that this code is for
- uint16_t outs_size_; // the number of words of outgoing argument space required
- // by this code for method invocation
- uint16_t tries_size_; // the number of try_items for this instance. If non-zero,
- // then these appear as the tries array just after the
- // insns in this instance.
- uint32_t debug_info_off_; // Holds file offset to debug info stream.
-
- uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units
- uint16_t insns_[1]; // actual array of bytecode.
-
- ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
- friend class CodeItemDataAccessor;
- friend class CodeItemDebugInfoAccessor;
- friend class CodeItemInstructionAccessor;
- friend class DexWriter;
- friend class StandardDexFile;
- DISALLOW_COPY_AND_ASSIGN(CodeItem);
- };
-
- // Write the standard dex specific magic.
- static void WriteMagic(uint8_t* magic);
-
- // Write the current version, note that the input is the address of the magic.
- static void WriteCurrentVersion(uint8_t* magic);
-
- static const uint8_t kDexMagic[kDexMagicSize];
- static constexpr size_t kNumDexVersions = 4;
- static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
-
- // Returns true if the byte string points to the magic value.
- static bool IsMagicValid(const uint8_t* magic);
- virtual bool IsMagicValid() const OVERRIDE;
-
- // Returns true if the byte string after the magic is the correct value.
- static bool IsVersionValid(const uint8_t* magic);
- virtual bool IsVersionValid() const OVERRIDE;
-
- virtual bool SupportsDefaultMethods() const OVERRIDE;
-
- uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
-
- virtual size_t GetDequickenedSize() const OVERRIDE {
- return Size();
- }
-
- private:
- StandardDexFile(const uint8_t* base,
- size_t size,
- const std::string& location,
- uint32_t location_checksum,
- const OatDexFile* oat_dex_file,
- DexFileContainer* container)
- : DexFile(base,
- size,
- /*data_begin*/ base,
- /*data_size*/ size,
- location,
- location_checksum,
- oat_dex_file,
- container,
- /*is_compact_dex*/ false) {}
-
- friend class DexFileLoader;
- friend class DexFileVerifierTest;
-
- ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor
- friend class OptimizingUnitTestHelper; // for constructor
-
- DISALLOW_COPY_AND_ASSIGN(StandardDexFile);
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_STANDARD_DEX_FILE_H_
diff --git a/runtime/dex/utf-inl.h b/runtime/dex/utf-inl.h
deleted file mode 100644
index 4f626a8580..0000000000
--- a/runtime/dex/utf-inl.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_UTF_INL_H_
-#define ART_RUNTIME_DEX_UTF_INL_H_
-
-#include "utf.h"
-
-namespace art {
-
-inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) {
- return static_cast<uint16_t>(maybe_pair >> 16);
-}
-
-inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) {
- return static_cast<uint16_t>(maybe_pair & 0x0000FFFF);
-}
-
-inline uint32_t GetUtf16FromUtf8(const char** utf8_data_in) {
- const uint8_t one = *(*utf8_data_in)++;
- if ((one & 0x80) == 0) {
- // one-byte encoding
- return one;
- }
-
- const uint8_t two = *(*utf8_data_in)++;
- if ((one & 0x20) == 0) {
- // two-byte encoding
- return ((one & 0x1f) << 6) | (two & 0x3f);
- }
-
- const uint8_t three = *(*utf8_data_in)++;
- if ((one & 0x10) == 0) {
- return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
- }
-
- // Four byte encodings need special handling. We'll have
- // to convert them into a surrogate pair.
- const uint8_t four = *(*utf8_data_in)++;
-
- // Since this is a 4 byte UTF-8 sequence, it will lie between
- // U+10000 and U+1FFFFF.
- //
- // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
- // spec says they're invalid but nobody appears to check for them.
- const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12)
- | ((three & 0x3f) << 6) | (four & 0x3f);
-
- uint32_t surrogate_pair = 0;
- // Step two: Write out the high (leading) surrogate to the bottom 16 bits
- // of the of the 32 bit type.
- surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff;
- // Step three : Write out the low (trailing) surrogate to the top 16 bits.
- surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
-
- return surrogate_pair;
-}
-
-inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1,
- const char* utf8_2) {
- uint32_t c1, c2;
- do {
- c1 = *utf8_1;
- c2 = *utf8_2;
- // Did we reach a terminating character?
- if (c1 == 0) {
- return (c2 == 0) ? 0 : -1;
- } else if (c2 == 0) {
- return 1;
- }
-
- c1 = GetUtf16FromUtf8(&utf8_1);
- c2 = GetUtf16FromUtf8(&utf8_2);
- } while (c1 == c2);
-
- const uint32_t leading_surrogate_diff = GetLeadingUtf16Char(c1) - GetLeadingUtf16Char(c2);
- if (leading_surrogate_diff != 0) {
- return static_cast<int>(leading_surrogate_diff);
- }
-
- return GetTrailingUtf16Char(c1) - GetTrailingUtf16Char(c2);
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_UTF_INL_H_
diff --git a/runtime/dex/utf.cc b/runtime/dex/utf.cc
deleted file mode 100644
index 772a610140..0000000000
--- a/runtime/dex/utf.cc
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2011 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 "utf.h"
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "base/casts.h"
-#include "utf-inl.h"
-
-namespace art {
-
-using android::base::StringAppendF;
-using android::base::StringPrintf;
-
-// This is used only from debugger and test code.
-size_t CountModifiedUtf8Chars(const char* utf8) {
- return CountModifiedUtf8Chars(utf8, strlen(utf8));
-}
-
-/*
- * This does not validate UTF8 rules (nor did older code). But it gets the right answer
- * for valid UTF-8 and that's fine because it's used only to size a buffer for later
- * conversion.
- *
- * Modified UTF-8 consists of a series of bytes up to 21 bit Unicode code points as follows:
- * U+0001 - U+007F 0xxxxxxx
- * U+0080 - U+07FF 110xxxxx 10xxxxxx
- * U+0800 - U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
- * U+10000 - U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- *
- * U+0000 is encoded using the 2nd form to avoid nulls inside strings (this differs from
- * standard UTF-8).
- * The four byte encoding converts to two utf16 characters.
- */
-size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
- DCHECK_LE(byte_count, strlen(utf8));
- size_t len = 0;
- const char* end = utf8 + byte_count;
- for (; utf8 < end; ++utf8) {
- int ic = *utf8;
- len++;
- if (LIKELY((ic & 0x80) == 0)) {
- // One-byte encoding.
- continue;
- }
- // Two- or three-byte encoding.
- utf8++;
- if ((ic & 0x20) == 0) {
- // Two-byte encoding.
- continue;
- }
- utf8++;
- if ((ic & 0x10) == 0) {
- // Three-byte encoding.
- continue;
- }
-
- // Four-byte encoding: needs to be converted into a surrogate
- // pair.
- utf8++;
- len++;
- }
- return len;
-}
-
-// This is used only from debugger and test code.
-void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) {
- while (*utf8_data_in != '\0') {
- const uint32_t ch = GetUtf16FromUtf8(&utf8_data_in);
- const uint16_t leading = GetLeadingUtf16Char(ch);
- const uint16_t trailing = GetTrailingUtf16Char(ch);
-
- *utf16_data_out++ = leading;
- if (trailing != 0) {
- *utf16_data_out++ = trailing;
- }
- }
-}
-
-void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, size_t out_chars,
- const char* utf8_data_in, size_t in_bytes) {
- const char *in_start = utf8_data_in;
- const char *in_end = utf8_data_in + in_bytes;
- uint16_t *out_p = utf16_data_out;
-
- if (LIKELY(out_chars == in_bytes)) {
- // Common case where all characters are ASCII.
- for (const char *p = in_start; p < in_end;) {
- // Safe even if char is signed because ASCII characters always have
- // the high bit cleared.
- *out_p++ = dchecked_integral_cast<uint16_t>(*p++);
- }
- return;
- }
-
- // String contains non-ASCII characters.
- for (const char *p = in_start; p < in_end;) {
- const uint32_t ch = GetUtf16FromUtf8(&p);
- const uint16_t leading = GetLeadingUtf16Char(ch);
- const uint16_t trailing = GetTrailingUtf16Char(ch);
-
- *out_p++ = leading;
- if (trailing != 0) {
- *out_p++ = trailing;
- }
- }
-}
-
-void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count,
- const uint16_t* utf16_in, size_t char_count) {
- if (LIKELY(byte_count == char_count)) {
- // Common case where all characters are ASCII.
- const uint16_t *utf16_end = utf16_in + char_count;
- for (const uint16_t *p = utf16_in; p < utf16_end;) {
- *utf8_out++ = dchecked_integral_cast<char>(*p++);
- }
- return;
- }
-
- // String contains non-ASCII characters.
- while (char_count--) {
- const uint16_t ch = *utf16_in++;
- if (ch > 0 && ch <= 0x7f) {
- *utf8_out++ = ch;
- } else {
- // Char_count == 0 here implies we've encountered an unpaired
- // surrogate and we have no choice but to encode it as 3-byte UTF
- // sequence. Note that unpaired surrogates can occur as a part of
- // "normal" operation.
- if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
- const uint16_t ch2 = *utf16_in;
-
- // Check if the other half of the pair is within the expected
- // range. If it isn't, we will have to emit both "halves" as
- // separate 3 byte sequences.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- utf16_in++;
- char_count--;
- const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
- *utf8_out++ = (code_point >> 18) | 0xf0;
- *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
- *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (code_point & 0x3f) | 0x80;
- continue;
- }
- }
-
- if (ch > 0x07ff) {
- // Three byte encoding.
- *utf8_out++ = (ch >> 12) | 0xe0;
- *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- } else /*(ch > 0x7f || ch == 0)*/ {
- // Two byte encoding.
- *utf8_out++ = (ch >> 6) | 0xc0;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- }
- }
- }
-}
-
-int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) {
- uint32_t hash = 0;
- while (utf16_length != 0u) {
- const uint32_t pair = GetUtf16FromUtf8(&utf8);
- const uint16_t first = GetLeadingUtf16Char(pair);
- hash = hash * 31 + first;
- --utf16_length;
- const uint16_t second = GetTrailingUtf16Char(pair);
- if (second != 0) {
- hash = hash * 31 + second;
- DCHECK_NE(utf16_length, 0u);
- --utf16_length;
- }
- }
- return static_cast<int32_t>(hash);
-}
-
-uint32_t ComputeModifiedUtf8Hash(const char* chars) {
- uint32_t hash = 0;
- while (*chars != '\0') {
- hash = hash * 31 + *chars++;
- }
- return static_cast<int32_t>(hash);
-}
-
-int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16,
- size_t utf16_length) {
- for (;;) {
- if (*utf8 == '\0') {
- return (utf16_length == 0) ? 0 : -1;
- } else if (utf16_length == 0) {
- return 1;
- }
-
- const uint32_t pair = GetUtf16FromUtf8(&utf8);
-
- // First compare the leading utf16 char.
- const uint16_t lhs = GetLeadingUtf16Char(pair);
- const uint16_t rhs = *utf16++;
- --utf16_length;
- if (lhs != rhs) {
- return lhs > rhs ? 1 : -1;
- }
-
- // Then compare the trailing utf16 char. First check if there
- // are any characters left to consume.
- const uint16_t lhs2 = GetTrailingUtf16Char(pair);
- if (lhs2 != 0) {
- if (utf16_length == 0) {
- return 1;
- }
-
- const uint16_t rhs2 = *utf16++;
- --utf16_length;
- if (lhs2 != rhs2) {
- return lhs2 > rhs2 ? 1 : -1;
- }
- }
- }
-}
-
-size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
- size_t result = 0;
- const uint16_t *end = chars + char_count;
- while (chars < end) {
- const uint16_t ch = *chars++;
- if (LIKELY(ch != 0 && ch < 0x80)) {
- result++;
- continue;
- }
- if (ch < 0x800) {
- result += 2;
- continue;
- }
- if (ch >= 0xd800 && ch < 0xdc00) {
- if (chars < end) {
- const uint16_t ch2 = *chars;
- // If we find a properly paired surrogate, we emit it as a 4 byte
- // UTF sequence. If we find an unpaired leading or trailing surrogate,
- // we emit it as a 3 byte sequence like would have done earlier.
- if (ch2 >= 0xdc00 && ch2 < 0xe000) {
- chars++;
- result += 4;
- continue;
- }
- }
- }
- result += 3;
- }
- return result;
-}
-
-static inline constexpr bool NeedsEscaping(uint16_t ch) {
- return (ch < ' ' || ch > '~');
-}
-
-std::string PrintableChar(uint16_t ch) {
- std::string result;
- result += '\'';
- if (NeedsEscaping(ch)) {
- StringAppendF(&result, "\\u%04x", ch);
- } else {
- result += static_cast<std::string::value_type>(ch);
- }
- result += '\'';
- return result;
-}
-
-std::string PrintableString(const char* utf) {
- std::string result;
- result += '"';
- const char* p = utf;
- size_t char_count = CountModifiedUtf8Chars(p);
- for (size_t i = 0; i < char_count; ++i) {
- uint32_t ch = GetUtf16FromUtf8(&p);
- if (ch == '\\') {
- result += "\\\\";
- } else if (ch == '\n') {
- result += "\\n";
- } else if (ch == '\r') {
- result += "\\r";
- } else if (ch == '\t') {
- result += "\\t";
- } else {
- const uint16_t leading = GetLeadingUtf16Char(ch);
-
- if (NeedsEscaping(leading)) {
- StringAppendF(&result, "\\u%04x", leading);
- } else {
- result += static_cast<std::string::value_type>(leading);
- }
-
- const uint32_t trailing = GetTrailingUtf16Char(ch);
- if (trailing != 0) {
- // All high surrogates will need escaping.
- StringAppendF(&result, "\\u%04x", trailing);
- }
- }
- }
- result += '"';
- return result;
-}
-
-} // namespace art
diff --git a/runtime/dex/utf.h b/runtime/dex/utf.h
deleted file mode 100644
index 4adfc4af8c..0000000000
--- a/runtime/dex/utf.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2011 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_DEX_UTF_H_
-#define ART_RUNTIME_DEX_UTF_H_
-
-#include "base/macros.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <string>
-
-/*
- * All UTF-8 in art is actually modified UTF-8. Mostly, this distinction
- * doesn't matter.
- *
- * See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details.
- */
-namespace art {
-
-/*
- * Returns the number of UTF-16 characters in the given modified UTF-8 string.
- */
-size_t CountModifiedUtf8Chars(const char* utf8);
-size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count);
-
-/*
- * Returns the number of modified UTF-8 bytes needed to represent the given
- * UTF-16 string.
- */
-size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count);
-
-/*
- * Convert from Modified UTF-8 to UTF-16.
- */
-void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in);
-void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, size_t out_chars,
- const char* utf8_in, size_t in_bytes);
-
-/*
- * Compare two modified UTF-8 strings as UTF-16 code point values in a non-locale sensitive manner
- */
-ALWAYS_INLINE int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1,
- const char* utf8_2);
-
-/*
- * Compare a null-terminated modified UTF-8 string with a UTF-16 string (not null-terminated)
- * as code point values in a non-locale sensitive manner.
- */
-int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16,
- size_t utf16_length);
-
-/*
- * Convert from UTF-16 to Modified UTF-8. Note that the output is _not_
- * NUL-terminated. You probably need to call CountUtf8Bytes before calling
- * this anyway, so if you want a NUL-terminated string, you know where to
- * put the NUL byte.
- */
-void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count,
- const uint16_t* utf16_in, size_t char_count);
-
-/*
- * The java.lang.String hashCode() algorithm.
- */
-template<typename MemoryType>
-int32_t ComputeUtf16Hash(const MemoryType* chars, size_t char_count) {
- uint32_t hash = 0;
- while (char_count--) {
- hash = hash * 31 + *chars++;
- }
- return static_cast<int32_t>(hash);
-}
-
-int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length);
-
-// Compute a hash code of a modified UTF-8 string. Not the standard java hash since it returns a
-// uint32_t and hashes individual chars instead of codepoint words.
-uint32_t ComputeModifiedUtf8Hash(const char* chars);
-
-/*
- * Retrieve the next UTF-16 character or surrogate pair from a UTF-8 string.
- * single byte, 2-byte and 3-byte UTF-8 sequences result in a single UTF-16
- * character (possibly one half of a surrogate) whereas 4-byte UTF-8 sequences
- * result in a surrogate pair. Use GetLeadingUtf16Char and GetTrailingUtf16Char
- * to process the return value of this function.
- *
- * Advances "*utf8_data_in" to the start of the next character.
- *
- * WARNING: If a string is corrupted by dropping a '\0' in the middle
- * of a multi byte sequence, you can end up overrunning the buffer with
- * reads (and possibly with the writes if the length was computed and
- * cached before the damage). For performance reasons, this function
- * assumes that the string being parsed is known to be valid (e.g., by
- * already being verified). Most strings we process here are coming
- * out of dex files or other internal translations, so the only real
- * risk comes from the JNI NewStringUTF call.
- */
-uint32_t GetUtf16FromUtf8(const char** utf8_data_in);
-
-/**
- * Gets the leading UTF-16 character from a surrogate pair, or the sole
- * UTF-16 character from the return value of GetUtf16FromUtf8.
- */
-ALWAYS_INLINE uint16_t GetLeadingUtf16Char(uint32_t maybe_pair);
-
-/**
- * Gets the trailing UTF-16 character from a surrogate pair, or 0 otherwise
- * from the return value of GetUtf16FromUtf8.
- */
-ALWAYS_INLINE uint16_t GetTrailingUtf16Char(uint32_t maybe_pair);
-
-// Returns a printable (escaped) version of a character.
-std::string PrintableChar(uint16_t ch);
-
-// Returns an ASCII string corresponding to the given UTF-8 string.
-// Java escapes are used for non-ASCII characters.
-std::string PrintableString(const char* utf8);
-
-} // namespace art
-
-#endif // ART_RUNTIME_DEX_UTF_H_
diff --git a/runtime/dex/utf_test.cc b/runtime/dex/utf_test.cc
deleted file mode 100644
index d2f22d16ef..0000000000
--- a/runtime/dex/utf_test.cc
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2015 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 "utf.h"
-
-#include <map>
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "utf-inl.h"
-
-namespace art {
-
-class UtfTest : public testing::Test {};
-
-TEST_F(UtfTest, GetLeadingUtf16Char) {
- EXPECT_EQ(0xffff, GetLeadingUtf16Char(0xeeeeffff));
-}
-
-TEST_F(UtfTest, GetTrailingUtf16Char) {
- EXPECT_EQ(0xffff, GetTrailingUtf16Char(0xffffeeee));
- EXPECT_EQ(0, GetTrailingUtf16Char(0x0000aaaa));
-}
-
-#define EXPECT_ARRAY_POSITION(expected, end, start) \
- EXPECT_EQ(static_cast<uintptr_t>(expected), \
- reinterpret_cast<uintptr_t>(end) - reinterpret_cast<uintptr_t>(start));
-
-// A test string containing one, two, three and four byte UTF-8 sequences.
-static const uint8_t kAllSequences[] = {
- 0x24,
- 0xc2, 0xa2,
- 0xe2, 0x82, 0xac,
- 0xf0, 0x9f, 0x8f, 0xa0,
- 0x00
-};
-
-// A test string that contains a UTF-8 encoding of a surrogate pair
-// (code point = U+10400).
-static const uint8_t kSurrogateEncoding[] = {
- 0xed, 0xa0, 0x81,
- 0xed, 0xb0, 0x80,
- 0x00
-};
-
-TEST_F(UtfTest, GetUtf16FromUtf8) {
- const char* const start = reinterpret_cast<const char*>(kAllSequences);
- const char* ptr = start;
- uint32_t pair = 0;
-
- // Single byte sequence.
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0x24, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(1, ptr, start);
-
- // Two byte sequence.
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0xa2, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(3, ptr, start);
-
- // Three byte sequence.
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0x20ac, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(6, ptr, start);
-
- // Four byte sequence
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0xd83c, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0xdfe0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(10, ptr, start);
-
- // Null terminator.
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(11, ptr, start);
-}
-
-TEST_F(UtfTest, GetUtf16FromUtf8_SurrogatesPassThrough) {
- const char* const start = reinterpret_cast<const char *>(kSurrogateEncoding);
- const char* ptr = start;
- uint32_t pair = 0;
-
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0xd801, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(3, ptr, start);
-
- pair = GetUtf16FromUtf8(&ptr);
- EXPECT_EQ(0xdc00, GetLeadingUtf16Char(pair));
- EXPECT_EQ(0, GetTrailingUtf16Char(pair));
- EXPECT_ARRAY_POSITION(6, ptr, start);
-}
-
-TEST_F(UtfTest, CountModifiedUtf8Chars) {
- EXPECT_EQ(5u, CountModifiedUtf8Chars(reinterpret_cast<const char *>(kAllSequences)));
- EXPECT_EQ(2u, CountModifiedUtf8Chars(reinterpret_cast<const char *>(kSurrogateEncoding)));
-}
-
-static void AssertConversion(const std::vector<uint16_t>& input,
- const std::vector<uint8_t>& expected) {
- ASSERT_EQ(expected.size(), CountUtf8Bytes(&input[0], input.size()));
-
- std::vector<uint8_t> output(expected.size());
- ConvertUtf16ToModifiedUtf8(reinterpret_cast<char*>(&output[0]), expected.size(),
- &input[0], input.size());
- EXPECT_EQ(expected, output);
-}
-
-TEST_F(UtfTest, CountAndConvertUtf8Bytes) {
- // Surrogate pairs will be converted into 4 byte sequences.
- AssertConversion({ 0xd801, 0xdc00 }, { 0xf0, 0x90, 0x90, 0x80 });
-
- // Three byte encodings that are below & above the leading surrogate
- // range respectively.
- AssertConversion({ 0xdef0 }, { 0xed, 0xbb, 0xb0 });
- AssertConversion({ 0xdcff }, { 0xed, 0xb3, 0xbf });
- // Two byte encoding.
- AssertConversion({ 0x0101 }, { 0xc4, 0x81 });
-
- // Two byte special case : 0 must use an overlong encoding.
- AssertConversion({ 0x0101, 0x0000 }, { 0xc4, 0x81, 0xc0, 0x80 });
-
- // One byte encoding.
- AssertConversion({ 'h', 'e', 'l', 'l', 'o' }, { 0x68, 0x65, 0x6c, 0x6c, 0x6f });
-
- AssertConversion({
- 0xd802, 0xdc02, // Surrogate pair.
- 0xdef0, 0xdcff, // Three byte encodings.
- 0x0101, 0x0000, // Two byte encodings.
- 'p' , 'p' // One byte encoding.
- }, {
- 0xf0, 0x90, 0xa0, 0x82,
- 0xed, 0xbb, 0xb0, 0xed, 0xb3, 0xbf,
- 0xc4, 0x81, 0xc0, 0x80,
- 0x70, 0x70
- });
-}
-
-TEST_F(UtfTest, CountAndConvertUtf8Bytes_UnpairedSurrogate) {
- // Unpaired trailing surrogate at the end of input.
- AssertConversion({ 'h', 'e', 0xd801 }, { 'h', 'e', 0xed, 0xa0, 0x81 });
- // Unpaired (or incorrectly paired) surrogates in the middle of the input.
- const std::map<std::vector<uint16_t>, std::vector<uint8_t>> prefixes {
- {{ 'h' }, { 'h' }},
- {{ 0 }, { 0xc0, 0x80 }},
- {{ 0x81 }, { 0xc2, 0x81 }},
- {{ 0x801 }, { 0xe0, 0xa0, 0x81 }},
- };
- const std::map<std::vector<uint16_t>, std::vector<uint8_t>> suffixes {
- {{ 'e' }, { 'e' }},
- {{ 0 }, { 0xc0, 0x80 }},
- {{ 0x7ff }, { 0xdf, 0xbf }},
- {{ 0xffff }, { 0xef, 0xbf, 0xbf }},
- };
- const std::map<std::vector<uint16_t>, std::vector<uint8_t>> tests {
- {{ 0xd801 }, { 0xed, 0xa0, 0x81 }},
- {{ 0xdc00 }, { 0xed, 0xb0, 0x80 }},
- {{ 0xd801, 0xd801 }, { 0xed, 0xa0, 0x81, 0xed, 0xa0, 0x81 }},
- {{ 0xdc00, 0xdc00 }, { 0xed, 0xb0, 0x80, 0xed, 0xb0, 0x80 }},
- };
- for (const auto& prefix : prefixes) {
- const std::vector<uint16_t>& prefix_in = prefix.first;
- const std::vector<uint8_t>& prefix_out = prefix.second;
- for (const auto& test : tests) {
- const std::vector<uint16_t>& test_in = test.first;
- const std::vector<uint8_t>& test_out = test.second;
- for (const auto& suffix : suffixes) {
- const std::vector<uint16_t>& suffix_in = suffix.first;
- const std::vector<uint8_t>& suffix_out = suffix.second;
- std::vector<uint16_t> in = prefix_in;
- in.insert(in.end(), test_in.begin(), test_in.end());
- in.insert(in.end(), suffix_in.begin(), suffix_in.end());
- std::vector<uint8_t> out = prefix_out;
- out.insert(out.end(), test_out.begin(), test_out.end());
- out.insert(out.end(), suffix_out.begin(), suffix_out.end());
- AssertConversion(in, out);
- }
- }
- }
-}
-
-// Old versions of functions, here to compare answers with optimized versions.
-
-size_t CountModifiedUtf8Chars_reference(const char* utf8) {
- size_t len = 0;
- int ic;
- while ((ic = *utf8++) != '\0') {
- len++;
- if ((ic & 0x80) == 0) {
- // one-byte encoding
- continue;
- }
- // two- or three-byte encoding
- utf8++;
- if ((ic & 0x20) == 0) {
- // two-byte encoding
- continue;
- }
- utf8++;
- if ((ic & 0x10) == 0) {
- // three-byte encoding
- continue;
- }
-
- // four-byte encoding: needs to be converted into a surrogate
- // pair.
- utf8++;
- len++;
- }
- return len;
-}
-
-static size_t CountUtf8Bytes_reference(const uint16_t* chars, size_t char_count) {
- size_t result = 0;
- while (char_count--) {
- const uint16_t ch = *chars++;
- if (ch > 0 && ch <= 0x7f) {
- ++result;
- } else if (ch >= 0xd800 && ch <= 0xdbff) {
- if (char_count > 0) {
- const uint16_t ch2 = *chars;
- // If we find a properly paired surrogate, we emit it as a 4 byte
- // UTF sequence. If we find an unpaired leading or trailing surrogate,
- // we emit it as a 3 byte sequence like would have done earlier.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- chars++;
- char_count--;
-
- result += 4;
- } else {
- result += 3;
- }
- } else {
- // This implies we found an unpaired trailing surrogate at the end
- // of a string.
- result += 3;
- }
- } else if (ch > 0x7ff) {
- result += 3;
- } else {
- result += 2;
- }
- }
- return result;
-}
-
-static void ConvertUtf16ToModifiedUtf8_reference(char* utf8_out, const uint16_t* utf16_in,
- size_t char_count) {
- while (char_count--) {
- const uint16_t ch = *utf16_in++;
- if (ch > 0 && ch <= 0x7f) {
- *utf8_out++ = ch;
- } else {
- // Char_count == 0 here implies we've encountered an unpaired
- // surrogate and we have no choice but to encode it as 3-byte UTF
- // sequence. Note that unpaired surrogates can occur as a part of
- // "normal" operation.
- if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
- const uint16_t ch2 = *utf16_in;
-
- // Check if the other half of the pair is within the expected
- // range. If it isn't, we will have to emit both "halves" as
- // separate 3 byte sequences.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- utf16_in++;
- char_count--;
- const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
- *utf8_out++ = (code_point >> 18) | 0xf0;
- *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
- *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (code_point & 0x3f) | 0x80;
- continue;
- }
- }
-
- if (ch > 0x07ff) {
- // Three byte encoding.
- *utf8_out++ = (ch >> 12) | 0xe0;
- *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- } else /*(ch > 0x7f || ch == 0)*/ {
- // Two byte encoding.
- *utf8_out++ = (ch >> 6) | 0xc0;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- }
- }
- }
-}
-
-// Exhaustive test of converting a single code point to UTF-16, then UTF-8, and back again.
-
-static void codePointToSurrogatePair(uint32_t code_point, uint16_t &first, uint16_t &second) {
- first = (code_point >> 10) + 0xd7c0;
- second = (code_point & 0x03ff) + 0xdc00;
-}
-
-static void testConversions(uint16_t *buf, int char_count) {
- char bytes_test[8] = { 0 }, bytes_reference[8] = { 0 };
- uint16_t out_buf_test[4] = { 0 }, out_buf_reference[4] = { 0 };
- int byte_count_test, byte_count_reference;
- int char_count_test, char_count_reference;
-
- // Calculate the number of utf-8 bytes for the utf-16 chars.
- byte_count_reference = CountUtf8Bytes_reference(buf, char_count);
- byte_count_test = CountUtf8Bytes(buf, char_count);
- EXPECT_EQ(byte_count_reference, byte_count_test);
-
- // Convert the utf-16 string to utf-8 bytes.
- ConvertUtf16ToModifiedUtf8_reference(bytes_reference, buf, char_count);
- ConvertUtf16ToModifiedUtf8(bytes_test, byte_count_test, buf, char_count);
- for (int i = 0; i < byte_count_test; ++i) {
- EXPECT_EQ(bytes_reference[i], bytes_test[i]);
- }
-
- // Calculate the number of utf-16 chars from the utf-8 bytes.
- bytes_reference[byte_count_reference] = 0; // Reference function needs null termination.
- char_count_reference = CountModifiedUtf8Chars_reference(bytes_reference);
- char_count_test = CountModifiedUtf8Chars(bytes_test, byte_count_test);
- EXPECT_EQ(char_count, char_count_reference);
- EXPECT_EQ(char_count, char_count_test);
-
- // Convert the utf-8 bytes back to utf-16 chars.
- // Does not need copied _reference version of the function because the original
- // function with the old API is retained for debug/testing code.
- ConvertModifiedUtf8ToUtf16(out_buf_reference, bytes_reference);
- ConvertModifiedUtf8ToUtf16(out_buf_test, char_count_test, bytes_test, byte_count_test);
- for (int i = 0; i < char_count_test; ++i) {
- EXPECT_EQ(buf[i], out_buf_reference[i]);
- EXPECT_EQ(buf[i], out_buf_test[i]);
- }
-}
-
-TEST_F(UtfTest, ExhaustiveBidirectionalCodePointCheck) {
- for (int codePoint = 0; codePoint <= 0x10ffff; ++codePoint) {
- uint16_t buf[4] = { 0 };
- if (codePoint <= 0xffff) {
- if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
- // According to the Unicode standard, no character will ever
- // be assigned to these code points, and they cannot be encoded
- // into either utf-16 or utf-8.
- continue;
- }
- buf[0] = 'h';
- buf[1] = codePoint;
- buf[2] = 'e';
- testConversions(buf, 2);
- testConversions(buf, 3);
- testConversions(buf + 1, 1);
- testConversions(buf + 1, 2);
- } else {
- buf[0] = 'h';
- codePointToSurrogatePair(codePoint, buf[1], buf[2]);
- buf[3] = 'e';
- testConversions(buf, 2);
- testConversions(buf, 3);
- testConversions(buf, 4);
- testConversions(buf + 1, 1);
- testConversions(buf + 1, 2);
- testConversions(buf + 1, 3);
- }
- }
-}
-
-} // namespace art
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 037d1fb49c..8ce7921287 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -49,7 +49,8 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
CompilerFilter::Filter filter,
bool relocate,
bool pic,
- bool with_alternate_image) {
+ bool with_alternate_image,
+ const char* compilation_reason) {
std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
std::string oat_location = oat_location_in;
@@ -89,6 +90,10 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
args.push_back("--boot-image=" + GetImageLocation2());
}
+ if (compilation_reason != nullptr) {
+ args.push_back("--compilation-reason=" + std::string(compilation_reason));
+ }
+
std::string error_msg;
ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
@@ -155,13 +160,15 @@ void DexoptTest::GenerateOdexForTest(const std::string& dex_location,
void DexoptTest::GeneratePicOdexForTest(const std::string& dex_location,
const std::string& odex_location,
- CompilerFilter::Filter filter) {
+ CompilerFilter::Filter filter,
+ const char* compilation_reason) {
GenerateOatForTest(dex_location,
odex_location,
filter,
/*relocate*/false,
/*pic*/true,
- /*with_alternate_image*/false);
+ /*with_alternate_image*/false,
+ compilation_reason);
}
void DexoptTest::GenerateOatForTest(const char* dex_location,
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
index 5f0eafd8eb..6e8dc097d5 100644
--- a/runtime/dexopt_test.h
+++ b/runtime/dexopt_test.h
@@ -46,7 +46,8 @@ class DexoptTest : public Dex2oatEnvironmentTest {
CompilerFilter::Filter filter,
bool relocate,
bool pic,
- bool with_alternate_image);
+ bool with_alternate_image,
+ const char* compilation_reason = nullptr);
// Generate a non-PIC odex file for the purposes of test.
// The generated odex file will be un-relocated.
@@ -56,7 +57,8 @@ class DexoptTest : public Dex2oatEnvironmentTest {
void GeneratePicOdexForTest(const std::string& dex_location,
const std::string& odex_location,
- CompilerFilter::Filter filter);
+ CompilerFilter::Filter filter,
+ const char* compilation_reason = nullptr);
// Generate an oat file for the given dex location in its oat location (under
// the dalvik cache).
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 9ef7d426df..404c5357bf 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -99,14 +99,13 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
<< "This must be due to playing wrongly with class loaders";
}
- inlined_method = klass->FindClassMethod(dex_cache, method_index, kRuntimePointerSize);
+ inlined_method = class_linker->FindResolvedMethod(klass, dex_cache, class_loader, method_index);
if (inlined_method == nullptr) {
LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor
<< " does not have " << dex_file->GetMethodName(method_id)
<< dex_file->GetMethodSignature(method_id) << " declared. "
<< "This must be due to duplicate classes or playing wrongly with class loaders";
}
- dex_cache->SetResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
return inlined_method;
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index c5157ce9f4..a8c328fbc7 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -319,7 +319,7 @@ class QuickArgumentVisitor {
// 'this' object is the 1st argument. They also have the same frame layout as the
// kRefAndArgs runtime method. Since 'this' is a reference, it is located in the
// 1st GPR.
- static mirror::Object* GetProxyThisObject(ArtMethod** sp)
+ static StackReference<mirror::Object>* GetProxyThisObjectReference(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK((*sp)->IsProxyMethod());
CHECK_GT(kNumQuickGprArgs, 0u);
@@ -327,7 +327,7 @@ class QuickArgumentVisitor {
size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
GprIndexToGprOffset(kThisGprIndex);
uint8_t* this_arg_address = reinterpret_cast<uint8_t*>(sp) + this_arg_offset;
- return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address)->AsMirrorPtr();
+ return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address);
}
static ArtMethod* GetCallingMethod(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -647,7 +647,11 @@ class QuickArgumentVisitor {
// allows to use the QuickArgumentVisitor constants without moving all the code in its own module.
extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return QuickArgumentVisitor::GetProxyThisObject(sp);
+ return QuickArgumentVisitor::GetProxyThisObjectReference(sp)->AsMirrorPtr();
+}
+extern "C" StackReference<mirror::Object>* artQuickGetProxyThisObjectReference(ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return QuickArgumentVisitor::GetProxyThisObjectReference(sp);
}
// Visits arguments on the stack placing them into the shadow frame.
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 72eb8274c8..f8d8271335 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -41,7 +41,7 @@ namespace art {
namespace gc {
namespace accounting {
-// Internal representation is StackReference<T>, so this only works with mirror::Object or it's
+// Internal representation is StackReference<T>, so this only works with mirror::Object or its
// subclasses.
template <typename T>
class AtomicStack {
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index df9ee8c219..354b9e1dbd 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -62,7 +62,8 @@ inline bool SpaceBitmap<kAlignment>::Test(const mirror::Object* obj) const {
return (bitmap_begin_[OffsetToIndex(offset)].LoadRelaxed() & OffsetToMask(offset)) != 0;
}
-template<size_t kAlignment> template<typename Visitor>
+template<size_t kAlignment>
+template<typename Visitor>
inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin,
uintptr_t visit_end,
Visitor&& visitor) const {
@@ -121,6 +122,7 @@ inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin,
uintptr_t w = bitmap_begin_[i].LoadRelaxed();
if (w != 0) {
const uintptr_t ptr_base = IndexToOffset(i) + heap_begin_;
+ // Iterate on the bits set in word `w`, from the least to the most significant bit.
do {
const size_t shift = CTZ(w);
mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
@@ -147,6 +149,7 @@ inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin,
right_edge &= ((static_cast<uintptr_t>(1) << bit_end) - 1);
if (right_edge != 0) {
const uintptr_t ptr_base = IndexToOffset(index_end) + heap_begin_;
+ // Iterate on the bits set in word `right_edge`, from the least to the most significant bit.
do {
const size_t shift = CTZ(right_edge);
mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
@@ -157,7 +160,8 @@ inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin,
#endif
}
-template<size_t kAlignment> template<typename Visitor>
+template<size_t kAlignment>
+template<typename Visitor>
void SpaceBitmap<kAlignment>::Walk(Visitor&& visitor) {
CHECK(bitmap_begin_ != nullptr);
@@ -177,7 +181,8 @@ void SpaceBitmap<kAlignment>::Walk(Visitor&& visitor) {
}
}
-template<size_t kAlignment> template<bool kSetBit>
+template<size_t kAlignment>
+template<bool kSetBit>
inline bool SpaceBitmap<kAlignment>::Modify(const mirror::Object* obj) {
uintptr_t addr = reinterpret_cast<uintptr_t>(obj);
DCHECK_GE(addr, heap_begin_);
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 237ee80ba0..0247564a8c 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -33,7 +33,12 @@ using android::base::StringPrintf;
template<size_t kAlignment>
size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) {
+ // Number of space (heap) bytes covered by one bitmap word.
+ // (Word size in bytes = `sizeof(intptr_t)`, which is expected to be
+ // 4 on a 32-bit architecture and 8 on a 64-bit one.)
const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT;
+ // Calculate the number of words required to cover a space (heap)
+ // having a size of `capacity` bytes.
return (RoundUp(capacity, kBytesCoveredPerWord) / kBytesCoveredPerWord) * sizeof(intptr_t);
}
@@ -74,7 +79,8 @@ SpaceBitmap<kAlignment>::~SpaceBitmap() {}
template<size_t kAlignment>
SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::Create(
const std::string& name, uint8_t* heap_begin, size_t heap_capacity) {
- // Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord.
+ // Round up since `heap_capacity` is not necessarily a multiple of `kAlignment * kBitsPerIntPtrT`
+ // (we represent one word as an `intptr_t`).
const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
std::string error_msg;
std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), nullptr, bitmap_size,
@@ -116,7 +122,7 @@ template<size_t kAlignment>
void SpaceBitmap<kAlignment>::ClearRange(const mirror::Object* begin, const mirror::Object* end) {
uintptr_t begin_offset = reinterpret_cast<uintptr_t>(begin) - heap_begin_;
uintptr_t end_offset = reinterpret_cast<uintptr_t>(end) - heap_begin_;
- // Align begin and end to word boundaries.
+ // Align begin and end to bitmap word boundaries.
while (begin_offset < end_offset && OffsetBitIndex(begin_offset) != 0) {
Clear(reinterpret_cast<mirror::Object*>(heap_begin_ + begin_offset));
begin_offset += kAlignment;
@@ -125,6 +131,7 @@ void SpaceBitmap<kAlignment>::ClearRange(const mirror::Object* begin, const mirr
end_offset -= kAlignment;
Clear(reinterpret_cast<mirror::Object*>(heap_begin_ + end_offset));
}
+ // Bitmap word boundaries.
const uintptr_t start_index = OffsetToIndex(begin_offset);
const uintptr_t end_index = OffsetToIndex(end_offset);
ZeroAndReleasePages(reinterpret_cast<uint8_t*>(&bitmap_begin_[start_index]),
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 2f33bac902..437aecc2b1 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -55,6 +55,10 @@ class SpaceBitmap {
~SpaceBitmap();
+ // Return the bitmap word index corresponding to memory offset (relative to
+ // `HeapBegin()`) `offset`.
+ // See also SpaceBitmap::OffsetBitIndex.
+ //
// <offset> is the difference from .base to a pointer address.
// <index> is the index of .bits that contains the bit representing
// <offset>.
@@ -62,24 +66,32 @@ class SpaceBitmap {
return offset / kAlignment / kBitsPerIntPtrT;
}
+ // Return the memory offset (relative to `HeapBegin()`) corresponding to
+ // bitmap word index `index`.
template<typename T>
static constexpr T IndexToOffset(T index) {
return static_cast<T>(index * kAlignment * kBitsPerIntPtrT);
}
+ // Return the bit within the bitmap word index corresponding to
+ // memory offset (relative to `HeapBegin()`) `offset`.
+ // See also SpaceBitmap::OffsetToIndex.
ALWAYS_INLINE static constexpr uintptr_t OffsetBitIndex(uintptr_t offset) {
return (offset / kAlignment) % kBitsPerIntPtrT;
}
+ // Return the word-wide bit mask corresponding to `OffsetBitIndex(offset)`.
// Bits are packed in the obvious way.
static constexpr uintptr_t OffsetToMask(uintptr_t offset) {
return static_cast<size_t>(1) << OffsetBitIndex(offset);
}
+ // Set the bit corresponding to `obj` in the bitmap and return the previous value of that bit.
bool Set(const mirror::Object* obj) ALWAYS_INLINE {
return Modify<true>(obj);
}
+ // Clear the bit corresponding to `obj` in the bitmap and return the previous value of that bit.
bool Clear(const mirror::Object* obj) ALWAYS_INLINE {
return Modify<false>(obj);
}
@@ -90,9 +102,14 @@ class SpaceBitmap {
// Fill the bitmap with zeroes. Returns the bitmap's memory to the system as a side-effect.
void Clear();
- // Clear a covered by the bitmap using madvise if possible.
+ // Clear a range covered by the bitmap using madvise if possible.
void ClearRange(const mirror::Object* begin, const mirror::Object* end);
+ // Test whether `obj` is part of the bitmap (i.e. return whether the bit
+ // corresponding to `obj` has been set in the bitmap).
+ //
+ // Precondition: `obj` is within the range of pointers that this bitmap could
+ // potentially cover (i.e. `this->HasAddress(obj)` is true)
bool Test(const mirror::Object* obj) const;
// Return true iff <obj> is within the range of pointers that this bitmap could potentially cover,
@@ -204,6 +221,8 @@ class SpaceBitmap {
const void* heap_begin,
size_t heap_capacity);
+ // Change the value of the bit corresponding to `obj` in the bitmap
+ // to `kSetBit` and return the previous value of that bit.
template<bool kSetBit>
bool Modify(const mirror::Object* obj);
diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc
index bd5f77ebb1..1ca3fd6d12 100644
--- a/runtime/gc/accounting/space_bitmap_test.cc
+++ b/runtime/gc/accounting/space_bitmap_test.cc
@@ -74,7 +74,7 @@ TEST_F(SpaceBitmapTest, ScanRange) {
}
}
// Try every possible starting bit in the first word. Then for each starting bit, try each
- // possible length up to a maximum of kBitsPerWord * 2 - 1 bits.
+ // possible length up to a maximum of `kBitsPerIntPtrT * 2 - 1` bits.
// This handles all the cases, having runs which start and end on the same word, and different
// words.
for (size_t i = 0; i < static_cast<size_t>(kBitsPerIntPtrT); ++i) {
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 85a656ec51..d739ed2867 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -52,7 +52,8 @@ inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion(
// we can avoid an expensive CAS.
// For the baker case, an object is marked if either the mark bit marked or the bitmap bit is
// set.
- success = ref->AtomicSetReadBarrierState(ReadBarrier::WhiteState(), ReadBarrier::GrayState());
+ success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(),
+ /* rb_state */ ReadBarrier::GrayState());
} else {
success = !bitmap->AtomicTestAndSet(ref);
}
@@ -86,8 +87,8 @@ inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) {
return ref;
}
// This may or may not succeed, which is ok because the object may already be gray.
- bool success = ref->AtomicSetReadBarrierState(ReadBarrier::WhiteState(),
- ReadBarrier::GrayState());
+ bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(),
+ /* rb_state */ ReadBarrier::GrayState());
if (success) {
MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
immune_gray_stack_.push_back(ref);
@@ -133,6 +134,8 @@ inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref,
// It isn't marked yet. Mark it by copying it to the to-space.
to_ref = Copy(from_ref, holder, offset);
}
+ // The copy should either be in a to-space region, or in the
+ // non-moving space, if it could not fit in a to-space region.
DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
<< "from_ref=" << from_ref << " to_ref=" << to_ref;
return to_ref;
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 1e0c0b16e4..3770085c07 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -347,8 +347,9 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor
// This must come before the revoke.
size_t thread_local_objects = thread->GetThreadLocalObjectsAllocated();
concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread);
- reinterpret_cast<Atomic<size_t>*>(&concurrent_copying_->from_space_num_objects_at_first_pause_)->
- FetchAndAddSequentiallyConsistent(thread_local_objects);
+ reinterpret_cast<Atomic<size_t>*>(
+ &concurrent_copying_->from_space_num_objects_at_first_pause_)->
+ FetchAndAddSequentiallyConsistent(thread_local_objects);
} else {
concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread);
}
@@ -440,7 +441,7 @@ class ConcurrentCopying::FlipCallback : public Closure {
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
cc->GrayAllNewlyDirtyImmuneObjects();
if (kIsDebugBuild) {
- // Check that all non-gray immune objects only refernce immune objects.
+ // Check that all non-gray immune objects only reference immune objects.
cc->VerifyGrayImmuneObjects();
}
}
@@ -1534,7 +1535,8 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
!IsInToSpace(referent)))) {
// Leave this reference gray in the queue so that GetReferent() will trigger a read barrier. We
// will change it to white later in ReferenceQueue::DequeuePendingReference().
- DCHECK(to_ref->AsReference()->GetPendingNext() != nullptr) << "Left unenqueued ref gray " << to_ref;
+ DCHECK(to_ref->AsReference()->GetPendingNext() != nullptr)
+ << "Left unenqueued ref gray " << to_ref;
} else {
// We may occasionally leave a reference white in the queue if its referent happens to be
// concurrently marked after the Scan() call above has enqueued the Reference, in which case the
@@ -1552,7 +1554,7 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
#endif
if (add_to_live_bytes) {
- // Add to the live bytes per unevacuated from space. Note this code is always run by the
+ // Add to the live bytes per unevacuated from-space. Note this code is always run by the
// GC-running thread (no synchronization required).
DCHECK(region_space_bitmap_->Test(to_ref));
size_t obj_size = to_ref->SizeOf<kDefaultVerifyFlags>();
@@ -1759,13 +1761,14 @@ void ConcurrentCopying::ReclaimPhase() {
}
CHECK_LE(to_objects, from_objects);
CHECK_LE(to_bytes, from_bytes);
- // cleared_bytes and cleared_objects may be greater than the from space equivalents since
- // ClearFromSpace may clear empty unevac regions.
+ // Cleared bytes and objects, populated by the call to RegionSpace::ClearFromSpace below.
uint64_t cleared_bytes;
uint64_t cleared_objects;
{
TimingLogger::ScopedTiming split4("ClearFromSpace", GetTimings());
region_space_->ClearFromSpace(&cleared_bytes, &cleared_objects);
+ // `cleared_bytes` and `cleared_objects` may be greater than the from space equivalents since
+ // RegionSpace::ClearFromSpace may clear empty unevac regions.
CHECK_GE(cleared_bytes, from_bytes);
CHECK_GE(cleared_objects, from_objects);
}
@@ -1774,17 +1777,20 @@ void ConcurrentCopying::ReclaimPhase() {
if (kVerboseMode) {
LOG(INFO) << "RecordFree:"
<< " from_bytes=" << from_bytes << " from_objects=" << from_objects
- << " unevac_from_bytes=" << unevac_from_bytes << " unevac_from_objects=" << unevac_from_objects
+ << " unevac_from_bytes=" << unevac_from_bytes
+ << " unevac_from_objects=" << unevac_from_objects
<< " to_bytes=" << to_bytes << " to_objects=" << to_objects
<< " freed_bytes=" << freed_bytes << " freed_objects=" << freed_objects
<< " from_space size=" << region_space_->FromSpaceSize()
<< " unevac_from_space size=" << region_space_->UnevacFromSpaceSize()
<< " to_space size=" << region_space_->ToSpaceSize();
- LOG(INFO) << "(before) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
+ LOG(INFO) << "(before) num_bytes_allocated="
+ << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
}
RecordFree(ObjectBytePair(freed_objects, freed_bytes));
if (kVerboseMode) {
- LOG(INFO) << "(after) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
+ LOG(INFO) << "(after) num_bytes_allocated="
+ << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
}
}
@@ -1806,27 +1812,82 @@ void ConcurrentCopying::ReclaimPhase() {
}
}
-// Assert the to-space invariant.
+std::string ConcurrentCopying::DumpReferenceInfo(mirror::Object* ref,
+ const char* ref_name,
+ std::string indent) {
+ std::ostringstream oss;
+ oss << indent << heap_->GetVerification()->DumpObjectInfo(ref, ref_name) << '\n';
+ if (ref != nullptr) {
+ if (kUseBakerReadBarrier) {
+ oss << indent << ref_name << "->GetMarkBit()=" << ref->GetMarkBit() << '\n';
+ oss << indent << ref_name << "->GetReadBarrierState()=" << ref->GetReadBarrierState() << '\n';
+ }
+ }
+ if (region_space_->HasAddress(ref)) {
+ oss << indent << "Region containing " << ref_name << ":" << '\n';
+ region_space_->DumpRegionForObject(oss, ref);
+ if (region_space_bitmap_ != nullptr) {
+ oss << indent << "region_space_bitmap_->Test(" << ref_name << ")="
+ << std::boolalpha << region_space_bitmap_->Test(ref) << std::noboolalpha;
+ }
+ }
+ return oss.str();
+}
+
+std::string ConcurrentCopying::DumpHeapReference(mirror::Object* obj,
+ MemberOffset offset,
+ mirror::Object* ref) {
+ std::ostringstream oss;
+ std::string indent = " ";
+ oss << indent << "Invalid reference: ref=" << ref
+ << " referenced from: object=" << obj << " offset= " << offset << '\n';
+ // Information about `obj`.
+ oss << DumpReferenceInfo(obj, "obj", indent) << '\n';
+ // Information about `ref`.
+ oss << DumpReferenceInfo(ref, "ref", indent);
+ return oss.str();
+}
+
void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj,
MemberOffset offset,
mirror::Object* ref) {
- CHECK_EQ(heap_->collector_type_, kCollectorTypeCC);
+ CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
if (is_asserting_to_space_invariant_) {
- using RegionType = space::RegionSpace::RegionType;
- space::RegionSpace::RegionType type = region_space_->GetRegionType(ref);
- if (type == RegionType::kRegionTypeToSpace) {
- // OK.
- return;
- } else if (type == RegionType::kRegionTypeUnevacFromSpace) {
- CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
- } else if (UNLIKELY(type == RegionType::kRegionTypeFromSpace)) {
- // Not OK. Do extra logging.
- if (obj != nullptr) {
- LogFromSpaceRefHolder(obj, offset);
+ if (region_space_->HasAddress(ref)) {
+ // Check to-space invariant in region space (moving space).
+ using RegionType = space::RegionSpace::RegionType;
+ space::RegionSpace::RegionType type = region_space_->GetRegionType(ref);
+ if (type == RegionType::kRegionTypeToSpace) {
+ // OK.
+ return;
+ } else if (type == RegionType::kRegionTypeUnevacFromSpace) {
+ if (!IsMarkedInUnevacFromSpace(ref)) {
+ LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:";
+ LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref);
+ }
+ CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
+ } else {
+ // Not OK: either a from-space ref or a reference in an unused region.
+ // Do extra logging.
+ if (type == RegionType::kRegionTypeFromSpace) {
+ LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:";
+ } else {
+ LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":";
+ }
+ LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref);
+ if (obj != nullptr) {
+ LogFromSpaceRefHolder(obj, offset);
+ }
+ ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+ LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:";
+ region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
+ PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ LOG(FATAL) << "Invalid reference " << ref
+ << " referenced from object " << obj << " at offset " << offset;
}
- ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
- CHECK(false) << "Found from-space ref " << ref << " " << ref->PrettyTypeOf();
} else {
+ // Check to-space invariant in non-moving space.
AssertToSpaceInvariantInNonMovingSpace(obj, ref);
}
}
@@ -1857,39 +1918,66 @@ class RootPrinter {
}
};
+std::string ConcurrentCopying::DumpGcRoot(mirror::Object* ref) {
+ std::ostringstream oss;
+ std::string indent = " ";
+ oss << indent << "Invalid GC root: ref=" << ref << '\n';
+ // Information about `ref`.
+ oss << DumpReferenceInfo(ref, "ref", indent);
+ return oss.str();
+}
+
void ConcurrentCopying::AssertToSpaceInvariant(GcRootSource* gc_root_source,
mirror::Object* ref) {
- CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
+ CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
if (is_asserting_to_space_invariant_) {
- if (region_space_->IsInToSpace(ref)) {
- // OK.
- return;
- } else if (region_space_->IsInUnevacFromSpace(ref)) {
- CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
- } else if (region_space_->IsInFromSpace(ref)) {
- // Not OK. Do extra logging.
- if (gc_root_source == nullptr) {
- // No info.
- } else if (gc_root_source->HasArtField()) {
- ArtField* field = gc_root_source->GetArtField();
- LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " "
- << ArtField::PrettyField(field);
- RootPrinter root_printer;
- field->VisitRoots(root_printer);
- } else if (gc_root_source->HasArtMethod()) {
- ArtMethod* method = gc_root_source->GetArtMethod();
- LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " "
- << ArtMethod::PrettyMethod(method);
- RootPrinter root_printer;
- method->VisitRoots(root_printer, kRuntimePointerSize);
+ if (region_space_->HasAddress(ref)) {
+ // Check to-space invariant in region space (moving space).
+ using RegionType = space::RegionSpace::RegionType;
+ space::RegionSpace::RegionType type = region_space_->GetRegionType(ref);
+ if (type == RegionType::kRegionTypeToSpace) {
+ // OK.
+ return;
+ } else if (type == RegionType::kRegionTypeUnevacFromSpace) {
+ if (!IsMarkedInUnevacFromSpace(ref)) {
+ LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:";
+ LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref);
+ }
+ CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
+ } else {
+ // Not OK: either a from-space ref or a reference in an unused region.
+ // Do extra logging.
+ if (type == RegionType::kRegionTypeFromSpace) {
+ LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:";
+ } else {
+ LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":";
+ }
+ LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref);
+ if (gc_root_source == nullptr) {
+ // No info.
+ } else if (gc_root_source->HasArtField()) {
+ ArtField* field = gc_root_source->GetArtField();
+ LOG(FATAL_WITHOUT_ABORT) << "gc root in field " << field << " "
+ << ArtField::PrettyField(field);
+ RootPrinter root_printer;
+ field->VisitRoots(root_printer);
+ } else if (gc_root_source->HasArtMethod()) {
+ ArtMethod* method = gc_root_source->GetArtMethod();
+ LOG(FATAL_WITHOUT_ABORT) << "gc root in method " << method << " "
+ << ArtMethod::PrettyMethod(method);
+ RootPrinter root_printer;
+ method->VisitRoots(root_printer, kRuntimePointerSize);
+ }
+ ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
+ LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:";
+ region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
+ PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
+ MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
+ LOG(FATAL) << "Invalid reference " << ref;
}
- ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
- region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
- PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
- MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true);
- CHECK(false) << "Found from-space ref " << ref << " " << ref->PrettyTypeOf();
} else {
- AssertToSpaceInvariantInNonMovingSpace(nullptr, ref);
+ // Check to-space invariant in non-moving space.
+ AssertToSpaceInvariantInNonMovingSpace(/* obj */ nullptr, ref);
}
}
}
@@ -1944,7 +2032,8 @@ void ConcurrentCopying::LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset
void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj,
mirror::Object* ref) {
- // In a non-moving spaces. Check that the ref is marked.
+ CHECK(!region_space_->HasAddress(ref)) << "obj=" << obj << " ref=" << ref;
+ // In a non-moving space. Check that the ref is marked.
if (immune_spaces_.ContainsObject(ref)) {
if (kUseBakerReadBarrier) {
// Immune object may not be gray if called from the GC.
@@ -1968,11 +2057,13 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o
(is_los && los_bitmap->Test(ref))) {
// OK.
} else {
- // If ref is on the allocation stack, then it may not be
+ // If `ref` is on the allocation stack, then it may not be
// marked live, but considered marked/alive (but not
// necessarily on the live stack).
- CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack. "
- << "obj=" << obj << " ref=" << ref;
+ CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack."
+ << " obj=" << obj
+ << " ref=" << ref
+ << " is_los=" << std::boolalpha << is_los << std::noboolalpha;
}
}
}
@@ -2013,7 +2104,6 @@ class ConcurrentCopying::RefFieldsVisitor {
ConcurrentCopying* const collector_;
};
-// Scan ref fields of an object.
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) {
// Avoid all read barriers during visit references to help performance.
@@ -2032,7 +2122,6 @@ inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
}
}
-// Process a field.
inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
DCHECK_EQ(Thread::Current(), thread_running_gc_);
mirror::Object* ref = obj->GetFieldObject<
@@ -2053,7 +2142,7 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset)
// It was updated by the mutator.
break;
}
- // Use release cas to make sure threads reading the reference see contents of copied objects.
+ // Use release CAS to make sure threads reading the reference see contents of copied objects.
} while (!obj->CasFieldWeakReleaseObjectWithoutWriteBarrier<false, false, kVerifyNone>(
offset,
expected_ref,
@@ -2428,6 +2517,9 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
to_ref = nullptr;
}
} else {
+ // At this point, `from_ref` should not be in the region space
+ // (i.e. within an "unused" region).
+ DCHECK(!region_space_->HasAddress(from_ref)) << from_ref;
// from_ref is in a non-moving space.
if (immune_spaces_.ContainsObject(from_ref)) {
// An immune object is alive.
@@ -2598,7 +2690,12 @@ void ConcurrentCopying::FinishPhase() {
DCHECK(rb_mark_bit_stack_ != nullptr);
const auto* limit = rb_mark_bit_stack_->End();
for (StackReference<mirror::Object>* it = rb_mark_bit_stack_->Begin(); it != limit; ++it) {
- CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0));
+ CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0))
+ << "rb_mark_bit_stack_->Begin()" << rb_mark_bit_stack_->Begin() << '\n'
+ << "rb_mark_bit_stack_->End()" << rb_mark_bit_stack_->End() << '\n'
+ << "rb_mark_bit_stack_->IsFull()"
+ << std::boolalpha << rb_mark_bit_stack_->IsFull() << std::noboolalpha << '\n'
+ << DumpReferenceInfo(it->AsMirrorPtr(), "*it");
}
rb_mark_bit_stack_->Reset();
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 8b4b58e7b1..c58dd44648 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -100,8 +100,10 @@ class ConcurrentCopying : public GarbageCollector {
space::RegionSpace* RegionSpace() {
return region_space_;
}
+ // Assert the to-space invariant for a heap reference `ref` held in `obj` at offset `offset`.
void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Assert the to-space invariant for a GC root reference `ref`.
void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref)
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsInToSpace(mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -109,6 +111,7 @@ class ConcurrentCopying : public GarbageCollector {
return IsMarked(ref) == ref;
}
template<bool kGrayImmuneObject = true, bool kFromGCThread = false>
+ // Mark object `from_ref`, copying it to the to-space if needed.
ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref,
mirror::Object* holder = nullptr,
MemberOffset offset = MemberOffset(0))
@@ -148,8 +151,10 @@ class ConcurrentCopying : public GarbageCollector {
MemberOffset offset)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
+ // Scan the reference fields of object `to_ref`.
void Scan(mirror::Object* to_ref) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
+ // Process a field.
void Process(mirror::Object* obj, MemberOffset offset)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_ , !skipped_blocks_lock_, !immune_gray_stack_lock_);
@@ -232,6 +237,16 @@ class ConcurrentCopying : public GarbageCollector {
void ComputeUnevacFromSpaceLiveRatio();
void LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset offset)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Dump information about reference `ref` and return it as a string.
+ // Use `ref_name` to name the reference in messages. Each message is prefixed with `indent`.
+ std::string DumpReferenceInfo(mirror::Object* ref, const char* ref_name, std::string indent = "")
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Dump information about heap reference `ref`, referenced from object `obj` at offset `offset`,
+ // and return it as a string.
+ std::string DumpHeapReference(mirror::Object* obj, MemberOffset offset, mirror::Object* ref)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Dump information about GC root `ref` and return it as a string.
+ std::string DumpGcRoot(mirror::Object* ref) REQUIRES_SHARED(Locks::mutator_lock_);
void AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj, mirror::Object* ref)
REQUIRES_SHARED(Locks::mutator_lock_);
void ReenableWeakRefAccess(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -266,8 +281,20 @@ class ConcurrentCopying : public GarbageCollector {
space::RegionSpace* region_space_; // The underlying region space.
std::unique_ptr<Barrier> gc_barrier_;
std::unique_ptr<accounting::ObjectStack> gc_mark_stack_;
+
+ // The read-barrier mark-bit stack. Stores object references whose
+ // mark bit has been set by ConcurrentCopying::MarkFromReadBarrier,
+ // so that this bit can be reset at the end of the collection in
+ // ConcurrentCopying::FinishPhase. The mark bit of an object can be
+ // used by mutator read barrier code to quickly test whether that
+ // object has been already marked.
std::unique_ptr<accounting::ObjectStack> rb_mark_bit_stack_;
+ // Thread-unsafe Boolean value hinting that `rb_mark_bit_stack_` is
+ // full. A thread-safe test of whether the read-barrier mark-bit
+ // stack is full is implemented by `rb_mark_bit_stack_->AtomicPushBack(ref)`
+ // (see use case in ConcurrentCopying::MarkFromReadBarrier).
bool rb_mark_bit_stack_full_;
+
std::vector<mirror::Object*> false_gray_stack_ GUARDED_BY(mark_stack_lock_);
Mutex mark_stack_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<accounting::ObjectStack*> revoked_mark_stacks_
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index fdfe949265..9ab965ec78 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -191,7 +191,7 @@ void MarkSweep::PausePhase() {
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
// Re-mark root set.
ReMarkRoots();
- // Scan dirty objects, this is only required if we are not doing concurrent GC.
+ // Scan dirty objects, this is only required if we are doing concurrent GC.
RecursiveMarkDirtyObjects(true, accounting::CardTable::kCardDirty);
}
{
@@ -259,8 +259,30 @@ void MarkSweep::MarkingPhase() {
BindBitmaps();
FindDefaultSpaceBitmap();
// Process dirty cards and add dirty cards to mod union tables.
- // If the GC type is non sticky, then we just clear the cards instead of ageing them.
- heap_->ProcessCards(GetTimings(), false, true, GetGcType() != kGcTypeSticky);
+ // If the GC type is non sticky, then we just clear the cards of the
+ // alloc space instead of aging them.
+ //
+ // Note that it is fine to clear the cards of the alloc space here,
+ // in the case of a concurrent (non-sticky) mark-sweep GC (whose
+ // marking phase _is_ performed concurrently with mutator threads
+ // running and possibly dirtying cards), as the whole alloc space
+ // will be traced in that case, starting *after* this call to
+ // Heap::ProcessCards (see calls to MarkSweep::MarkRoots and
+ // MarkSweep::MarkReachableObjects). References held by objects on
+ // cards that became dirty *after* the actual marking work started
+ // will be marked in the pause (see MarkSweep::PausePhase), in a
+ // *non-concurrent* way to prevent races with mutator threads.
+ //
+ // TODO: Do we need some sort of fence between the call to
+ // Heap::ProcessCard and the calls to MarkSweep::MarkRoot /
+ // MarkSweep::MarkReachableObjects below to make sure write
+ // operations in the card table clearing the alloc space's dirty
+ // cards (during the call to Heap::ProcessCard) are not reordered
+ // *after* marking actually starts?
+ heap_->ProcessCards(GetTimings(),
+ /* use_rem_sets */ false,
+ /* process_alloc_space_cards */ true,
+ /* clear_alloc_space_cards */ GetGcType() != kGcTypeSticky);
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
MarkRoots(self);
MarkReachableObjects();
@@ -1287,7 +1309,7 @@ void MarkSweep::Sweep(bool swap_bitmaps) {
CHECK_GE(live_stack_freeze_size_, GetHeap()->GetLiveStack()->Size());
{
TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings());
- // Mark everything allocated since the last as GC live so that we can sweep concurrently,
+ // Mark everything allocated since the last GC as live so that we can sweep concurrently,
// knowing that new allocations won't be marked as live.
accounting::ObjectStack* live_stack = heap_->GetLiveStack();
heap_->MarkAllocStackAsLive(live_stack);
diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc
index b66095fc2a..d93bd89835 100644
--- a/runtime/gc/collector/sticky_mark_sweep.cc
+++ b/runtime/gc/collector/sticky_mark_sweep.cc
@@ -63,7 +63,7 @@ void StickyMarkSweep::MarkReachableObjects() {
void StickyMarkSweep::MarkConcurrentRoots(VisitRootFlags flags) {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
// Visit all runtime roots and clear dirty flags including class loader. This is done to prevent
- // incorrect class unloading since the GC does not card mark when storing store the class during
+ // incorrect class unloading since the GC does not card mark when storing the class during
// object allocation. Doing this for each allocation would be slow.
// Since the card is not dirty, it means the object may not get scanned. This can cause class
// unloading to occur even though the class and class loader are reachable through the object's
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 84671c34b5..17913fc2dc 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -434,6 +434,7 @@ Heap::Heap(size_t initial_size,
// Create other spaces based on whether or not we have a moving GC.
if (foreground_collector_type_ == kCollectorTypeCC) {
CHECK(separate_non_moving_space);
+ // Reserve twice the capacity, to allow evacuating every region for explicit GCs.
MemMap* region_space_mem_map = space::RegionSpace::CreateMemMap(kRegionSpaceName,
capacity_ * 2,
request_begin);
@@ -517,7 +518,7 @@ Heap::Heap(size_t initial_size,
// Since we don't know where in the low_4gb the app image will be located, make the card table
// cover the whole low_4gb. TODO: Extend the card table in AddSpace.
UNUSED(heap_capacity);
- // Start at 64 KB, we can be sure there are no spaces mapped this low since the address range is
+ // Start at 4 KB, we can be sure there are no spaces mapped this low since the address range is
// reserved by the kernel.
static constexpr size_t kMinHeapAddress = 4 * KB;
card_table_.reset(accounting::CardTable::Create(reinterpret_cast<uint8_t*>(kMinHeapAddress),
@@ -1536,7 +1537,7 @@ void Heap::RecordFree(uint64_t freed_objects, int64_t freed_bytes) {
void Heap::RecordFreeRevoke() {
// Subtract num_bytes_freed_revoke_ from num_bytes_allocated_ to cancel out the
- // the ahead-of-time, bulk counting of bytes allocated in rosalloc thread-local buffers.
+ // ahead-of-time, bulk counting of bytes allocated in rosalloc thread-local buffers.
// If there's a concurrent revoke, ok to not necessarily reset num_bytes_freed_revoke_
// all the way to zero exactly as the remainder will be subtracted at the next GC.
size_t bytes_freed = num_bytes_freed_revoke_.LoadSequentiallyConsistent();
@@ -1758,7 +1759,7 @@ void Heap::SetTargetHeapUtilization(float target) {
size_t Heap::GetObjectsAllocated() const {
Thread* const self = Thread::Current();
ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated);
- // Prevent GC running during GetObjectsALlocated since we may get a checkpoint request that tells
+ // Prevent GC running during GetObjectsAllocated since we may get a checkpoint request that tells
// us to suspend while we are doing SuspendAll. b/35232978
gc::ScopedGCCriticalSection gcs(Thread::Current(),
gc::kGcCauseGetObjectsAllocated,
@@ -1899,10 +1900,10 @@ HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() {
MutexLock mu(self, *gc_complete_lock_);
// Ensure there is only one GC at a time.
WaitForGcToCompleteLocked(kGcCauseHomogeneousSpaceCompact, self);
- // Homogeneous space compaction is a copying transition, can't run it if the moving GC disable count
- // is non zero.
- // If the collector type changed to something which doesn't benefit from homogeneous space compaction,
- // exit.
+ // Homogeneous space compaction is a copying transition, can't run it if the moving GC disable
+ // count is non zero.
+ // If the collector type changed to something which doesn't benefit from homogeneous space
+ // compaction, exit.
if (disable_moving_gc_count_ != 0 || IsMovingGc(collector_type_) ||
!main_space_->CanMoveObjects()) {
return kErrorReject;
@@ -3445,8 +3446,8 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
TraceHeapSize(bytes_allocated);
uint64_t target_size;
collector::GcType gc_type = collector_ran->GetGcType();
- const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
- // foreground.
+ // Use the multiplier to grow more for foreground.
+ const double multiplier = HeapGrowthMultiplier();
const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier);
const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier);
if (gc_type != collector::kGcTypeSticky) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index faa6195259..7fb634fcf7 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -551,7 +551,7 @@ class Heap {
return total_memory - std::min(total_memory, byte_allocated);
}
- // get the space that corresponds to an object's address. Current implementation searches all
+ // Get the space that corresponds to an object's address. Current implementation searches all
// spaces in turn. If fail_ok is false then failing to find a space will cause an abort.
// TODO: consider using faster data structure like binary tree.
space::ContinuousSpace* FindContinuousSpaceFromObject(ObjPtr<mirror::Object>, bool fail_ok) const
@@ -1293,8 +1293,9 @@ class Heap {
// Parallel GC data structures.
std::unique_ptr<ThreadPool> thread_pool_;
- // For a GC cycle, a bitmap that is set corresponding to the
+ // A bitmap that is set corresponding to the known live objects since the last GC cycle.
std::unique_ptr<accounting::HeapBitmap> live_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_);
+ // A bitmap that is set corresponding to the marked objects in the current GC cycle.
std::unique_ptr<accounting::HeapBitmap> mark_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_);
// Mark stack that we reuse to avoid re-allocating the mark stack.
@@ -1312,7 +1313,7 @@ class Heap {
AllocatorType current_allocator_;
const AllocatorType current_non_moving_allocator_;
- // Which GCs we run in order when we an allocation fails.
+ // Which GCs we run in order when an allocation fails.
std::vector<collector::GcType> gc_plan_;
// Bump pointer spaces.
@@ -1320,6 +1321,7 @@ class Heap {
// Temp space is the space which the semispace collector copies to.
space::BumpPointerSpace* temp_space_;
+ // Region space, used by the concurrent collector.
space::RegionSpace* region_space_;
// Minimum free guarantees that you always have at least min_free_ free bytes after growing for
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 9d8e5d23eb..6d426c2dd0 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -81,7 +81,6 @@ class ZygoteHeapTest : public CommonRuntimeTest {
void SetUpRuntimeOptions(RuntimeOptions* options) {
CommonRuntimeTest::SetUpRuntimeOptions(options);
options->push_back(std::make_pair("-Xzygote", nullptr));
- options->push_back(std::make_pair("-Xno-hidden-api-checks", nullptr));
}
};
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index e74e9b169f..410931cbe5 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -24,26 +24,30 @@ namespace art {
namespace gc {
namespace space {
-inline mirror::Object* RegionSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated) {
+inline mirror::Object* RegionSpace::Alloc(Thread* self ATTRIBUTE_UNUSED,
+ size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated) {
num_bytes = RoundUp(num_bytes, kAlignment);
return AllocNonvirtual<false>(num_bytes, bytes_allocated, usable_size,
bytes_tl_bulk_allocated);
}
-inline mirror::Object* RegionSpace::AllocThreadUnsafe(Thread* self, size_t num_bytes,
- size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated) {
+inline mirror::Object* RegionSpace::AllocThreadUnsafe(Thread* self,
+ size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated) {
Locks::mutator_lock_->AssertExclusiveHeld(self);
return Alloc(self, num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
}
template<bool kForEvac>
-inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated) {
+inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated) {
DCHECK_ALIGNED(num_bytes, kAlignment);
mirror::Object* obj;
if (LIKELY(num_bytes <= kRegionSize)) {
@@ -79,8 +83,7 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by
}
} else {
// Large object.
- obj = AllocLarge<kForEvac>(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
+ obj = AllocLarge<kForEvac>(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
@@ -88,9 +91,10 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by
return nullptr;
}
-inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated) {
+inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated) {
DCHECK(IsAllocated() && IsInToSpace());
DCHECK_ALIGNED(num_bytes, kAlignment);
uint8_t* old_top;
@@ -238,9 +242,9 @@ inline mirror::Object* RegionSpace::GetNextObject(mirror::Object* obj) {
template<bool kForEvac>
inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes,
- size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated) {
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated) {
DCHECK_ALIGNED(num_bytes, kAlignment);
DCHECK_GT(num_bytes, kRegionSize);
size_t num_regs = RoundUp(num_bytes, kRegionSize) / kRegionSize;
@@ -254,7 +258,7 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes,
return nullptr;
}
}
- // Find a large enough contiguous free regions.
+ // Find a large enough set of contiguous free regions.
size_t left = 0;
while (left + num_regs - 1 < num_regions_) {
bool found = true;
@@ -270,7 +274,7 @@ inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes,
}
}
if (found) {
- // right points to the one region past the last free region.
+ // `right` points to the one region past the last free region.
DCHECK_EQ(left + num_regs, right);
Region* first_reg = &regions_[left];
DCHECK(first_reg->IsFree());
@@ -345,7 +349,7 @@ inline size_t RegionSpace::Region::BytesAllocated() const {
DCHECK_EQ(begin_, Top());
return 0;
} else {
- DCHECK(IsAllocated()) << static_cast<uint>(state_);
+ DCHECK(IsAllocated()) << "state=" << state_;
DCHECK_LE(begin_, Top());
size_t bytes;
if (is_a_tlab_) {
@@ -358,6 +362,20 @@ inline size_t RegionSpace::Region::BytesAllocated() const {
}
}
+inline size_t RegionSpace::Region::ObjectsAllocated() const {
+ if (IsLarge()) {
+ DCHECK_LT(begin_ + kRegionSize, Top());
+ DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U);
+ return 1;
+ } else if (IsLargeTail()) {
+ DCHECK_EQ(begin_, Top());
+ DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U);
+ return 0;
+ } else {
+ DCHECK(IsAllocated()) << "state=" << state_;
+ return objects_allocated_;
+ }
+}
} // namespace space
} // namespace gc
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index a505f32c49..8d94c86701 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -27,7 +27,7 @@ namespace space {
// If a region has live objects whose size is less than this percent
// value of the region size, evaculate the region.
-static constexpr uint kEvaculateLivePercentThreshold = 75U;
+static constexpr uint kEvacuateLivePercentThreshold = 75U;
// If we protect the cleared regions.
// Only protect for target builds to prevent flaky test failures (b/63131961).
@@ -158,14 +158,14 @@ size_t RegionSpace::ToSpaceSize() {
inline bool RegionSpace::Region::ShouldBeEvacuated() {
DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
- // if the region was allocated after the start of the
- // previous GC or the live ratio is below threshold, evacuate
- // it.
+ // The region should be evacuated if:
+ // - the region was allocated after the start of the previous GC (newly allocated region); or
+ // - the live ratio is below threshold (`kEvacuateLivePercentThreshold`).
bool result;
if (is_newly_allocated_) {
result = true;
} else {
- bool is_live_percent_valid = live_bytes_ != static_cast<size_t>(-1);
+ bool is_live_percent_valid = (live_bytes_ != static_cast<size_t>(-1));
if (is_live_percent_valid) {
DCHECK(IsInToSpace());
DCHECK(!IsLargeTail());
@@ -177,10 +177,10 @@ inline bool RegionSpace::Region::ShouldBeEvacuated() {
// Side node: live_percent == 0 does not necessarily mean
// there's no live objects due to rounding (there may be a
// few).
- result = live_bytes_ * 100U < kEvaculateLivePercentThreshold * bytes_allocated;
+ result = (live_bytes_ * 100U < kEvacuateLivePercentThreshold * bytes_allocated);
} else {
DCHECK(IsLarge());
- result = live_bytes_ == 0U;
+ result = (live_bytes_ == 0U);
}
} else {
result = false;
@@ -198,7 +198,10 @@ void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table, bool forc
rb_table->SetAll();
}
MutexLock mu(Thread::Current(), region_lock_);
- size_t num_expected_large_tails = 0;
+ // Counter for the number of expected large tail regions following a large region.
+ size_t num_expected_large_tails = 0U;
+ // Flag to store whether the previously seen large region has been evacuated.
+ // This is used to apply the same evacuation policy to related large tail regions.
bool prev_large_evacuated = false;
VerifyNonFreeRegionLimit();
const size_t iter_limit = kUseTableLookupReadBarrier
@@ -260,7 +263,8 @@ static void ZeroAndProtectRegion(uint8_t* begin, uint8_t* end) {
}
}
-void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_objects) {
+void RegionSpace::ClearFromSpace(/* out */ uint64_t* cleared_bytes,
+ /* out */ uint64_t* cleared_objects) {
DCHECK(cleared_bytes != nullptr);
DCHECK(cleared_objects != nullptr);
*cleared_bytes = 0;
@@ -272,18 +276,32 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje
// Update max of peak non free region count before reclaiming evacuated regions.
max_peak_num_non_free_regions_ = std::max(max_peak_num_non_free_regions_,
num_non_free_regions_);
- // Combine zeroing and releasing pages to reduce how often madvise is called. This helps
- // reduce contention on the mmap semaphore. b/62194020
- // clear_region adds a region to the current block. If the region is not adjacent, the
- // clear block is zeroed, released, and a new block begins.
+
+ // Lambda expression `clear_region` clears a region and adds a region to the
+ // "clear block".
+ //
+ // As we sweep regions to clear them, we maintain a "clear block", composed of
+ // adjacent cleared regions and whose bounds are `clear_block_begin` and
+ // `clear_block_end`. When processing a new region which is not adjacent to
+ // the clear block (discontinuity in cleared regions), the clear block
+ // is zeroed and released and the clear block is reset (to the most recent
+ // cleared region).
+ //
+ // This is done in order to combine zeroing and releasing pages to reduce how
+ // often madvise is called. This helps reduce contention on the mmap semaphore
+ // (see b/62194020).
uint8_t* clear_block_begin = nullptr;
uint8_t* clear_block_end = nullptr;
auto clear_region = [&clear_block_begin, &clear_block_end](Region* r) {
r->Clear(/*zero_and_release_pages*/false);
if (clear_block_end != r->Begin()) {
+ // Region `r` is not adjacent to the current clear block; zero and release
+ // pages within the current block and restart a new clear block at the
+ // beginning of region `r`.
ZeroAndProtectRegion(clear_block_begin, clear_block_end);
clear_block_begin = r->Begin();
}
+ // Add region `r` to the clear block.
clear_block_end = r->End();
};
for (size_t i = 0; i < std::min(num_regions_, non_free_region_index_limit_); ++i) {
@@ -334,12 +352,22 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje
++regions_to_clear_bitmap;
}
+ // Optimization: If the live bytes are *all* live in a region
+ // then the live-bit information for these objects is superfluous:
+ // - We can determine that these objects are all live by using
+ // Region::AllAllocatedBytesAreLive (which just checks whether
+ // `LiveBytes() == static_cast<size_t>(Top() - Begin())`.
+ // - We can visit the objects in this region using
+ // RegionSpace::GetNextObject, i.e. without resorting to the
+ // live bits (see RegionSpace::WalkInternal).
+ // Therefore, we can clear the bits for these objects in the
+ // (live) region space bitmap (and release the corresponding pages).
GetLiveBitmap()->ClearRange(
reinterpret_cast<mirror::Object*>(r->Begin()),
reinterpret_cast<mirror::Object*>(r->Begin() + regions_to_clear_bitmap * kRegionSize));
- // Skip over extra regions we cleared the bitmaps: we don't need to clear them, as they
- // are unevac region sthat are live.
- // Subtract one for the for loop.
+ // Skip over extra regions for which we cleared the bitmaps: we shall not clear them,
+ // as they are unevac regions that are live.
+ // Subtract one for the for-loop.
i += regions_to_clear_bitmap - 1;
}
}
@@ -432,7 +460,13 @@ void RegionSpace::ClampGrowthLimit(size_t new_capacity) {
void RegionSpace::Dump(std::ostream& os) const {
os << GetName() << " "
- << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit());
+ << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit());
+}
+
+void RegionSpace::DumpRegionForObject(std::ostream& os, mirror::Object* obj) {
+ CHECK(HasAddress(obj));
+ MutexLock mu(Thread::Current(), region_lock_);
+ RefToRegionUnlocked(obj)->Dump(os);
}
void RegionSpace::DumpRegions(std::ostream& os) {
@@ -526,13 +560,18 @@ void RegionSpace::AssertAllThreadLocalBuffersAreRevoked() {
}
void RegionSpace::Region::Dump(std::ostream& os) const {
- os << "Region[" << idx_ << "]=" << reinterpret_cast<void*>(begin_) << "-"
- << reinterpret_cast<void*>(Top())
+ os << "Region[" << idx_ << "]="
+ << reinterpret_cast<void*>(begin_)
+ << "-" << reinterpret_cast<void*>(Top())
<< "-" << reinterpret_cast<void*>(end_)
- << " state=" << static_cast<uint>(state_) << " type=" << static_cast<uint>(type_)
+ << " state=" << state_
+ << " type=" << type_
<< " objects_allocated=" << objects_allocated_
- << " alloc_time=" << alloc_time_ << " live_bytes=" << live_bytes_
- << " is_newly_allocated=" << is_newly_allocated_ << " is_a_tlab=" << is_a_tlab_ << " thread=" << thread_ << "\n";
+ << " alloc_time=" << alloc_time_
+ << " live_bytes=" << live_bytes_
+ << " is_newly_allocated=" << std::boolalpha << is_newly_allocated_ << std::noboolalpha
+ << " is_a_tlab=" << std::boolalpha << is_a_tlab_ << std::noboolalpha
+ << " thread=" << thread_ << '\n';
}
size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size) {
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 55c2772129..c3b7ff72ef 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -46,24 +46,33 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
static MemMap* CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin);
static RegionSpace* Create(const std::string& name, MemMap* mem_map);
- // Allocate num_bytes, returns null if the space is full.
- mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size, size_t* bytes_tl_bulk_allocated)
+ // Allocate `num_bytes`, returns null if the space is full.
+ mirror::Object* Alloc(Thread* self,
+ size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated)
OVERRIDE REQUIRES(!region_lock_);
// Thread-unsafe allocation for when mutators are suspended, used by the semispace collector.
- mirror::Object* AllocThreadUnsafe(Thread* self, size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size, size_t* bytes_tl_bulk_allocated)
+ mirror::Object* AllocThreadUnsafe(Thread* self,
+ size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated)
OVERRIDE REQUIRES(Locks::mutator_lock_) REQUIRES(!region_lock_);
// The main allocation routine.
template<bool kForEvac>
- ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated)
+ ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated)
REQUIRES(!region_lock_);
- // Allocate/free large objects (objects that are larger than the region size.)
+ // Allocate/free large objects (objects that are larger than the region size).
template<bool kForEvac>
- mirror::Object* AllocLarge(size_t num_bytes, size_t* bytes_allocated, size_t* usable_size,
- size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_);
+ mirror::Object* AllocLarge(size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_);
template<bool kForEvac>
void FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) REQUIRES(!region_lock_);
@@ -101,6 +110,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
void Dump(std::ostream& os) const;
void DumpRegions(std::ostream& os) REQUIRES(!region_lock_);
+ // Dump region containing object `obj`. Precondition: `obj` is in the region space.
+ void DumpRegionForObject(std::ostream& os, mirror::Object* obj) REQUIRES(!region_lock_);
void DumpNonFreeRegions(std::ostream& os) REQUIRES(!region_lock_);
size_t RevokeThreadLocalBuffers(Thread* thread) REQUIRES(!region_lock_);
@@ -174,7 +185,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
template <typename Visitor>
ALWAYS_INLINE void WalkToSpace(Visitor&& visitor)
REQUIRES(Locks::mutator_lock_) {
- WalkInternal<true>(visitor);
+ WalkInternal<true /* kToSpaceOnly */>(visitor);
}
accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE {
@@ -228,13 +239,16 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
return RegionType::kRegionTypeNone;
}
+ // Determine which regions to evacuate and tag them as
+ // from-space. Tag the rest as unevacuated from-space.
void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all)
REQUIRES(!region_lock_);
size_t FromSpaceSize() REQUIRES(!region_lock_);
size_t UnevacFromSpaceSize() REQUIRES(!region_lock_);
size_t ToSpaceSize() REQUIRES(!region_lock_);
- void ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_objects) REQUIRES(!region_lock_);
+ void ClearFromSpace(/* out */ uint64_t* cleared_bytes, /* out */ uint64_t* cleared_objects)
+ REQUIRES(!region_lock_);
void AddLiveBytes(mirror::Object* ref, size_t alloc_size) {
Region* reg = RefToRegionUnlocked(ref);
@@ -301,12 +315,13 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
void Clear(bool zero_and_release_pages);
- ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated,
- size_t* usable_size,
- size_t* bytes_tl_bulk_allocated);
+ ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes,
+ /* out */ size_t* bytes_allocated,
+ /* out */ size_t* usable_size,
+ /* out */ size_t* bytes_tl_bulk_allocated);
bool IsFree() const {
- bool is_free = state_ == RegionState::kRegionStateFree;
+ bool is_free = (state_ == RegionState::kRegionStateFree);
if (is_free) {
DCHECK(IsInNoSpace());
DCHECK_EQ(begin_, Top());
@@ -319,9 +334,11 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
void Unfree(RegionSpace* region_space, uint32_t alloc_time)
REQUIRES(region_space->region_lock_);
+ // Given a free region, declare it non-free (allocated) and large.
void UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time)
REQUIRES(region_space->region_lock_);
+ // Given a free region, declare it non-free (allocated) and large tail.
void UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time)
REQUIRES(region_space->region_lock_);
@@ -339,7 +356,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
// Large allocated.
bool IsLarge() const {
- bool is_large = state_ == RegionState::kRegionStateLarge;
+ bool is_large = (state_ == RegionState::kRegionStateLarge);
if (is_large) {
DCHECK_LT(begin_ + kRegionSize, Top());
}
@@ -348,7 +365,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
// Large-tail allocated.
bool IsLargeTail() const {
- bool is_large_tail = state_ == RegionState::kRegionStateLargeTail;
+ bool is_large_tail = (state_ == RegionState::kRegionStateLargeTail);
if (is_large_tail) {
DCHECK_EQ(begin_, Top());
}
@@ -379,23 +396,33 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
return type_ == RegionType::kRegionTypeNone;
}
+ // Set this region as evacuated from-space. At the end of the
+ // collection, RegionSpace::ClearFromSpace will clear and reclaim
+ // the space used by this region, and tag it as unallocated/free.
void SetAsFromSpace() {
DCHECK(!IsFree() && IsInToSpace());
type_ = RegionType::kRegionTypeFromSpace;
live_bytes_ = static_cast<size_t>(-1);
}
+ // Set this region as unevacuated from-space. At the end of the
+ // collection, RegionSpace::ClearFromSpace will preserve the space
+ // used by this region, and tag it as to-space (see
+ // Region::SetUnevacFromSpaceAsToSpace below).
void SetAsUnevacFromSpace() {
DCHECK(!IsFree() && IsInToSpace());
type_ = RegionType::kRegionTypeUnevacFromSpace;
live_bytes_ = 0U;
}
+ // Set this region as to-space. Used by RegionSpace::ClearFromSpace.
+ // This is only valid if it is currently an unevac from-space region.
void SetUnevacFromSpaceAsToSpace() {
DCHECK(!IsFree() && IsInUnevacFromSpace());
type_ = RegionType::kRegionTypeToSpace;
}
+ // Return whether this region should be evacuated. Used by RegionSpace::SetFromSpace.
ALWAYS_INLINE bool ShouldBeEvacuated();
void AddLiveBytes(size_t live_bytes) {
@@ -418,20 +445,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
size_t BytesAllocated() const;
- size_t ObjectsAllocated() const {
- if (IsLarge()) {
- DCHECK_LT(begin_ + kRegionSize, Top());
- DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U);
- return 1;
- } else if (IsLargeTail()) {
- DCHECK_EQ(begin_, Top());
- DCHECK_EQ(objects_allocated_.LoadRelaxed(), 0U);
- return 0;
- } else {
- DCHECK(IsAllocated()) << static_cast<uint>(state_);
- return objects_allocated_;
- }
- }
+ size_t ObjectsAllocated() const;
uint8_t* Begin() const {
return begin_;
@@ -467,12 +481,17 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
private:
size_t idx_; // The region's index in the region space.
uint8_t* begin_; // The begin address of the region.
+ // Note that `top_` can be higher than `end_` in the case of a
+ // large region, where an allocated object spans multiple regions
+ // (large region + one or more large tail regions).
Atomic<uint8_t*> top_; // The current position of the allocation.
uint8_t* end_; // The end address of the region.
RegionState state_; // The region state (see RegionState).
RegionType type_; // The region type (see RegionType).
Atomic<size_t> objects_allocated_; // The number of objects allocated.
uint32_t alloc_time_; // The allocation time of the region.
+ // Note that newly allocated and evacuated regions use -1 as
+ // special value for `live_bytes_`.
size_t live_bytes_; // The live bytes. Used to compute the live percent.
bool is_newly_allocated_; // True if it's allocated after the last collection.
bool is_a_tlab_; // True if it's a tlab.
@@ -488,12 +507,12 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
Region* RefToRegionUnlocked(mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS {
// For a performance reason (this is frequently called via
- // IsInFromSpace() etc.) we avoid taking a lock here. Note that
- // since we only change a region from to-space to from-space only
- // during a pause (SetFromSpace()) and from from-space to free
- // (after GC is done) as long as ref is a valid reference into an
- // allocated region, it's safe to access the region state without
- // the lock.
+ // RegionSpace::IsInFromSpace, etc.) we avoid taking a lock here.
+ // Note that since we only change a region from to-space to (evac)
+ // from-space during a pause (in RegionSpace::SetFromSpace) and
+ // from (evac) from-space to free (after GC is done), as long as
+ // `ref` is a valid reference into an allocated region, it's safe
+ // to access the region state without the lock.
return RefToRegionLocked(ref);
}
@@ -508,6 +527,13 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
return reg;
}
+ // Return the object location following `obj` in the region space
+ // (i.e., the object location at `obj + obj->SizeOf()`).
+ //
+ // Note that
+ // - unless the region containing `obj` is fully used; and
+ // - `obj` is not the last object of that region;
+ // the returned location is not guaranteed to be a valid object.
mirror::Object* GetNextObject(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -524,6 +550,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
VerifyNonFreeRegionLimit();
}
+ // Implementation of this invariant:
+ // for all `i >= non_free_region_index_limit_`, `regions_[i].IsFree()` is true.
void VerifyNonFreeRegionLimit() REQUIRES(region_lock_) {
if (kIsDebugBuild && non_free_region_index_limit_ < num_regions_) {
for (size_t i = non_free_region_index_limit_; i < num_regions_; ++i) {
@@ -549,14 +577,18 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
// regions are in non-free.
size_t max_peak_num_non_free_regions_;
+ // The pointer to the region array.
std::unique_ptr<Region[]> regions_ GUARDED_BY(region_lock_);
- // The pointer to the region array.
+
// The upper-bound index of the non-free regions. Used to avoid scanning all regions in
- // SetFromSpace(). Invariant: for all i >= non_free_region_index_limit_, regions_[i].IsFree() is
- // true.
+ // RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace.
+ //
+ // Invariant (verified by RegionSpace::VerifyNonFreeRegionLimit):
+ // for all `i >= non_free_region_index_limit_`, `regions_[i].IsFree()` is true.
size_t non_free_region_index_limit_ GUARDED_BY(region_lock_);
- Region* current_region_; // The region that's being allocated currently.
- Region* evac_region_; // The region that's being evacuated to currently.
+
+ Region* current_region_; // The region currently used for allocation.
+ Region* evac_region_; // The region currently used for evacuation.
Region full_region_; // The dummy/sentinel region that looks full.
// Mark bitmap used by the GC.
diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc
index 2c6afa7eb8..a8bd7b816e 100644
--- a/runtime/gc/space/space.cc
+++ b/runtime/gc/space/space.cc
@@ -107,7 +107,6 @@ collector::ObjectBytePair ContinuousMemMapAllocSpace::Sweep(bool swap_bitmaps) {
return scc.freed;
}
-// Returns the old mark bitmap.
void ContinuousMemMapAllocSpace::BindLiveToMarkBitmap() {
CHECK(!HasBoundBitmaps());
accounting::ContinuousSpaceBitmap* live_bitmap = GetLiveBitmap();
@@ -125,7 +124,7 @@ bool ContinuousMemMapAllocSpace::HasBoundBitmaps() const {
void ContinuousMemMapAllocSpace::UnBindBitmaps() {
CHECK(HasBoundBitmaps());
- // At this point, the temp_bitmap holds our old mark bitmap.
+ // At this point, `temp_bitmap_` holds our old mark bitmap.
accounting::ContinuousSpaceBitmap* new_bitmap = temp_bitmap_.release();
Runtime::Current()->GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap_.get(), new_bitmap);
CHECK_EQ(mark_bitmap_.release(), live_bitmap_.get());
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 6b76048cb1..12bccb35e7 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -389,7 +389,11 @@ class MemMapSpace : public ContinuousSpace {
}
protected:
- MemMapSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end, uint8_t* limit,
+ MemMapSpace(const std::string& name,
+ MemMap* mem_map,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
GcRetentionPolicy gc_retention_policy)
: ContinuousSpace(name, gc_retention_policy, begin, end, limit),
mem_map_(mem_map) {
@@ -420,7 +424,10 @@ class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace {
}
bool HasBoundBitmaps() const REQUIRES(Locks::heap_bitmap_lock_);
+ // Make the mark bitmap an alias of the live bitmap. Save the current mark bitmap into
+ // `temp_bitmap_`, so that we can restore it later in ContinuousMemMapAllocSpace::UnBindBitmaps.
void BindLiveToMarkBitmap() REQUIRES(Locks::heap_bitmap_lock_);
+ // Unalias the mark bitmap from the live bitmap and restore the old mark bitmap.
void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_);
// Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping.
void SwapBitmaps();
diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h
index 4d10de8237..08231017e7 100644
--- a/runtime/gc/space/zygote_space.h
+++ b/runtime/gc/space/zygote_space.h
@@ -26,7 +26,7 @@ namespace gc {
namespace space {
-// An zygote space is a space which you cannot allocate into or free from.
+// A zygote space is a space which you cannot allocate into or free from.
class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace {
public:
// Returns the remaining storage in the out_map field.
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index d99b37762f..fb5db1147f 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -140,7 +140,7 @@ bool Verification::IsValidClass(const void* addr) const {
if (!IsValidHeapObjectAddress(k1)) {
return false;
}
- // k should be class class, take the class again to verify.
+ // `k1` should be class class, take the class again to verify.
// Note that this check may not be valid for the no image space since the class class might move
// around from moving GC.
mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>();
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 05e68e66dd..d7e5e18b9e 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -31,6 +31,23 @@ enum Action {
kDeny
};
+enum AccessMethod {
+ kReflection,
+ kJNI
+};
+
+inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
+ switch (value) {
+ case kReflection:
+ os << "reflection";
+ break;
+ case kJNI:
+ os << "JNI";
+ break;
+ }
+ return os;
+}
+
inline Action GetMemberAction(uint32_t access_flags) {
switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
case HiddenApiAccessFlags::kWhitelist:
@@ -45,19 +62,25 @@ inline Action GetMemberAction(uint32_t access_flags) {
}
// Issue a warning about field access.
-inline void WarnAboutMemberAccess(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
+inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
std::string tmp;
LOG(WARNING) << "Accessing hidden field "
<< field->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
- << field->GetName() << ":" << field->GetTypeDescriptor();
+ << field->GetName() << ":" << field->GetTypeDescriptor()
+ << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags())
+ << ", " << access_method << ")";
}
// Issue a warning about method access.
-inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
std::string tmp;
LOG(WARNING) << "Accessing hidden method "
<< method->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
- << method->GetName() << method->GetSignature().ToString();
+ << method->GetName() << method->GetSignature().ToString()
+ << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags())
+ << ", " << access_method << ")";
}
// Returns true if access to `member` should be denied to the caller of the
@@ -69,7 +92,8 @@ inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::muta
template<typename T>
inline bool ShouldBlockAccessToMember(T* member,
Thread* self,
- std::function<bool(Thread*)> fn_caller_in_boot)
+ std::function<bool(Thread*)> fn_caller_in_boot,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
@@ -92,25 +116,33 @@ inline bool ShouldBlockAccessToMember(T* member,
return false;
}
- // Member is hidden and we are not in the boot class path. Act accordingly.
+ // Member is hidden and we are not in the boot class path.
+
+ // Print a log message with information about this class member access.
+ // We do this regardless of whether we block the access or not.
+ WarnAboutMemberAccess(member, access_method);
+
+ // Block access if on blacklist.
if (action == kDeny) {
return true;
- } else {
- DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
-
- // Allow access to this member but print a warning. Depending on a runtime
- // flag, we might move the member into whitelist and skip the warning the
- // next time the member is used.
- if (runtime->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
- }
- WarnAboutMemberAccess(member);
- if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
- Runtime::Current()->SetPendingHiddenApiWarning(true);
- }
- return false;
}
+
+ // Allow access to this member but print a warning.
+ DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
+
+ // Depending on a runtime flag, we might move the member into whitelist and
+ // skip the warning the next time the member is accessed.
+ if (runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ }
+
+ // If this action requires a UI warning, set the appropriate flag.
+ if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+ Runtime::Current()->SetPendingHiddenApiWarning(true);
+ }
+
+ return false;
}
// Returns true if access to member with `access_flags` should be denied to `caller`.
diff --git a/runtime/hidden_api_access_flags.h b/runtime/hidden_api_access_flags.h
index c328f965d2..6a88c12be5 100644
--- a/runtime/hidden_api_access_flags.h
+++ b/runtime/hidden_api_access_flags.h
@@ -146,6 +146,24 @@ class HiddenApiAccessFlags {
};
};
+inline std::ostream& operator<<(std::ostream& os, HiddenApiAccessFlags::ApiList value) {
+ switch (value) {
+ case HiddenApiAccessFlags::kWhitelist:
+ os << "whitelist";
+ break;
+ case HiddenApiAccessFlags::kLightGreylist:
+ os << "light greylist";
+ break;
+ case HiddenApiAccessFlags::kDarkGreylist:
+ os << "dark greylist";
+ break;
+ case HiddenApiAccessFlags::kBlacklist:
+ os << "blacklist";
+ break;
+ }
+ return os;
+}
+
} // namespace art
diff --git a/runtime/image.cc b/runtime/image.cc
index 8e3615ffcf..99406229a5 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '4', '\0' }; // Math.pow() intrinsic.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '5', '\0' }; // Bitstring type check off.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 24cedb093b..0ae6dbfa88 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -269,7 +269,20 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
}
} else {
CHECK_NE(return_pc, 0U);
- CHECK(!reached_existing_instrumentation_frames_);
+ if (UNLIKELY(reached_existing_instrumentation_frames_)) {
+ std::string thread_name;
+ GetThread()->GetThreadName(thread_name);
+ uint32_t dex_pc = dex::kDexNoIndex;
+ if (last_return_pc_ != 0 &&
+ GetCurrentOatQuickMethodHeader() != nullptr) {
+ dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
+ }
+ LOG(FATAL) << "While walking " << thread_name << " found existing instrumentation frames."
+ << " method is " << GetMethod()->PrettyMethod()
+ << " return_pc is " << std::hex << return_pc
+ << " dex pc: " << dex_pc;
+ UNREACHABLE();
+ }
InstrumentationStackFrame instrumentation_frame(
m->IsRuntimeMethod() ? nullptr : GetThisObject(),
m,
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 12b8c38bbb..a8ab626a62 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -31,6 +31,7 @@
#include "mirror/class.h"
#include "mirror/emulated_stack_frame.h"
#include "mirror/method_handle_impl-inl.h"
+#include "mirror/var_handle.h"
#include "reflection-inl.h"
#include "reflection.h"
#include "stack.h"
@@ -723,263 +724,149 @@ bool DoMethodHandleInvoke(Thread* self,
}
}
-static bool UnimplementedSignaturePolymorphicMethod(Thread* self ATTRIBUTE_UNUSED,
- ShadowFrame& shadow_frame ATTRIBUTE_UNUSED,
- const Instruction* inst ATTRIBUTE_UNUSED,
- uint16_t inst_data ATTRIBUTE_UNUSED,
- JValue* result ATTRIBUTE_UNUSED)
+static bool DoVarHandleInvokeChecked(Thread* self,
+ Handle<mirror::VarHandle> var_handle,
+ Handle<mirror::MethodType> callsite_type,
+ mirror::VarHandle::AccessMode access_mode,
+ ShadowFrame& shadow_frame,
+ InstructionOperands* operands,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
- UNIMPLEMENTED(FATAL) << "TODO(oth): b/65872996";
- return false;
-}
-
-bool DoVarHandleCompareAndExchange(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleCompareAndExchangeAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleCompareAndExchangeRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleCompareAndSet(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGet(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndAdd(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndAddAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndAddRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseAnd(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseAndAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseAndRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseOr(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseOrAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseOrRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseXor(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseXorAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndBitwiseXorRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndSet(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndSetAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetAndSetRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetOpaque(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
-
-bool DoVarHandleGetVolatile(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+ // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType()
+ // which is only required if we need to convert argument and/or
+ // return types.
+ StackHandleScope<1> hs(self);
+ Handle<mirror::MethodType> accessor_type(hs.NewHandle(
+ var_handle->GetMethodTypeForAccessMode(self, access_mode)));
+ const size_t num_vregs = accessor_type->NumberOfVRegs();
+ const int num_params = accessor_type->GetPTypes()->GetLength();
+ ShadowFrameAllocaUniquePtr accessor_frame =
+ CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+ ShadowFrameGetter getter(shadow_frame, operands);
+ static const uint32_t kFirstDestinationReg = 0;
+ ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
+ if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
+ return false;
+ }
+ RangeInstructionOperands accessor_operands(kFirstDestinationReg,
+ kFirstDestinationReg + num_vregs);
+ if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
+ return false;
+ }
+ return ConvertReturnValue(callsite_type, accessor_type, result);
}
-bool DoVarHandleSet(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
+static bool DoVarHandleInvokeCommon(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result,
+ mirror::VarHandle::AccessMode access_mode)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Make sure to check for async exceptions
+ if (UNLIKELY(self->ObserveAsyncException())) {
+ return false;
+ }
-bool DoVarHandleSetOpaque(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
+ bool is_var_args = inst->HasVarArgs();
+ const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
+ ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
+ if (receiver.IsNull()) {
+ ThrowNullPointerExceptionFromDexPC();
+ return false;
+ }
-bool DoVarHandleSetRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
+ StackHandleScope<2> hs(self);
+ Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
+ if (!var_handle->IsAccessModeSupported(access_mode)) {
+ ThrowUnsupportedOperationException();
+ return false;
+ }
-bool DoVarHandleSetVolatile(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
+ const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc();
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::MethodType> callsite_type(hs.NewHandle(
+ class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod())));
+ // This implies we couldn't resolve one or more types in this VarHandle.
+ if (UNLIKELY(callsite_type == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
-bool DoVarHandleWeakCompareAndSet(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
+ if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) {
+ ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode),
+ callsite_type.Get());
+ return false;
+ }
-bool DoVarHandleWeakCompareAndSetAcquire(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+ if (is_var_args) {
+ uint32_t args[Instruction::kMaxVarArgRegs];
+ inst->GetVarArgs(args, inst_data);
+ VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc());
+ NoReceiverInstructionOperands operands(&all_operands);
+ return DoVarHandleInvokeChecked(self,
+ var_handle,
+ callsite_type,
+ access_mode,
+ shadow_frame,
+ &operands,
+ result);
+ } else {
+ RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc());
+ NoReceiverInstructionOperands operands(&all_operands);
+ return DoVarHandleInvokeChecked(self,
+ var_handle,
+ callsite_type,
+ access_mode,
+ shadow_frame,
+ &operands,
+ result);
+ }
}
-bool DoVarHandleWeakCompareAndSetPlain(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
+#define DO_VAR_HANDLE_ACCESSOR(_access_mode) \
+bool DoVarHandle ## _access_mode(Thread* self, \
+ ShadowFrame& shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { \
+ const auto access_mode = mirror::VarHandle::AccessMode::k ## _access_mode; \
+ return DoVarHandleInvokeCommon(self, shadow_frame, inst, inst_data, result, access_mode); \
}
-bool DoVarHandleWeakCompareAndSetRelease(Thread* self,
- ShadowFrame& shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
- return UnimplementedSignaturePolymorphicMethod(self, shadow_frame, inst, inst_data, result);
-}
+DO_VAR_HANDLE_ACCESSOR(CompareAndExchange)
+DO_VAR_HANDLE_ACCESSOR(CompareAndExchangeAcquire)
+DO_VAR_HANDLE_ACCESSOR(CompareAndExchangeRelease)
+DO_VAR_HANDLE_ACCESSOR(CompareAndSet)
+DO_VAR_HANDLE_ACCESSOR(Get)
+DO_VAR_HANDLE_ACCESSOR(GetAcquire)
+DO_VAR_HANDLE_ACCESSOR(GetAndAdd)
+DO_VAR_HANDLE_ACCESSOR(GetAndAddAcquire)
+DO_VAR_HANDLE_ACCESSOR(GetAndAddRelease)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseAnd)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseAndAcquire)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseAndRelease)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseOr)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseOrAcquire)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseOrRelease)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseXor)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseXorAcquire)
+DO_VAR_HANDLE_ACCESSOR(GetAndBitwiseXorRelease)
+DO_VAR_HANDLE_ACCESSOR(GetAndSet)
+DO_VAR_HANDLE_ACCESSOR(GetAndSetAcquire)
+DO_VAR_HANDLE_ACCESSOR(GetAndSetRelease)
+DO_VAR_HANDLE_ACCESSOR(GetOpaque)
+DO_VAR_HANDLE_ACCESSOR(GetVolatile)
+DO_VAR_HANDLE_ACCESSOR(Set)
+DO_VAR_HANDLE_ACCESSOR(SetOpaque)
+DO_VAR_HANDLE_ACCESSOR(SetRelease)
+DO_VAR_HANDLE_ACCESSOR(SetVolatile)
+DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSet)
+DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSetAcquire)
+DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSetPlain)
+DO_VAR_HANDLE_ACCESSOR(WeakCompareAndSetRelease)
+
+#undef DO_VAR_HANDLE_ACCESSOR
template<bool is_range>
bool DoInvokePolymorphic(Thread* self,
@@ -1049,7 +936,8 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
// The first parameter is a MethodHandles lookup instance.
{
- Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass()));
+ Handle<mirror::Class> lookup_class =
+ hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass());
ObjPtr<mirror::MethodHandlesLookup> lookup =
mirror::MethodHandlesLookup::Create(self, lookup_class);
if (lookup.IsNull()) {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 8180222e22..39a1db85d4 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -176,7 +176,8 @@ static inline bool DoInvoke(Thread* self,
}
const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- ObjPtr<mirror::Object> receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
+ ObjPtr<mirror::Object> receiver =
+ (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
ArtMethod* sf_method = shadow_frame.GetMethod();
ArtMethod* const called_method = FindMethodFromCode<type, do_access_check>(
method_idx, &receiver, sf_method, self);
@@ -645,7 +646,7 @@ EXPLICIT_DO_FAST_INVOKE_TEMPLATE_DECL(kVirtual); // invoke-virtual
// Explicitly instantiate all DoInvokeVirtualQuick functions.
#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \
const Instruction* inst, uint16_t inst_data, \
JValue* result)
diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S
index df4bcc66f3..7c7c527ef4 100644
--- a/runtime/interpreter/mterp/arm/entry.S
+++ b/runtime/interpreter/mterp/arm/entry.S
@@ -56,7 +56,7 @@ ENTRY ExecuteMterpImpl
VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame
ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc.
add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index 64ab9efa19..1f15f870ea 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -93,6 +93,8 @@ unspecified registers or condition codes.
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
#define rPC r4
+#define CFI_DEX 4 // DWARF register number of the register holding dex-pc (xPC).
+#define CFI_TMP 0 // DWARF register number of the first argument register (r0).
#define rFP r5
#define rSELF r6
#define rINST r7
diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S
index 8d61210be8..cf38a2992d 100644
--- a/runtime/interpreter/mterp/arm64/entry.S
+++ b/runtime/interpreter/mterp/arm64/entry.S
@@ -46,7 +46,7 @@ ENTRY ExecuteMterpImpl
add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame
ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc.
add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode
- .cfi_register DPC_PSEUDO_REG, xPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S
index 9261b770d6..f0bf8ca34e 100644
--- a/runtime/interpreter/mterp/arm64/header.S
+++ b/runtime/interpreter/mterp/arm64/header.S
@@ -95,6 +95,8 @@ codes.
/* During bringup, we'll use the shadow frame model instead of xFP */
/* single-purpose registers, given names for clarity */
#define xPC x20
+#define CFI_DEX 20 // DWARF register number of the register holding dex-pc (xPC).
+#define CFI_TMP 0 // DWARF register number of the first argument register (r0).
#define xFP x21
#define xSELF x22
#define xINST x23
diff --git a/runtime/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/mterp/cfi_asm_support.h
index a97e153993..0df4eb4f81 100644
--- a/runtime/interpreter/mterp/cfi_asm_support.h
+++ b/runtime/interpreter/mterp/cfi_asm_support.h
@@ -18,14 +18,30 @@
#define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_
/*
- * To keep track of the Dalvik PC, give assign it a magic register number that
- * won't be confused with a pysical register. Then, standard .cfi directives
- * will track the location of it so that it may be extracted during a stack
- * unwind.
+ * Define the DEX PC (memory address of the currently interpreted bytecode)
+ * within the CFI stream of the current function (stored in .eh_frame).
+ * This allows libunwind to detect that the frame is in the interpreter,
+ * and to resolve the memory address into human readable Java method name.
+ * The CFI instruction is recognised by the magic bytes in the expression
+ * (we push magic "DEX1" constant on the DWARF stack and drop it again).
*
- * The Dalvik PC will be in either a physical registor, or the frame.
- * Encoded from the ASCII string " DEX" -> 0x20 0x44 0x45 0x58
+ * As with any other CFI opcode, the expression needs to be associated with
+ * a register. Any caller-save register will do as those are unused in CFI.
+ * Better solution would be to store the expression in Android-specific
+ * DWARF register (CFI registers don't have to correspond to real hardware
+ * registers), however, gdb handles any unknown registers very poorly.
+ * Similarly, we could also use some of the user-defined opcodes defined
+ * in the DWARF specification, but gdb doesn't support those either.
+ *
+ * The DEX PC is generally advanced in the middle of the bytecode handler,
+ * which will result in the reported DEX PC to be off by an instruction.
+ * Therefore the macro allows adding/subtracting an offset to compensate.
+ * TODO: Add the offsets to handlers to get line-accurate DEX PC reporting.
*/
-#define DPC_PSEUDO_REG 0x20444558
+#define CFI_DEFINE_DEX_PC_WITH_OFFSET(tmpReg, dexReg, dexOffset) .cfi_escape \
+ 0x16 /* DW_CFA_val_expression */, tmpReg, 0x09 /* size */, \
+ 0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = "DEX1" */ \
+ 0x13 /* DW_OP_drop */, \
+ 0x92 /* DW_OP_bregx */, dexReg, (dexOffset & 0x7F) /* 1-byte SLEB128 */
#endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_
diff --git a/runtime/interpreter/mterp/gen_mterp.py b/runtime/interpreter/mterp/gen_mterp.py
index 1c9af30d0a..40d99df037 100755
--- a/runtime/interpreter/mterp/gen_mterp.py
+++ b/runtime/interpreter/mterp/gen_mterp.py
@@ -22,7 +22,7 @@
import sys, string, re, time
from string import Template
-interp_defs_file = "../../dex_instruction_list.h" # need opcode list
+interp_defs_file = "../../dex/dex_instruction_list.h" # need opcode list
kNumPackedOpcodes = 256
splitops = False
diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S
index 41b5d5650d..d342354969 100644
--- a/runtime/interpreter/mterp/mips/entry.S
+++ b/runtime/interpreter/mterp/mips/entry.S
@@ -54,7 +54,7 @@ ExecuteMterpImpl:
EAS2(rREFS, rFP, a0) # point to reference array in shadow frame
lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc
EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC()
diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S
index 0f7a6f1116..014628f415 100644
--- a/runtime/interpreter/mterp/mips/header.S
+++ b/runtime/interpreter/mterp/mips/header.S
@@ -61,6 +61,8 @@
/* single-purpose registers, given names for clarity */
#define rPC s0
+#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0).
+#define CFI_TMP 4 // DWARF register number of the first argument register (a0).
#define rFP s1
#define rSELF s2
#define rIBASE s3
diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S
index 841a817569..ed965aa201 100644
--- a/runtime/interpreter/mterp/mips64/entry.S
+++ b/runtime/interpreter/mterp/mips64/entry.S
@@ -73,7 +73,7 @@ ExecuteMterpImpl:
dlsa rREFS, v0, rFP, 2
lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2)
dlsa rPC, v0, a1, 1
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S
index 2b550cb533..4947aff38e 100644
--- a/runtime/interpreter/mterp/mips64/header.S
+++ b/runtime/interpreter/mterp/mips64/header.S
@@ -90,6 +90,8 @@ The following registers have fixed assignments:
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
#define rPC s0
+#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0).
+#define CFI_TMP 4 // DWARF register number of the first argument register (a0).
#define rFP s1
#define rSELF s2
#define rINST s3
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index f3c1124ec4..5c1a13b9d6 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -100,6 +100,8 @@ unspecified registers or condition codes.
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
#define rPC r4
+#define CFI_DEX 4 // DWARF register number of the register holding dex-pc (xPC).
+#define CFI_TMP 0 // DWARF register number of the first argument register (r0).
#define rFP r5
#define rSELF r6
#define rINST r7
@@ -375,7 +377,7 @@ ENTRY ExecuteMterpImpl
VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame
ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc.
add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 347d54f705..72446ba082 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -102,6 +102,8 @@ codes.
/* During bringup, we'll use the shadow frame model instead of xFP */
/* single-purpose registers, given names for clarity */
#define xPC x20
+#define CFI_DEX 20 // DWARF register number of the register holding dex-pc (xPC).
+#define CFI_TMP 0 // DWARF register number of the first argument register (r0).
#define xFP x21
#define xSELF x22
#define xINST x23
@@ -405,7 +407,7 @@ ENTRY ExecuteMterpImpl
add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame
ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc.
add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode
- .cfi_register DPC_PSEUDO_REG, xPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S
index 1687afa58a..d5861b28a7 100644
--- a/runtime/interpreter/mterp/out/mterp_mips.S
+++ b/runtime/interpreter/mterp/out/mterp_mips.S
@@ -68,6 +68,8 @@
/* single-purpose registers, given names for clarity */
#define rPC s0
+#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0).
+#define CFI_TMP 4 // DWARF register number of the first argument register (a0).
#define rFP s1
#define rSELF s2
#define rIBASE s3
@@ -788,7 +790,7 @@ ExecuteMterpImpl:
EAS2(rREFS, rFP, a0) # point to reference array in shadow frame
lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc
EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC()
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 559c72bb0c..5224df92be 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -97,6 +97,8 @@ The following registers have fixed assignments:
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
#define rPC s0
+#define CFI_DEX 16 // DWARF register number of the register holding dex-pc (s0).
+#define CFI_TMP 4 // DWARF register number of the first argument register (a0).
#define rFP s1
#define rSELF s2
#define rINST s3
@@ -408,7 +410,7 @@ ExecuteMterpImpl:
dlsa rREFS, v0, rFP, 2
lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2)
dlsa rPC, v0, a1, 1
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index 0613c9d12e..f98fa5b74f 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -164,6 +164,8 @@ unspecified registers or condition codes.
/* single-purpose registers, given names for clarity */
#define rSELF IN_ARG0(%esp)
#define rPC %esi
+#define CFI_DEX 6 // DWARF register number of the register holding dex-pc (esi).
+#define CFI_TMP 0 // DWARF register number of the first argument register (eax).
#define rFP %edi
#define rINST %ebx
#define rINSTw %bx
@@ -380,7 +382,7 @@ SYMBOL(ExecuteMterpImpl):
leal (rFP, %eax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
lea (%ecx, %eax, 2), rPC
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Set up for backwards branches & osr profiling */
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index aa91db3b61..d82a2d2eb0 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -164,6 +164,8 @@ unspecified registers or condition codes.
/* single-purpose registers, given names for clarity */
#define rSELF SELF_SPILL(%rsp)
#define rPC %r12
+#define CFI_DEX 12 // DWARF register number of the register holding dex-pc (rPC).
+#define CFI_TMP 5 // DWARF register number of the first argument register (rdi).
#define rFP %r13
#define rINST %ebx
#define rINSTq %rbx
@@ -363,7 +365,7 @@ SYMBOL(ExecuteMterpImpl):
leaq (rFP, %rax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
leaq (IN_ARG1, %rax, 2), rPC
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S
index 10ca8366de..324637bf9a 100644
--- a/runtime/interpreter/mterp/x86/entry.S
+++ b/runtime/interpreter/mterp/x86/entry.S
@@ -61,7 +61,7 @@ SYMBOL(ExecuteMterpImpl):
leal (rFP, %eax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
lea (%ecx, %eax, 2), rPC
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Set up for backwards branches & osr profiling */
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 0e585e86f0..2e3bbdf6f7 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -157,6 +157,8 @@ unspecified registers or condition codes.
/* single-purpose registers, given names for clarity */
#define rSELF IN_ARG0(%esp)
#define rPC %esi
+#define CFI_DEX 6 // DWARF register number of the register holding dex-pc (esi).
+#define CFI_TMP 0 // DWARF register number of the first argument register (eax).
#define rFP %edi
#define rINST %ebx
#define rINSTw %bx
diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S
index d85ef7fe24..2f69226206 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -58,7 +58,7 @@ SYMBOL(ExecuteMterpImpl):
leaq (rFP, %rax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
leaq (IN_ARG1, %rax, 2), rPC
- .cfi_register DPC_PSEUDO_REG, rPC
+ CFI_DEFINE_DEX_PC_WITH_OFFSET(CFI_TMP, CFI_DEX, 0)
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index a3ef8953ca..eabaade4e7 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -157,6 +157,8 @@ unspecified registers or condition codes.
/* single-purpose registers, given names for clarity */
#define rSELF SELF_SPILL(%rsp)
#define rPC %r12
+#define CFI_DEX 12 // DWARF register number of the register holding dex-pc (rPC).
+#define CFI_TMP 5 // DWARF register number of the first argument register (rdi).
#define rFP %r13
#define rINST %ebx
#define rINSTq %rbx
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 85acc71377..f8dd8293ca 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -235,6 +235,20 @@ void UnstartedRuntime::UnstartedClassForNameLong(
UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.forName");
}
+void UnstartedRuntime::UnstartedClassGetPrimitiveClass(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ ObjPtr<mirror::String> class_name = GetClassName(self, shadow_frame, arg_offset);
+ ObjPtr<mirror::Class> klass = mirror::Class::GetPrimitiveClass(class_name);
+ if (UNLIKELY(klass == nullptr)) {
+ DCHECK(self->IsExceptionPending());
+ AbortTransactionOrFail(self,
+ "Class.getPrimitiveClass() failed: %s",
+ self->GetException()->GetDetailMessage()->ToModifiedUtf8().c_str());
+ return;
+ }
+ result->SetL(klass);
+}
+
void UnstartedRuntime::UnstartedClassClassForName(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.classForName");
@@ -738,12 +752,6 @@ void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
}
}
-void UnstartedRuntime::UnstartedVoidLookupType(
- Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, JValue* result,
- size_t arg_offset ATTRIBUTE_UNUSED) {
- result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V'));
-}
-
// Arraycopy emulation.
// Note: we can't use any fast copy functions, as they are not available under transaction.
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index c029e07432..3cc598aed7 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -23,6 +23,7 @@
V(CharacterToUpperCase, "int java.lang.Character.toUpperCase(int)") \
V(ClassForName, "java.lang.Class java.lang.Class.forName(java.lang.String)") \
V(ClassForNameLong, "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") \
+ V(ClassGetPrimitiveClass, "java.lang.Class java.lang.Class.getPrimitiveClass(java.lang.String)") \
V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \
V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \
V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \
@@ -36,7 +37,6 @@
V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \
V(ConstructorNewInstance0, "java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[])") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
- V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
V(SystemArraycopyByte, "void java.lang.System.arraycopy(byte[], int, byte[], int, int)") \
V(SystemArraycopyChar, "void java.lang.System.arraycopy(char[], int, char[], int, int)") \
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 8c81c2565d..ac20afecd4 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -225,7 +225,7 @@ class JavaVMExt : public JavaVM {
// Extra checking.
bool check_jni_;
- bool force_copy_;
+ const bool force_copy_;
const bool tracing_enabled_;
// Extra diagnostics.
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index d68430f3ac..481aff91f8 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -38,8 +38,7 @@
* domain stream socket (@jdwp-control) that is opened by the
* ADB daemon.
*
- * 2/ it then sends the current process PID as a string of 4 hexadecimal
- * chars (no terminating zero)
+ * 2/ it then sends the current process PID as an int32_t.
*
* 3/ then, it uses recvmsg to receive file descriptors from the
* daemon. each incoming file descriptor is a pass-through to
@@ -225,7 +224,6 @@ bool JdwpAdbState::Accept() {
if (ControlSock() == -1) {
int sleep_ms = 500;
const int sleep_max_ms = 2*1000;
- char buff[5];
int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (sock < 0) {
@@ -247,8 +245,7 @@ bool JdwpAdbState::Accept() {
}
}
- snprintf(buff, sizeof(buff), "%04x", getpid());
- buff[4] = 0;
+ int32_t pid = getpid();
for (;;) {
/*
@@ -277,9 +274,9 @@ bool JdwpAdbState::Accept() {
#endif
/* now try to send our pid to the ADB daemon */
- ret = TEMP_FAILURE_RETRY(send(control_sock, buff, 4, 0));
- if (ret == 4) {
- VLOG(jdwp) << StringPrintf("PID sent as '%.*s' to ADB", 4, buff);
+ ret = TEMP_FAILURE_RETRY(send(control_sock, &pid, sizeof(pid), 0));
+ if (ret == sizeof(pid)) {
+ VLOG(jdwp) << "PID " << pid << " sent to ADB";
break;
}
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index de4d02edaf..dcb4a20b5f 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -168,9 +168,10 @@ bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
return data->AddMethod(flags, method_idx);
}
-bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
+bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods,
+ MethodHotness::Flag flags) {
for (const ProfileMethodInfo& method : methods) {
- if (!AddMethod(method)) {
+ if (!AddMethod(method, flags)) {
return false;
}
}
@@ -644,15 +645,26 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
uint32_t dex_checksum,
uint16_t method_index,
uint32_t num_method_ids,
- const OfflineProfileMethodInfo& pmi) {
+ const OfflineProfileMethodInfo& pmi,
+ MethodHotness::Flag flags) {
DexFileData* const data = GetOrAddDexFileData(GetProfileDexFileKey(dex_location),
dex_checksum,
num_method_ids);
- if (data == nullptr) { // checksum mismatch
+ if (data == nullptr) {
+ // The data is null if there is a mismatch in the checksum or number of method ids.
return false;
}
+
// Add the method.
InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
+ if (inline_cache == nullptr) {
+ // Happens if the method index is outside the range (i.e. is greater then the number
+ // of methods in the dex file). This should not happen during normal execution,
+ // But tools (e.g. boot image aggregation tools) and tests stress this behaviour.
+ return false;
+ }
+
+ data->SetMethodHotness(method_index, flags);
if (pmi.inline_caches == nullptr) {
// If we don't have inline caches return success right away.
@@ -691,12 +703,16 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
return true;
}
-bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
+bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi, MethodHotness::Flag flags) {
DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file);
if (data == nullptr) { // checksum mismatch
return false;
}
InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.ref.index);
+ if (inline_cache == nullptr) {
+ return false;
+ }
+ data->SetMethodHotness(pmi.ref.index, flags);
for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
if (cache.is_missing_types) {
@@ -811,6 +827,9 @@ bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
uint16_t method_index = last_method_index + diff_with_last_method_index;
last_method_index = method_index;
InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
+ if (inline_cache == nullptr) {
+ return false;
+ }
if (!ReadInlineCache(buffer,
number_of_dex_files,
dex_profile_index_remap,
@@ -1521,6 +1540,9 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
for (const auto& other_method_it : other_dex_data->method_map) {
uint16_t other_method_index = other_method_it.first;
InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
+ if (inline_cache == nullptr) {
+ return false;
+ }
const auto& other_inline_cache = other_method_it.second;
for (const auto& other_ic_it : other_inline_cache) {
uint16_t other_dex_pc = other_ic_it.first;
@@ -1955,6 +1977,10 @@ bool ProfileCompilationInfo::IsEmpty() const {
ProfileCompilationInfo::InlineCacheMap*
ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
+ if (method_index >= num_method_ids) {
+ LOG(ERROR) << "Invalid method index " << method_index << ". num_method_ids=" << num_method_ids;
+ return nullptr;
+ }
return &(method_map.FindOrAdd(
method_index,
InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)))->second);
@@ -1967,12 +1993,8 @@ bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, s
return false;
}
- if ((flags & MethodHotness::kFlagStartup) != 0) {
- method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
- }
- if ((flags & MethodHotness::kFlagPostStartup) != 0) {
- method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
- }
+ SetMethodHotness(index, flags);
+
if ((flags & MethodHotness::kFlagHot) != 0) {
method_map.FindOrAdd(
index,
@@ -1981,6 +2003,17 @@ bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, s
return true;
}
+void ProfileCompilationInfo::DexFileData::SetMethodHotness(size_t index,
+ MethodHotness::Flag flags) {
+ DCHECK_LT(index, num_method_ids);
+ if ((flags & MethodHotness::kFlagStartup) != 0) {
+ method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
+ }
+ if ((flags & MethodHotness::kFlagPostStartup) != 0) {
+ method_bitmap.StoreBit(MethodBitIndex(/*startup*/ false, index), /*value*/ true);
+ }
+}
+
ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
uint32_t dex_method_index) const {
MethodHotness ret;
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 1973f3f09e..5488a9e81e 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -241,7 +241,7 @@ class ProfileCompilationInfo {
~ProfileCompilationInfo();
// Add the given methods to the current profile object.
- bool AddMethods(const std::vector<ProfileMethodInfo>& methods);
+ bool AddMethods(const std::vector<ProfileMethodInfo>& methods, MethodHotness::Flag flags);
// Add the given classes to the current profile object.
bool AddClasses(const std::set<DexCacheResolvedClasses>& resolved_classes);
@@ -278,7 +278,7 @@ class ProfileCompilationInfo {
bool AddMethodIndex(MethodHotness::Flag flags, const MethodReference& ref);
// Add a method to the profile using its online representation (containing runtime structures).
- bool AddMethod(const ProfileMethodInfo& pmi);
+ bool AddMethod(const ProfileMethodInfo& pmi, MethodHotness::Flag flags);
// Bulk add sampled methods and/or hot methods for a single dex, fast since it only has one
// GetOrAddDexFileData call.
@@ -500,6 +500,7 @@ class ProfileCompilationInfo {
}
}
+ void SetMethodHotness(size_t index, MethodHotness::Flag flags);
MethodHotness GetHotnessInfo(uint32_t dex_method_index) const;
// The allocator used to allocate new inline cache maps.
@@ -559,7 +560,8 @@ class ProfileCompilationInfo {
uint32_t dex_checksum,
uint16_t method_index,
uint32_t num_method_ids,
- const OfflineProfileMethodInfo& pmi);
+ const OfflineProfileMethodInfo& pmi,
+ MethodHotness::Flag flags);
// Add a class index to the profile.
bool AddClassIndex(const std::string& dex_location,
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 4ac11ee422..e6917956ae 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -80,7 +80,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
uint16_t method_index,
const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
ProfileCompilationInfo* info) {
- return info->AddMethod(dex_location, checksum, method_index, kMaxMethodIds, pmi);
+ return info->AddMethod(
+ dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup);
}
bool AddClass(const std::string& dex_location,
@@ -99,7 +100,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
bool SaveProfilingInfo(
const std::string& filename,
const std::vector<ArtMethod*>& methods,
- const std::set<DexCacheResolvedClasses>& resolved_classes) {
+ const std::set<DexCacheResolvedClasses>& resolved_classes,
+ Hotness::Flag flags) {
ProfileCompilationInfo info;
std::vector<ProfileMethodInfo> profile_methods;
ScopedObjectAccess soa(Thread::Current());
@@ -107,7 +109,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
profile_methods.emplace_back(
MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
}
- if (!info.AddMethods(profile_methods) || !info.AddClasses(resolved_classes)) {
+ if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) {
return false;
}
if (info.GetNumberOfMethods() != profile_methods.size()) {
@@ -130,6 +132,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
bool SaveProfilingInfoWithFakeInlineCaches(
const std::string& filename,
const std::vector<ArtMethod*>& methods,
+ Hotness::Flag flags,
/*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
ProfileCompilationInfo info;
std::vector<ProfileMethodInfo> profile_methods;
@@ -170,7 +173,8 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
profile_methods_map->Put(method, pmi);
}
- if (!info.AddMethods(profile_methods) || info.GetNumberOfMethods() != profile_methods.size()) {
+ if (!info.AddMethods(profile_methods, flags)
+ || info.GetNumberOfMethods() != profile_methods.size()) {
return false;
}
return info.Save(filename, nullptr);
@@ -345,7 +349,8 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
// Save virtual methods from Main.
std::set<DexCacheResolvedClasses> resolved_classes;
std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
- ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes));
+ ASSERT_TRUE(SaveProfilingInfo(
+ profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup));
// Check that what we saved is in the profile.
ProfileCompilationInfo info1;
@@ -354,14 +359,16 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
{
ScopedObjectAccess soa(self);
for (ArtMethod* m : main_methods) {
- ASSERT_TRUE(info1.GetMethodHotness(
- MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot());
+ Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+ ASSERT_TRUE(h.IsHot());
+ ASSERT_TRUE(h.IsPostStartup());
}
}
// Save virtual methods from Second.
std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
- ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes));
+ ASSERT_TRUE(SaveProfilingInfo(
+ profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup));
// Check that what we saved is in the profile (methods form Main and Second).
ProfileCompilationInfo info2;
@@ -371,12 +378,14 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
{
ScopedObjectAccess soa(self);
for (ArtMethod* m : main_methods) {
- ASSERT_TRUE(
- info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot());
+ Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+ ASSERT_TRUE(h.IsHot());
+ ASSERT_TRUE(h.IsPostStartup());
}
for (ArtMethod* m : second_methods) {
- ASSERT_TRUE(
- info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot());
+ Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+ ASSERT_TRUE(h.IsHot());
+ ASSERT_TRUE(h.IsStartup());
}
}
}
@@ -730,7 +739,7 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
- profile.GetFilename(), main_methods, &profile_methods_map));
+ profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map));
// Check that what we saved is in the profile.
ProfileCompilationInfo info;
@@ -739,8 +748,9 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
{
ScopedObjectAccess soa(self);
for (ArtMethod* m : main_methods) {
- ASSERT_TRUE(
- info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())).IsHot());
+ Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+ ASSERT_TRUE(h.IsHot());
+ ASSERT_TRUE(h.IsStartup());
const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
info.GetMethod(m->GetDexFile()->GetLocation(),
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 8f0ac33594..53f48644f2 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -511,7 +511,7 @@ bool ProfileSaver::ProcessProfilingInfo(bool force_save, /*out*/uint16_t* number
uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
- info.AddMethods(profile_methods);
+ info.AddMethods(profile_methods, ProfileCompilationInfo::MethodHotness::kFlagPostStartup);
auto profile_cache_it = profile_cache_.find(filename);
if (profile_cache_it != profile_cache_.end()) {
info.MergeWith(*(profile_cache_it->second));
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 0e8fd03057..291ac48e86 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -96,6 +96,15 @@ class JNIEnvExt : public JNIEnv {
}
Thread* GetSelf() const { return self_; }
+ uint32_t GetCritical() const { return critical_; }
+ void SetCritical(uint32_t new_critical) { critical_ = new_critical; }
+ uint64_t GetCriticalStartUs() const { return critical_start_us_; }
+ void SetCriticalStartUs(uint64_t new_critical_start_us) {
+ critical_start_us_ = new_critical_start_us;
+ }
+ const JNINativeInterface* GetUncheckedFunctions() const {
+ return unchecked_functions_;
+ }
JavaVMExt* GetVm() const { return vm_; }
bool IsRuntimeDeleted() const { return runtime_deleted_; }
@@ -190,9 +199,7 @@ class JNIEnvExt : public JNIEnv {
// If we are a JNI env for a daemon thread with a deleted runtime.
bool runtime_deleted_;
- friend class CheckJNI;
friend class JNI;
- friend class ScopedCheck;
friend class ScopedJniEnvLocalRefState;
friend class Thread;
ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 666fb98354..cd4d9543be 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -90,7 +90,8 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath);
+ return hiddenapi::ShouldBlockAccessToMember(
+ member, self, IsCallerInBootClassPath, hiddenapi::kJNI);
}
// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index fac1a7597d..e89beb6d41 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -34,7 +34,9 @@ class Monitor;
/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of
* the state. The four possible states are fat locked, thin/unlocked, hash code, and forwarding
- * address. When the lock word is in the "thin" state and its bits are formatted as follows:
+ * address.
+ *
+ * When the lock word is in the "thin" state and its bits are formatted as follows:
*
* |33|2|2|222222221111|1111110000000000|
* |10|9|8|765432109876|5432109876543210|
@@ -59,7 +61,7 @@ class Monitor;
* |11|0| ForwardingAddress |
*
* The `r` bit stores the read barrier state.
- * The `m` bit stores the mark state.
+ * The `m` bit stores the mark bit state.
*/
class LockWord {
public:
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 2701ec66a4..9b21e1d9bf 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -346,7 +346,7 @@ inline bool ConvertAndCopyArgumentsFromCallerFrame(
return false;
}
- ShadowFrameGetter getter(operands, caller_frame);
+ ShadowFrameGetter getter(caller_frame, operands);
ShadowFrameSetter setter(callee_frame, first_dest_reg);
return PerformConversions<ShadowFrameGetter, ShadowFrameSetter>(self,
callsite_type,
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 6ffd1a81fc..3b1bf2ee66 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -130,8 +130,10 @@ bool PerformConversions(Thread* self,
// arguments while performing standard argument conversions.
class ShadowFrameGetter {
public:
- ShadowFrameGetter(const InstructionOperands* const operands, const ShadowFrame& shadow_frame)
- : operands_(operands), operand_index_(0), shadow_frame_(shadow_frame) {}
+ ShadowFrameGetter(const ShadowFrame& shadow_frame,
+ const InstructionOperands* const operands,
+ size_t operand_index = 0u)
+ : shadow_frame_(shadow_frame), operands_(operands), operand_index_(operand_index) {}
ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) {
return shadow_frame_.GetVReg(Next());
@@ -151,26 +153,24 @@ class ShadowFrameGetter {
operand_index_ += 1;
return next;
}
+
uint32_t NextLong() {
const uint32_t next = operands_->GetOperand(operand_index_);
operand_index_ += 2;
return next;
}
- const InstructionOperands* const operands_;
- size_t operand_index_; // the next register operand to read from frame
const ShadowFrame& shadow_frame_;
+ const InstructionOperands* const operands_; // the set of register operands to read
+ size_t operand_index_; // the next register operand to read from frame
};
// A convenience class that allows values to be written to a given shadow frame,
// starting at location |first_dst_reg|.
class ShadowFrameSetter {
public:
- ShadowFrameSetter(ShadowFrame* shadow_frame,
- size_t first_dst_reg) :
- shadow_frame_(shadow_frame),
- arg_index_(first_dst_reg) {
- }
+ ShadowFrameSetter(ShadowFrame* shadow_frame, size_t first_dst_reg)
+ : shadow_frame_(shadow_frame), arg_index_(first_dst_reg) {}
ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
shadow_frame_->SetVReg(arg_index_++, value);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 36388eb3aa..86d538ec80 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -550,7 +550,7 @@ inline bool Class::IsSubClass(ObjPtr<Class> klass) {
current = current->GetSuperClass();
} while (current != nullptr);
- if (kIsDebugBuild) {
+ if (kIsDebugBuild && kBitstringSubtypeCheckEnabled) {
ObjPtr<mirror::Class> dis(this);
SubtypeCheckInfo::Result sc_result = SubtypeCheck<ObjPtr<Class>>::IsSubtypeOf(dis, klass);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 8a7defd362..5d730ce0b0 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -72,6 +72,42 @@ void Class::VisitRoots(RootVisitor* visitor) {
java_lang_Class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
+ObjPtr<mirror::Class> Class::GetPrimitiveClass(ObjPtr<mirror::String> name) {
+ const char* expected_name = nullptr;
+ ClassLinker::ClassRoot class_root = ClassLinker::kJavaLangObject; // Invalid.
+ if (name != nullptr && name->GetLength() >= 2) {
+ // Perfect hash for the expected values: from the second letters of the primitive types,
+ // only 'y' has the bit 0x10 set, so use it to change 'b' to 'B'.
+ char hash = name->CharAt(0) ^ ((name->CharAt(1) & 0x10) << 1);
+ switch (hash) {
+ case 'b': expected_name = "boolean"; class_root = ClassLinker::kPrimitiveBoolean; break;
+ case 'B': expected_name = "byte"; class_root = ClassLinker::kPrimitiveByte; break;
+ case 'c': expected_name = "char"; class_root = ClassLinker::kPrimitiveChar; break;
+ case 'd': expected_name = "double"; class_root = ClassLinker::kPrimitiveDouble; break;
+ case 'f': expected_name = "float"; class_root = ClassLinker::kPrimitiveFloat; break;
+ case 'i': expected_name = "int"; class_root = ClassLinker::kPrimitiveInt; break;
+ case 'l': expected_name = "long"; class_root = ClassLinker::kPrimitiveLong; break;
+ case 's': expected_name = "short"; class_root = ClassLinker::kPrimitiveShort; break;
+ case 'v': expected_name = "void"; class_root = ClassLinker::kPrimitiveVoid; break;
+ default: break;
+ }
+ }
+ if (expected_name != nullptr && name->Equals(expected_name)) {
+ ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->GetClassRoot(class_root);
+ DCHECK(klass != nullptr);
+ return klass;
+ } else {
+ Thread* self = Thread::Current();
+ if (name == nullptr) {
+ // Note: ThrowNullPointerException() requires a message which we deliberately want to omit.
+ self->ThrowNewException("Ljava/lang/NullPointerException;", /* msg */ nullptr);
+ } else {
+ self->ThrowNewException("Ljava/lang/ClassNotFoundException;", name->ToModifiedUtf8().c_str());
+ }
+ return nullptr;
+ }
+}
+
ClassExt* Class::EnsureExtDataPresent(Thread* self) {
ObjPtr<ClassExt> existing(GetExtData());
if (!existing.IsNull()) {
@@ -156,9 +192,19 @@ void Class::SetStatus(Handle<Class> h_this, ClassStatus new_status, Thread* self
self->AssertPendingException();
}
- {
+ if (kBitstringSubtypeCheckEnabled) {
+ // FIXME: This looks broken with respect to aborted transactions.
ObjPtr<mirror::Class> h_this_ptr = h_this.Get();
SubtypeCheck<ObjPtr<mirror::Class>>::WriteStatus(h_this_ptr, new_status);
+ } else {
+ // The ClassStatus is always in the 4 most-significant bits of status_.
+ static_assert(sizeof(status_) == sizeof(uint32_t), "Size of status_ not equal to uint32");
+ uint32_t new_status_value = static_cast<uint32_t>(new_status) << (32 - kClassStatusBitSize);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ h_this->SetField32Volatile<true>(StatusOffset(), new_status_value);
+ } else {
+ h_this->SetField32Volatile<false>(StatusOffset(), new_status_value);
+ }
}
// Setting the object size alloc fast path needs to be after the status write so that if the
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index ced7c7c908..b9a31e54b7 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -81,7 +81,7 @@ class MANAGED Class FINAL : public Object {
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ClassStatus GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid including "subtype_check_bits_and_status.h" to get the field.
- // The ClassStatus is always in the 4 most-significant of status_.
+ // The ClassStatus is always in the 4 most-significant bits of status_.
return enum_cast<ClassStatus>(
static_cast<uint32_t>(GetField32Volatile<kVerifyFlags>(StatusOffset())) >> (32 - 4));
}
@@ -1134,6 +1134,10 @@ class MANAGED Class FINAL : public Object {
void VisitNativeRoots(Visitor& visitor, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Get one of the primitive classes.
+ static ObjPtr<mirror::Class> GetPrimitiveClass(ObjPtr<mirror::String> name)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// When class is verified, set the kAccSkipAccessChecks flag on each method.
void SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index 5757992167..5f00c6e9c7 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -183,7 +183,7 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs(
}
// Step 4 : Perform argument conversions (if required).
- ShadowFrameGetter getter(operands, caller_frame);
+ ShadowFrameGetter getter(caller_frame, operands);
EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength());
if (!PerformConversions<ShadowFrameGetter, EmulatedStackFrameAccessor>(
self, caller_type, callee_type, &getter, &setter, num_method_params)) {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 6e2a07c9e0..7fdaa32751 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -160,7 +160,8 @@ inline bool Object::VerifierInstanceOf(ObjPtr<Class> klass) {
template<VerifyObjectFlags kVerifyFlags>
inline bool Object::InstanceOf(ObjPtr<Class> klass) {
DCHECK(klass != nullptr);
- DCHECK(GetClass<kVerifyNone>() != nullptr);
+ DCHECK(GetClass<kVerifyNone>() != nullptr)
+ << "this=" << std::hex << reinterpret_cast<uintptr_t>(this) << std::dec;
return klass->IsAssignableFrom(GetClass<kVerifyFlags>());
}
@@ -935,6 +936,195 @@ inline bool Object::CasFieldWeakReleaseObjectWithoutWriteBarrier(
return success;
}
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline ObjPtr<Object> Object::CompareAndExchangeFieldObject(MemberOffset field_offset,
+ ObjPtr<Object> old_value,
+ ObjPtr<Object> new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(old_value);
+ }
+ uint32_t old_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(old_value));
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
+ bool success = atomic_addr->CompareAndExchangeStrongSequentiallyConsistent(&old_ref, new_ref);
+ ObjPtr<Object> witness_value(PtrCompression<kPoisonHeapReferences, Object>::Decompress(old_ref));
+ if (kIsDebugBuild) {
+ // Ensure caller has done read barrier on the reference field so it's in the to-space.
+ ReadBarrier::AssertToSpaceInvariant(witness_value.Ptr());
+ }
+ if (kTransactionActive && success) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, witness_value, true);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(witness_value);
+ }
+ return witness_value;
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline ObjPtr<Object> Object::ExchangeFieldObject(MemberOffset field_offset,
+ ObjPtr<Object> new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
+ uint32_t new_ref(PtrCompression<kPoisonHeapReferences, Object>::Compress(new_value));
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
+ uint32_t old_ref = atomic_addr->ExchangeSequentiallyConsistent(new_ref);
+ ObjPtr<Object> old_value(PtrCompression<kPoisonHeapReferences, Object>::Decompress(old_ref));
+ if (kIsDebugBuild) {
+ // Ensure caller has done read barrier on the reference field so it's in the to-space.
+ ReadBarrier::AssertToSpaceInvariant(old_value.Ptr());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(old_value);
+ }
+ return old_value;
+}
+
+template<typename T, VerifyObjectFlags kVerifyFlags>
+inline void Object::GetPrimitiveFieldViaAccessor(MemberOffset field_offset, Accessor<T>* accessor) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ T* addr = reinterpret_cast<T*>(raw_addr);
+ accessor->Access(addr);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::UpdateFieldBooleanViaAccessor(MemberOffset field_offset,
+ Accessor<uint8_t>* accessor) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ static const bool kIsVolatile = true;
+ uint8_t old_value = GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset);
+ Runtime::Current()->RecordWriteFieldBoolean(this, field_offset, old_value, kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ uint8_t* addr = raw_addr;
+ accessor->Access(addr);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::UpdateFieldByteViaAccessor(MemberOffset field_offset,
+ Accessor<int8_t>* accessor) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ static const bool kIsVolatile = true;
+ int8_t old_value = GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset);
+ Runtime::Current()->RecordWriteFieldByte(this, field_offset, old_value, kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ int8_t* addr = reinterpret_cast<int8_t*>(raw_addr);
+ accessor->Access(addr);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::UpdateFieldCharViaAccessor(MemberOffset field_offset,
+ Accessor<uint16_t>* accessor) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ static const bool kIsVolatile = true;
+ uint16_t old_value = GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset);
+ Runtime::Current()->RecordWriteFieldChar(this, field_offset, old_value, kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ uint16_t* addr = reinterpret_cast<uint16_t*>(raw_addr);
+ accessor->Access(addr);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::UpdateFieldShortViaAccessor(MemberOffset field_offset,
+ Accessor<int16_t>* accessor) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ static const bool kIsVolatile = true;
+ int16_t old_value = GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset);
+ Runtime::Current()->RecordWriteFieldShort(this, field_offset, old_value, kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ int16_t* addr = reinterpret_cast<int16_t*>(raw_addr);
+ accessor->Access(addr);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::UpdateField32ViaAccessor(MemberOffset field_offset,
+ Accessor<int32_t>* accessor) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ static const bool kIsVolatile = true;
+ int32_t old_value = GetField32<kVerifyFlags, kIsVolatile>(field_offset);
+ Runtime::Current()->RecordWriteField32(this, field_offset, old_value, kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ int32_t* addr = reinterpret_cast<int32_t*>(raw_addr);
+ accessor->Access(addr);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::UpdateField64ViaAccessor(MemberOffset field_offset,
+ Accessor<int64_t>* accessor) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ static const bool kIsVolatile = true;
+ int64_t old_value = GetField64<kVerifyFlags, kIsVolatile>(field_offset);
+ Runtime::Current()->RecordWriteField64(this, field_offset, old_value, kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ int64_t* addr = reinterpret_cast<int64_t*>(raw_addr);
+ accessor->Access(addr);
+}
+
template<bool kIsStatic,
VerifyObjectFlags kVerifyFlags,
ReadBarrierOption kReadBarrierOption,
diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h
index d81fff0a22..126cb04cf1 100644
--- a/runtime/mirror/object-readbarrier-inl.h
+++ b/runtime/mirror/object-readbarrier-inl.h
@@ -187,7 +187,7 @@ inline bool Object::AtomicSetMarkBit(uint32_t expected_mark_bit, uint32_t mark_b
expected_lw = lw;
new_lw = lw;
new_lw.SetMarkBitState(mark_bit);
- // Since this is only set from the mutator, we can use the non release Cas.
+ // Since this is only set from the mutator, we can use the non-release CAS.
} while (!CasLockWordWeakRelaxed(expected_lw, new_lw));
return true;
}
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 25b86a527d..816ac69b29 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -347,6 +347,21 @@ class MANAGED LOCKABLE Object {
ObjPtr<Object> old_value,
ObjPtr<Object> new_value)
REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive,
+ bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ObjPtr<Object> CompareAndExchangeFieldObject(MemberOffset field_offset,
+ ObjPtr<Object> old_value,
+ ObjPtr<Object> new_value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive,
+ bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ObjPtr<Object> ExchangeFieldObject(MemberOffset field_offset, ObjPtr<Object> new_value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
template<bool kTransactionActive,
bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -592,6 +607,52 @@ class MANAGED LOCKABLE Object {
field_offset, reinterpret_cast64<int64_t>(new_value));
}
}
+
+ // Base class for accessors used to describe accesses performed by VarHandle methods.
+ template <typename T>
+ class Accessor {
+ public:
+ virtual ~Accessor() {
+ static_assert(std::is_arithmetic<T>::value, "unsupported type");
+ }
+ virtual void Access(T* field_address) = 0;
+ };
+
+ // Getter method that exposes the raw address of a primitive value-type field to an Accessor
+ // instance. This are used by VarHandle accessor methods to read fields with a wider range of
+ // memory orderings than usually required.
+ template<typename T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void GetPrimitiveFieldViaAccessor(MemberOffset field_offset, Accessor<T>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Update methods that expose the raw address of a primitive value-type to an Accessor instance
+ // that will attempt to update the field. These are used by VarHandle accessor methods to
+ // atomically update fields with a wider range of memory orderings than usually required.
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void UpdateFieldBooleanViaAccessor(MemberOffset field_offset, Accessor<uint8_t>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void UpdateFieldByteViaAccessor(MemberOffset field_offset, Accessor<int8_t>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void UpdateFieldCharViaAccessor(MemberOffset field_offset, Accessor<uint16_t>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void UpdateFieldShortViaAccessor(MemberOffset field_offset, Accessor<int16_t>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void UpdateField32ViaAccessor(MemberOffset field_offset, Accessor<int32_t>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void UpdateField64ViaAccessor(MemberOffset field_offset, Accessor<int64_t>* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// TODO fix thread safety analysis broken by the use of template. This should be
// REQUIRES_SHARED(Locks::mutator_lock_).
template <bool kVisitNativeRoots = true,
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index 3f4a28ce9d..85d06f03fe 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -16,14 +16,23 @@
#include "var_handle.h"
+#include "array-inl.h"
+#include "art_field-inl.h"
#include "class-inl.h"
#include "class_linker.h"
#include "gc_root-inl.h"
+#include "jni_internal.h"
+#include "jvalue-inl.h"
+#include "method_handles.h"
#include "method_type.h"
+#include "well_known_classes.h"
namespace art {
namespace mirror {
+static constexpr bool kTransactionActive = true;
+static constexpr bool kTransactionInactive = !kTransactionActive;
+
namespace {
struct VarHandleAccessorToAccessModeEntry {
@@ -158,32 +167,64 @@ AccessModeTemplate GetAccessModeTemplate(VarHandle::AccessMode access_mode) {
}
}
-// Returns the number of parameters associated with an
-// AccessModeTemplate and the supplied coordinate types.
-int32_t GetParameterCount(AccessModeTemplate access_mode_template,
- ObjPtr<Class> coordinateType0,
- ObjPtr<Class> coordinateType1) {
- int32_t index = 0;
- if (!coordinateType0.IsNull()) {
- index++;
- if (!coordinateType1.IsNull()) {
- index++;
- }
- }
-
+int32_t GetNumberOfVarTypeParameters(AccessModeTemplate access_mode_template) {
switch (access_mode_template) {
case AccessModeTemplate::kGet:
- return index;
+ return 0;
case AccessModeTemplate::kSet:
case AccessModeTemplate::kGetAndUpdate:
- return index + 1;
+ return 1;
case AccessModeTemplate::kCompareAndSet:
case AccessModeTemplate::kCompareAndExchange:
- return index + 2;
+ return 2;
}
UNREACHABLE();
}
+// Returns the number of parameters associated with an
+// AccessModeTemplate and the supplied coordinate types.
+int32_t GetNumberOfParameters(AccessModeTemplate access_mode_template,
+ ObjPtr<Class> coordinateType0,
+ ObjPtr<Class> coordinateType1) {
+ int32_t count = 0;
+ if (!coordinateType0.IsNull()) {
+ count++;
+ if (!coordinateType1.IsNull()) {
+ count++;
+ }
+ }
+ return count + GetNumberOfVarTypeParameters(access_mode_template);
+}
+
+void ThrowNullPointerExceptionForCoordinate() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ThrowNullPointerException("Attempt to access memory on a null object");
+}
+
+bool CheckElementIndex(Primitive::Type type,
+ int32_t relative_index,
+ int32_t start,
+ int32_t limit) REQUIRES_SHARED(Locks::mutator_lock_) {
+ int64_t index = start + relative_index;
+ int64_t max_index = limit - Primitive::ComponentSize(type);
+ if (index < start || index > max_index) {
+ ThrowIndexOutOfBoundsException(index, limit - start);
+ return false;
+ }
+ return true;
+}
+
+bool CheckElementIndex(Primitive::Type type, int32_t index, int32_t range_limit)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return CheckElementIndex(type, index, 0, range_limit);
+}
+
+// Returns true if access_mode only entails a memory read. False if
+// access_mode may write to memory.
+bool IsReadOnlyAccessMode(VarHandle::AccessMode access_mode) {
+ AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
+ return access_mode_template == AccessModeTemplate::kGet;
+}
+
// Writes the parameter types associated with the AccessModeTemplate
// into an array. The parameter types are derived from the specified
// variable type and coordinate types. Returns the number of
@@ -248,6 +289,1123 @@ ObjectArray<Class>* NewArrayOfClasses(Thread* self, int count)
return ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, count);
}
+// Method to insert a read barrier for accessors to reference fields.
+inline void ReadBarrierForVarHandleAccess(ObjPtr<Object> obj, MemberOffset field_offset)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kUseReadBarrier) {
+ // We need to ensure that the reference stored in the field is a to-space one before attempting
+ // the CompareAndSet/CompareAndExchange/Exchange operation otherwise it will fail incorrectly
+ // if obj is in the process of being moved.
+ uint8_t* raw_field_addr = reinterpret_cast<uint8_t*>(obj.Ptr()) + field_offset.SizeValue();
+ auto field_addr = reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_field_addr);
+ // Note that the read barrier load does NOT need to be volatile.
+ static constexpr bool kIsVolatile = false;
+ static constexpr bool kAlwaysUpdateField = true;
+ ReadBarrier::Barrier<mirror::Object, kIsVolatile, kWithReadBarrier, kAlwaysUpdateField>(
+ obj.Ptr(),
+ MemberOffset(field_offset),
+ field_addr);
+ }
+}
+
+inline MemberOffset GetMemberOffset(jfieldID field_id) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* const field = jni::DecodeArtField(field_id);
+ return field->GetOffset();
+}
+
+//
+// Helper methods for storing results from atomic operations into
+// JValue instances.
+//
+
+inline void StoreResult(uint8_t value, JValue* result) {
+ result->SetZ(value);
+}
+
+inline void StoreResult(int8_t value, JValue* result) {
+ result->SetB(value);
+}
+
+inline void StoreResult(uint16_t value, JValue* result) {
+ result->SetC(value);
+}
+
+inline void StoreResult(int16_t value, JValue* result) {
+ result->SetS(value);
+}
+
+inline void StoreResult(int32_t value, JValue* result) {
+ result->SetI(value);
+}
+
+inline void StoreResult(int64_t value, JValue* result) {
+ result->SetJ(value);
+}
+
+inline void StoreResult(float value, JValue* result) {
+ result->SetF(value);
+}
+
+inline void StoreResult(double value, JValue* result) {
+ result->SetD(value);
+}
+
+inline void StoreResult(ObjPtr<Object> value, JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ result->SetL(value);
+}
+
+//
+// Helper class for byte-swapping value that has been stored in a JValue.
+//
+
+template <typename T>
+class JValueByteSwapper FINAL {
+ public:
+ static void ByteSwap(JValue* value);
+ static void MaybeByteSwap(bool byte_swap, JValue* value) {
+ if (byte_swap) {
+ ByteSwap(value);
+ }
+ }
+};
+
+template <>
+void JValueByteSwapper<uint16_t>::ByteSwap(JValue* value) {
+ value->SetC(BSWAP(value->GetC()));
+}
+
+template <>
+void JValueByteSwapper<int16_t>::ByteSwap(JValue* value) {
+ value->SetS(BSWAP(value->GetS()));
+}
+
+template <>
+void JValueByteSwapper<int32_t>::ByteSwap(JValue* value) {
+ value->SetI(BSWAP(value->GetI()));
+}
+
+template <>
+void JValueByteSwapper<int64_t>::ByteSwap(JValue* value) {
+ value->SetJ(BSWAP(value->GetJ()));
+}
+
+//
+// Accessor implementations, shared across all VarHandle types.
+//
+
+template <typename T, std::memory_order MO>
+class AtomicGetAccessor : public Object::Accessor<T> {
+ public:
+ explicit AtomicGetAccessor(JValue* result) : result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ StoreResult(atom->load(MO), result_);
+ }
+
+ private:
+ JValue* result_;
+};
+
+template <typename T, std::memory_order MO>
+class AtomicSetAccessor : public Object::Accessor<T> {
+ public:
+ explicit AtomicSetAccessor(T new_value) : new_value_(new_value) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ atom->store(new_value_, MO);
+ }
+
+ private:
+ T new_value_;
+};
+
+template <typename T> using GetAccessor = AtomicGetAccessor<T, std::memory_order_relaxed>;
+
+template <typename T> using SetAccessor = AtomicSetAccessor<T, std::memory_order_relaxed>;
+
+template <typename T>
+using GetVolatileAccessor = AtomicGetAccessor<T, std::memory_order_seq_cst>;
+
+template <typename T>
+using SetVolatileAccessor = AtomicSetAccessor<T, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MOS, std::memory_order MOF>
+class AtomicStrongCompareAndSetAccessor : public Object::Accessor<T> {
+ public:
+ AtomicStrongCompareAndSetAccessor(T expected_value, T desired_value, JValue* result)
+ : expected_value_(expected_value), desired_value_(desired_value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ bool success = atom->compare_exchange_strong(expected_value_, desired_value_, MOS, MOF);
+ StoreResult(success ? JNI_TRUE : JNI_FALSE, result_);
+ }
+
+ private:
+ T expected_value_;
+ T desired_value_;
+ JValue* result_;
+};
+
+template<typename T>
+using CompareAndSetAccessor =
+ AtomicStrongCompareAndSetAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MOS, std::memory_order MOF>
+class AtomicStrongCompareAndExchangeAccessor : public Object::Accessor<T> {
+ public:
+ AtomicStrongCompareAndExchangeAccessor(T expected_value, T desired_value, JValue* result)
+ : expected_value_(expected_value), desired_value_(desired_value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ atom->compare_exchange_strong(expected_value_, desired_value_, MOS, MOF);
+ StoreResult(expected_value_, result_);
+ }
+
+ private:
+ T expected_value_;
+ T desired_value_;
+ JValue* result_;
+};
+
+template <typename T>
+using CompareAndExchangeAccessor =
+ AtomicStrongCompareAndExchangeAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MOS, std::memory_order MOF>
+class AtomicWeakCompareAndSetAccessor : public Object::Accessor<T> {
+ public:
+ AtomicWeakCompareAndSetAccessor(T expected_value, T desired_value, JValue* result)
+ : expected_value_(expected_value), desired_value_(desired_value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ bool success = atom->compare_exchange_weak(expected_value_, desired_value_, MOS, MOF);
+ StoreResult(success ? JNI_TRUE : JNI_FALSE, result_);
+ }
+
+ private:
+ T expected_value_;
+ T desired_value_;
+ JValue* result_;
+};
+
+template <typename T>
+using WeakCompareAndSetAccessor =
+ AtomicWeakCompareAndSetAccessor<T, std::memory_order_seq_cst, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MO>
+class AtomicGetAndSetAccessor : public Object::Accessor<T> {
+ public:
+ AtomicGetAndSetAccessor(T new_value, JValue* result) : new_value_(new_value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ T old_value = atom->exchange(new_value_, MO);
+ StoreResult(old_value, result_);
+ }
+
+ private:
+ T new_value_;
+ JValue* result_;
+};
+
+template <typename T>
+using GetAndSetAccessor = AtomicGetAndSetAccessor<T, std::memory_order_seq_cst>;
+
+template <typename T, bool kIsFloat, std::memory_order MO>
+class AtomicGetAndAddOperator {
+ public:
+ static T Apply(T* addr, T addend) {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ return atom->fetch_add(addend, MO);
+ }
+};
+
+template <typename T, std::memory_order MO>
+class AtomicGetAndAddOperator<T, /* kIsFloat */ true, MO> {
+ public:
+ static T Apply(T* addr, T addend) {
+ // c++11 does not have std::atomic<T>::fetch_and_add for floating
+ // point types, so we effect one with a compare and swap.
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ T old_value = atom->load(std::memory_order_relaxed);
+ T new_value;
+ do {
+ new_value = old_value + addend;
+ } while (!atom->compare_exchange_weak(old_value, new_value, MO, std::memory_order_relaxed));
+ return old_value;
+ }
+};
+
+template <typename T, std::memory_order MO>
+class AtomicGetAndAddAccessor : public Object::Accessor<T> {
+ public:
+ AtomicGetAndAddAccessor(T addend, JValue* result) : addend_(addend), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ constexpr bool kIsFloatingPoint = std::is_floating_point<T>::value;
+ T old_value = AtomicGetAndAddOperator<T, kIsFloatingPoint, MO>::Apply(addr, addend_);
+ StoreResult(old_value, result_);
+ }
+
+ private:
+ T addend_;
+ JValue* result_;
+};
+
+template <typename T>
+using GetAndAddAccessor = AtomicGetAndAddAccessor<T, std::memory_order_seq_cst>;
+
+// Accessor specifically for memory views where the caller can specify
+// the byte-ordering. Addition only works outside of the byte-swapped
+// memory view because of the direction of carries.
+template <typename T, std::memory_order MO>
+class AtomicGetAndAddWithByteSwapAccessor : public Object::Accessor<T> {
+ public:
+ AtomicGetAndAddWithByteSwapAccessor(T value, JValue* result) : value_(value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* const atom = reinterpret_cast<std::atomic<T>*>(addr);
+ T current_value = atom->load(std::memory_order_relaxed);
+ T sum;
+ do {
+ sum = BSWAP(current_value) + value_;
+ // NB current_value is a pass-by-reference argument in the call to
+ // atomic<T>::compare_exchange_weak().
+ } while (!atom->compare_exchange_weak(current_value,
+ BSWAP(sum),
+ MO,
+ std::memory_order_relaxed));
+ StoreResult(BSWAP(current_value), result_);
+ }
+
+ private:
+ T value_;
+ JValue* result_;
+};
+
+template <typename T>
+using GetAndAddWithByteSwapAccessor =
+ AtomicGetAndAddWithByteSwapAccessor<T, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MO>
+class AtomicGetAndBitwiseOrAccessor : public Object::Accessor<T> {
+ public:
+ AtomicGetAndBitwiseOrAccessor(T value, JValue* result) : value_(value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ T old_value = atom->fetch_or(value_, MO);
+ StoreResult(old_value, result_);
+ }
+
+ private:
+ T value_;
+ JValue* result_;
+};
+
+template <typename T>
+using GetAndBitwiseOrAccessor = AtomicGetAndBitwiseOrAccessor<T, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MO>
+class AtomicGetAndBitwiseAndAccessor : public Object::Accessor<T> {
+ public:
+ AtomicGetAndBitwiseAndAccessor(T value, JValue* result) : value_(value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ T old_value = atom->fetch_and(value_, MO);
+ StoreResult(old_value, result_);
+ }
+
+ private:
+ T value_;
+ JValue* result_;
+};
+
+template <typename T>
+using GetAndBitwiseAndAccessor =
+ AtomicGetAndBitwiseAndAccessor<T, std::memory_order_seq_cst>;
+
+template <typename T, std::memory_order MO>
+class AtomicGetAndBitwiseXorAccessor : public Object::Accessor<T> {
+ public:
+ AtomicGetAndBitwiseXorAccessor(T value, JValue* result) : value_(value), result_(result) {}
+
+ void Access(T* addr) OVERRIDE {
+ std::atomic<T>* atom = reinterpret_cast<std::atomic<T>*>(addr);
+ T old_value = atom->fetch_xor(value_, MO);
+ StoreResult(old_value, result_);
+ }
+
+ private:
+ T value_;
+ JValue* result_;
+};
+
+template <typename T>
+using GetAndBitwiseXorAccessor = AtomicGetAndBitwiseXorAccessor<T, std::memory_order_seq_cst>;
+
+//
+// Unreachable access modes.
+//
+
+NO_RETURN void UnreachableAccessMode(const char* access_mode, const char* type_name) {
+ LOG(FATAL) << "Unreachable access mode :" << access_mode << " for type " << type_name;
+ UNREACHABLE();
+}
+
+#define UNREACHABLE_ACCESS_MODE(ACCESS_MODE, TYPE) \
+template<> void ACCESS_MODE ## Accessor<TYPE>::Access(TYPE*) { \
+ UnreachableAccessMode(#ACCESS_MODE, #TYPE); \
+}
+
+// The boolean primitive type is not numeric (boolean == std::uint8_t).
+UNREACHABLE_ACCESS_MODE(GetAndAdd, uint8_t)
+
+// The floating point types do not support bitwise operations.
+UNREACHABLE_ACCESS_MODE(GetAndBitwiseOr, float)
+UNREACHABLE_ACCESS_MODE(GetAndBitwiseAnd, float)
+UNREACHABLE_ACCESS_MODE(GetAndBitwiseXor, float)
+UNREACHABLE_ACCESS_MODE(GetAndBitwiseOr, double)
+UNREACHABLE_ACCESS_MODE(GetAndBitwiseAnd, double)
+UNREACHABLE_ACCESS_MODE(GetAndBitwiseXor, double)
+
+// A helper class for object field accesses for floats and
+// doubles. The object interface deals with Field32 and Field64. The
+// former is used for both integers and floats, the latter for longs
+// and doubles. This class provides the necessary coercion.
+template <typename T, typename U>
+class TypeAdaptorAccessor : public Object::Accessor<T> {
+ public:
+ explicit TypeAdaptorAccessor(Object::Accessor<U>* inner_accessor)
+ : inner_accessor_(inner_accessor) {}
+
+ void Access(T* addr) OVERRIDE {
+ static_assert(sizeof(T) == sizeof(U), "bad conversion");
+ inner_accessor_->Access(reinterpret_cast<U*>(addr));
+ }
+
+ private:
+ Object::Accessor<U>* inner_accessor_;
+};
+
+template <typename T>
+class FieldAccessViaAccessor {
+ public:
+ typedef Object::Accessor<T> Accessor;
+
+ // Apply an Accessor to get a field in an object.
+ static void Get(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ obj->GetPrimitiveFieldViaAccessor(field_offset, accessor);
+ }
+
+ // Apply an Accessor to update a field in an object.
+ static void Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+template <>
+inline void FieldAccessViaAccessor<float>::Get(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ TypeAdaptorAccessor<int32_t, float> float_to_int_accessor(accessor);
+ obj->GetPrimitiveFieldViaAccessor(field_offset, &float_to_int_accessor);
+}
+
+template <>
+inline void FieldAccessViaAccessor<double>::Get(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ TypeAdaptorAccessor<int64_t, double> double_to_int_accessor(accessor);
+ obj->GetPrimitiveFieldViaAccessor(field_offset, &double_to_int_accessor);
+}
+
+template <>
+void FieldAccessViaAccessor<uint8_t>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateFieldBooleanViaAccessor<kTransactionActive>(field_offset, accessor);
+ } else {
+ obj->UpdateFieldBooleanViaAccessor<kTransactionInactive>(field_offset, accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<int8_t>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateFieldByteViaAccessor<kTransactionActive>(field_offset, accessor);
+ } else {
+ obj->UpdateFieldByteViaAccessor<kTransactionInactive>(field_offset, accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<uint16_t>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateFieldCharViaAccessor<kTransactionActive>(field_offset, accessor);
+ } else {
+ obj->UpdateFieldCharViaAccessor<kTransactionInactive>(field_offset, accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<int16_t>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateFieldShortViaAccessor<kTransactionActive>(field_offset, accessor);
+ } else {
+ obj->UpdateFieldShortViaAccessor<kTransactionInactive>(field_offset, accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<int32_t>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateField32ViaAccessor<kTransactionActive>(field_offset, accessor);
+ } else {
+ obj->UpdateField32ViaAccessor<kTransactionInactive>(field_offset, accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<int64_t>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateField64ViaAccessor<kTransactionActive>(field_offset, accessor);
+ } else {
+ obj->UpdateField64ViaAccessor<kTransactionInactive>(field_offset, accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<float>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ TypeAdaptorAccessor<int32_t, float> float_to_int_accessor(accessor);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateField32ViaAccessor<kTransactionActive>(field_offset, &float_to_int_accessor);
+ } else {
+ obj->UpdateField32ViaAccessor<kTransactionInactive>(field_offset, &float_to_int_accessor);
+ }
+}
+
+template <>
+void FieldAccessViaAccessor<double>::Update(ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ Accessor* accessor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ TypeAdaptorAccessor<int64_t, double> double_to_int_accessor(accessor);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->UpdateField64ViaAccessor<kTransactionActive>(field_offset, &double_to_int_accessor);
+ } else {
+ obj->UpdateField64ViaAccessor<kTransactionInactive>(field_offset, &double_to_int_accessor);
+ }
+}
+
+// Helper class that gets values from a shadow frame with appropriate type coercion.
+template <typename T>
+class ValueGetter {
+ public:
+ static T Get(ShadowFrameGetter* getter) REQUIRES_SHARED(Locks::mutator_lock_) {
+ static_assert(sizeof(T) <= sizeof(uint32_t), "Bad size");
+ uint32_t raw_value = getter->Get();
+ return static_cast<T>(raw_value);
+ }
+};
+
+template <>
+int64_t ValueGetter<int64_t>::Get(ShadowFrameGetter* getter) {
+ return getter->GetLong();
+}
+
+template <>
+float ValueGetter<float>::Get(ShadowFrameGetter* getter) {
+ uint32_t raw_value = getter->Get();
+ return *reinterpret_cast<float*>(&raw_value);
+}
+
+template <>
+double ValueGetter<double>::Get(ShadowFrameGetter* getter) {
+ int64_t raw_value = getter->GetLong();
+ return *reinterpret_cast<double*>(&raw_value);
+}
+
+template <>
+ObjPtr<Object> ValueGetter<ObjPtr<Object>>::Get(ShadowFrameGetter* getter) {
+ return getter->GetReference();
+}
+
+// Class for accessing fields of Object instances
+template <typename T>
+class FieldAccessor {
+ public:
+ static bool Dispatch(VarHandle::AccessMode access_mode,
+ ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ ShadowFrameGetter* getter,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+// Dispatch implementation for primitive fields.
+template <typename T>
+bool FieldAccessor<T>::Dispatch(VarHandle::AccessMode access_mode,
+ ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ ShadowFrameGetter* getter,
+ JValue* result) {
+ switch (access_mode) {
+ case VarHandle::AccessMode::kGet: {
+ GetAccessor<T> accessor(result);
+ FieldAccessViaAccessor<T>::Get(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kSet: {
+ T new_value = ValueGetter<T>::Get(getter);
+ SetAccessor<T> accessor(new_value);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAcquire:
+ case VarHandle::AccessMode::kGetOpaque:
+ case VarHandle::AccessMode::kGetVolatile: {
+ GetVolatileAccessor<T> accessor(result);
+ FieldAccessViaAccessor<T>::Get(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kSetOpaque:
+ case VarHandle::AccessMode::kSetRelease:
+ case VarHandle::AccessMode::kSetVolatile: {
+ T new_value = ValueGetter<T>::Get(getter);
+ SetVolatileAccessor<T> accessor(new_value);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndSet: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ CompareAndSetAccessor<T> accessor(expected_value, desired_value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndExchange:
+ case VarHandle::AccessMode::kCompareAndExchangeAcquire:
+ case VarHandle::AccessMode::kCompareAndExchangeRelease: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kWeakCompareAndSet:
+ case VarHandle::AccessMode::kWeakCompareAndSetAcquire:
+ case VarHandle::AccessMode::kWeakCompareAndSetPlain:
+ case VarHandle::AccessMode::kWeakCompareAndSetRelease: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndSet:
+ case VarHandle::AccessMode::kGetAndSetAcquire:
+ case VarHandle::AccessMode::kGetAndSetRelease: {
+ T new_value = ValueGetter<T>::Get(getter);
+ GetAndSetAccessor<T> accessor(new_value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndAdd:
+ case VarHandle::AccessMode::kGetAndAddAcquire:
+ case VarHandle::AccessMode::kGetAndAddRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndAddAccessor<T> accessor(value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseOr:
+ case VarHandle::AccessMode::kGetAndBitwiseOrAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseOrRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndBitwiseOrAccessor<T> accessor(value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseAnd:
+ case VarHandle::AccessMode::kGetAndBitwiseAndAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseAndRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndBitwiseAndAccessor<T> accessor(value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseXor:
+ case VarHandle::AccessMode::kGetAndBitwiseXorAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseXorRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndBitwiseXorAccessor<T> accessor(value, result);
+ FieldAccessViaAccessor<T>::Update(obj, field_offset, &accessor);
+ break;
+ }
+ }
+ return true;
+}
+
+// Dispatch implementation for reference fields.
+template <>
+bool FieldAccessor<ObjPtr<Object>>::Dispatch(VarHandle::AccessMode access_mode,
+ ObjPtr<Object> obj,
+ MemberOffset field_offset,
+ ShadowFrameGetter* getter,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // To keep things simple, use the minimum strongest existing
+ // field accessor for Object fields. This may be the most
+ // straightforward strategy in general for the interpreter.
+ switch (access_mode) {
+ case VarHandle::AccessMode::kGet: {
+ StoreResult(obj->GetFieldObject<Object>(field_offset), result);
+ break;
+ }
+ case VarHandle::AccessMode::kSet: {
+ ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->SetFieldObject<kTransactionActive>(field_offset, new_value);
+ } else {
+ obj->SetFieldObject<kTransactionInactive>(field_offset, new_value);
+ }
+ break;
+ }
+ case VarHandle::AccessMode::kGetAcquire:
+ case VarHandle::AccessMode::kGetOpaque:
+ case VarHandle::AccessMode::kGetVolatile: {
+ StoreResult(obj->GetFieldObjectVolatile<Object>(field_offset), result);
+ break;
+ }
+ case VarHandle::AccessMode::kSetOpaque:
+ case VarHandle::AccessMode::kSetRelease:
+ case VarHandle::AccessMode::kSetVolatile: {
+ ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ obj->SetFieldObjectVolatile<kTransactionActive>(field_offset, new_value);
+ } else {
+ obj->SetFieldObjectVolatile<kTransactionInactive>(field_offset, new_value);
+ }
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndSet: {
+ ReadBarrierForVarHandleAccess(obj, field_offset);
+ ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ bool cas_result;
+ if (Runtime::Current()->IsActiveTransaction()) {
+ cas_result = obj->CasFieldStrongSequentiallyConsistentObject<kTransactionActive>(
+ field_offset,
+ expected_value,
+ desired_value);
+ } else {
+ cas_result = obj->CasFieldStrongSequentiallyConsistentObject<kTransactionInactive>(
+ field_offset,
+ expected_value,
+ desired_value);
+ }
+ StoreResult(cas_result, result);
+ break;
+ }
+ case VarHandle::AccessMode::kWeakCompareAndSet:
+ case VarHandle::AccessMode::kWeakCompareAndSetAcquire:
+ case VarHandle::AccessMode::kWeakCompareAndSetPlain:
+ case VarHandle::AccessMode::kWeakCompareAndSetRelease: {
+ ReadBarrierForVarHandleAccess(obj, field_offset);
+ ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ bool cas_result;
+ if (Runtime::Current()->IsActiveTransaction()) {
+ cas_result = obj->CasFieldWeakSequentiallyConsistentObject<kTransactionActive>(
+ field_offset,
+ expected_value,
+ desired_value);
+ } else {
+ cas_result = obj->CasFieldWeakSequentiallyConsistentObject<kTransactionInactive>(
+ field_offset,
+ expected_value,
+ desired_value);
+ }
+ StoreResult(cas_result, result);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndExchange:
+ case VarHandle::AccessMode::kCompareAndExchangeAcquire:
+ case VarHandle::AccessMode::kCompareAndExchangeRelease: {
+ ReadBarrierForVarHandleAccess(obj, field_offset);
+ ObjPtr<Object> expected_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ ObjPtr<Object> desired_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ ObjPtr<Object> witness_value;
+ if (Runtime::Current()->IsActiveTransaction()) {
+ witness_value = obj->CompareAndExchangeFieldObject<kTransactionActive>(
+ field_offset,
+ expected_value,
+ desired_value);
+ } else {
+ witness_value = obj->CompareAndExchangeFieldObject<kTransactionInactive>(
+ field_offset,
+ expected_value,
+ desired_value);
+ }
+ StoreResult(witness_value, result);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndSet:
+ case VarHandle::AccessMode::kGetAndSetAcquire:
+ case VarHandle::AccessMode::kGetAndSetRelease: {
+ ReadBarrierForVarHandleAccess(obj, field_offset);
+ ObjPtr<Object> new_value = ValueGetter<ObjPtr<Object>>::Get(getter);
+ ObjPtr<Object> old_value;
+ if (Runtime::Current()->IsActiveTransaction()) {
+ old_value = obj->ExchangeFieldObject<kTransactionActive>(field_offset, new_value);
+ } else {
+ old_value = obj->ExchangeFieldObject<kTransactionInactive>(field_offset, new_value);
+ }
+ StoreResult(old_value, result);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndAdd:
+ case VarHandle::AccessMode::kGetAndAddAcquire:
+ case VarHandle::AccessMode::kGetAndAddRelease:
+ case VarHandle::AccessMode::kGetAndBitwiseOr:
+ case VarHandle::AccessMode::kGetAndBitwiseOrAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseOrRelease:
+ case VarHandle::AccessMode::kGetAndBitwiseAnd:
+ case VarHandle::AccessMode::kGetAndBitwiseAndAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseAndRelease:
+ case VarHandle::AccessMode::kGetAndBitwiseXor:
+ case VarHandle::AccessMode::kGetAndBitwiseXorAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseXorRelease: {
+ size_t index = static_cast<size_t>(access_mode);
+ const char* access_mode_name = kAccessorToAccessMode[index].method_name;
+ UnreachableAccessMode(access_mode_name, "Object");
+ }
+ }
+ return true;
+}
+
+// Class for accessing primitive array elements.
+template <typename T>
+class PrimitiveArrayElementAccessor {
+ public:
+ static T* GetElementAddress(ObjPtr<Array> target_array, int target_element)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto primitive_array = ObjPtr<PrimitiveArray<T>>::DownCast(target_array);
+ DCHECK(primitive_array->CheckIsValidIndex(target_element));
+ return &primitive_array->GetData()[target_element];
+ }
+
+ static bool Dispatch(VarHandle::AccessMode access_mode,
+ ObjPtr<Array> target_array,
+ int target_element,
+ ShadowFrameGetter* getter,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ T* element_address = GetElementAddress(target_array, target_element);
+ switch (access_mode) {
+ case VarHandle::AccessMode::kGet: {
+ GetAccessor<T> accessor(result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kSet: {
+ T new_value = ValueGetter<T>::Get(getter);
+ SetAccessor<T> accessor(new_value);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAcquire:
+ case VarHandle::AccessMode::kGetOpaque:
+ case VarHandle::AccessMode::kGetVolatile: {
+ GetVolatileAccessor<T> accessor(result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kSetOpaque:
+ case VarHandle::AccessMode::kSetRelease:
+ case VarHandle::AccessMode::kSetVolatile: {
+ T new_value = ValueGetter<T>::Get(getter);
+ SetVolatileAccessor<T> accessor(new_value);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndSet: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ CompareAndSetAccessor<T> accessor(expected_value, desired_value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndExchange:
+ case VarHandle::AccessMode::kCompareAndExchangeAcquire:
+ case VarHandle::AccessMode::kCompareAndExchangeRelease: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kWeakCompareAndSet:
+ case VarHandle::AccessMode::kWeakCompareAndSetAcquire:
+ case VarHandle::AccessMode::kWeakCompareAndSetPlain:
+ case VarHandle::AccessMode::kWeakCompareAndSetRelease: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndSet:
+ case VarHandle::AccessMode::kGetAndSetAcquire:
+ case VarHandle::AccessMode::kGetAndSetRelease: {
+ T new_value = ValueGetter<T>::Get(getter);
+ GetAndSetAccessor<T> accessor(new_value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndAdd:
+ case VarHandle::AccessMode::kGetAndAddAcquire:
+ case VarHandle::AccessMode::kGetAndAddRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndAddAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseOr:
+ case VarHandle::AccessMode::kGetAndBitwiseOrAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseOrRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndBitwiseOrAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseAnd:
+ case VarHandle::AccessMode::kGetAndBitwiseAndAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseAndRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndBitwiseAndAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseXor:
+ case VarHandle::AccessMode::kGetAndBitwiseXorAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseXorRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ GetAndBitwiseXorAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ }
+ return true;
+ }
+};
+
+// Class for accessing primitive array elements.
+template <typename T>
+class ByteArrayViewAccessor {
+ public:
+ static inline bool IsAccessAligned(int8_t* data, int data_index) {
+ static_assert(IsPowerOfTwo(sizeof(T)), "unexpected size");
+ static_assert(std::is_arithmetic<T>::value, "unexpected type");
+ uintptr_t alignment_mask = sizeof(T) - 1;
+ uintptr_t address = reinterpret_cast<uintptr_t>(data + data_index);
+ return (address & alignment_mask) == 0;
+ }
+
+ static inline void MaybeByteSwap(bool byte_swap, T* const value) {
+ if (byte_swap) {
+ *value = BSWAP(*value);
+ }
+ }
+
+ static bool Dispatch(const VarHandle::AccessMode access_mode,
+ int8_t* const data,
+ const int data_index,
+ const bool byte_swap,
+ ShadowFrameGetter* const getter,
+ JValue* const result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const bool is_aligned = IsAccessAligned(data, data_index);
+ if (!is_aligned) {
+ switch (access_mode) {
+ case VarHandle::AccessMode::kGet: {
+ T value;
+ memcpy(&value, data + data_index, sizeof(T));
+ MaybeByteSwap(byte_swap, &value);
+ StoreResult(value, result);
+ return true;
+ }
+ case VarHandle::AccessMode::kSet: {
+ T new_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &new_value);
+ memcpy(data + data_index, &new_value, sizeof(T));
+ return true;
+ }
+ default:
+ // No other access modes support unaligned access.
+ ThrowIllegalStateException("Unaligned access not supported");
+ return false;
+ }
+ }
+
+ T* const element_address = reinterpret_cast<T*>(data + data_index);
+ CHECK(IsAccessAligned(reinterpret_cast<int8_t*>(element_address), 0));
+ switch (access_mode) {
+ case VarHandle::AccessMode::kGet: {
+ GetAccessor<T> accessor(result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ case VarHandle::AccessMode::kSet: {
+ T new_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &new_value);
+ SetAccessor<T> accessor(new_value);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAcquire:
+ case VarHandle::AccessMode::kGetOpaque:
+ case VarHandle::AccessMode::kGetVolatile: {
+ GetVolatileAccessor<T> accessor(result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ case VarHandle::AccessMode::kSetOpaque:
+ case VarHandle::AccessMode::kSetRelease:
+ case VarHandle::AccessMode::kSetVolatile: {
+ T new_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &new_value);
+ SetVolatileAccessor<T> accessor(new_value);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndSet: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &expected_value);
+ MaybeByteSwap(byte_swap, &desired_value);
+ CompareAndSetAccessor<T> accessor(expected_value, desired_value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kCompareAndExchange:
+ case VarHandle::AccessMode::kCompareAndExchangeAcquire:
+ case VarHandle::AccessMode::kCompareAndExchangeRelease: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &expected_value);
+ MaybeByteSwap(byte_swap, &desired_value);
+ CompareAndExchangeAccessor<T> accessor(expected_value, desired_value, result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ case VarHandle::AccessMode::kWeakCompareAndSet:
+ case VarHandle::AccessMode::kWeakCompareAndSetAcquire:
+ case VarHandle::AccessMode::kWeakCompareAndSetPlain:
+ case VarHandle::AccessMode::kWeakCompareAndSetRelease: {
+ T expected_value = ValueGetter<T>::Get(getter);
+ T desired_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &expected_value);
+ MaybeByteSwap(byte_swap, &desired_value);
+ WeakCompareAndSetAccessor<T> accessor(expected_value, desired_value, result);
+ accessor.Access(element_address);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndSet:
+ case VarHandle::AccessMode::kGetAndSetAcquire:
+ case VarHandle::AccessMode::kGetAndSetRelease: {
+ T new_value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &new_value);
+ GetAndSetAccessor<T> accessor(new_value, result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndAdd:
+ case VarHandle::AccessMode::kGetAndAddAcquire:
+ case VarHandle::AccessMode::kGetAndAddRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ if (byte_swap) {
+ GetAndAddWithByteSwapAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ } else {
+ GetAndAddAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ }
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseOr:
+ case VarHandle::AccessMode::kGetAndBitwiseOrAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseOrRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &value);
+ GetAndBitwiseOrAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseAnd:
+ case VarHandle::AccessMode::kGetAndBitwiseAndAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseAndRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &value);
+ GetAndBitwiseAndAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ case VarHandle::AccessMode::kGetAndBitwiseXor:
+ case VarHandle::AccessMode::kGetAndBitwiseXorAcquire:
+ case VarHandle::AccessMode::kGetAndBitwiseXorRelease: {
+ T value = ValueGetter<T>::Get(getter);
+ MaybeByteSwap(byte_swap, &value);
+ GetAndBitwiseXorAccessor<T> accessor(value, result);
+ accessor.Access(element_address);
+ JValueByteSwapper<T>::MaybeByteSwap(byte_swap, result);
+ break;
+ }
+ }
+ return true;
+ }
+};
+
} // namespace
Class* VarHandle::GetVarType() {
@@ -267,35 +1425,38 @@ int32_t VarHandle::GetAccessModesBitMask() {
}
bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) {
- ScopedAssertNoThreadSuspension ants(__FUNCTION__);
-
+ StackHandleScope<3> hs(Thread::Current());
+ Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType()));
+ Handle<VarHandle> vh(hs.NewHandle(this));
+ Handle<Class> var_type(hs.NewHandle(vh->GetVarType()));
AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
- // Check return types first.
- ObjPtr<Class> var_type = GetVarType();
- ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type);
- ObjPtr<Class> void_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V');
- ObjPtr<Class> mt_rtype = method_type->GetRType();
-
- // If the mt_rtype is void, the result of the operation will be discarded (okay).
- if (mt_rtype != void_type && mt_rtype != vh_rtype) {
- return false;
+
+ // Check return type first.
+ if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) {
+ // The result of the operation will be discarded. The return type
+ // of the VarHandle is immaterial.
+ } else {
+ ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get()));
+ if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) {
+ return false;
+ }
}
// Check the number of parameters matches.
ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
access_mode_template,
- var_type,
+ var_type.Get(),
GetCoordinateType0(),
GetCoordinateType1());
if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) {
return false;
}
- // Check the parameter types match.
+ // Check the parameter types are compatible.
ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
for (int32_t i = 0; i < vh_ptypes_count; ++i) {
- if (mt_ptypes->Get(i) != vh_ptypes[i].Ptr()) {
+ if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
return false;
}
}
@@ -311,8 +1472,9 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
StackHandleScope<3> hs(self);
Handle<VarHandle> vh = hs.NewHandle(var_handle);
Handle<Class> rtype = hs.NewHandle(GetReturnType(access_mode_template, vh->GetVarType()));
- const int32_t ptypes_count =
- GetParameterCount(access_mode_template, vh->GetCoordinateType0(), vh->GetCoordinateType1());
+ const int32_t ptypes_count = GetNumberOfParameters(access_mode_template,
+ vh->GetCoordinateType0(),
+ vh->GetCoordinateType1());
Handle<ObjectArray<Class>> ptypes = hs.NewHandle(NewArrayOfClasses(self, ptypes_count));
if (ptypes == nullptr) {
return nullptr;
@@ -334,6 +1496,29 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode acces
return GetMethodTypeForAccessMode(self, this, access_mode);
}
+bool VarHandle::Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result) {
+ Class* klass = GetClass();
+ if (klass == FieldVarHandle::StaticClass()) {
+ auto vh = reinterpret_cast<FieldVarHandle*>(this);
+ return vh->Access(access_mode, shadow_frame, operands, result);
+ } else if (klass == ArrayElementVarHandle::StaticClass()) {
+ auto vh = reinterpret_cast<ArrayElementVarHandle*>(this);
+ return vh->Access(access_mode, shadow_frame, operands, result);
+ } else if (klass == ByteArrayViewVarHandle::StaticClass()) {
+ auto vh = reinterpret_cast<ByteArrayViewVarHandle*>(this);
+ return vh->Access(access_mode, shadow_frame, operands, result);
+ } else if (klass == ByteBufferViewVarHandle::StaticClass()) {
+ auto vh = reinterpret_cast<ByteBufferViewVarHandle*>(this);
+ return vh->Access(access_mode, shadow_frame, operands, result);
+ } else {
+ LOG(FATAL) << "Unknown varhandle kind";
+ UNREACHABLE();
+ }
+}
+
const char* VarHandle::GetReturnTypeDescriptor(const char* accessor_name) {
AccessMode access_mode;
if (!GetAccessModeByMethodName(accessor_name, &access_mode)) {
@@ -369,6 +1554,10 @@ bool VarHandle::GetAccessModeByMethodName(const char* method_name, AccessMode* a
return true;
}
+Class* VarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+}
+
void VarHandle::SetClass(Class* klass) {
CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
CHECK(klass != nullptr);
@@ -391,6 +1580,57 @@ ArtField* FieldVarHandle::GetField() {
return reinterpret_cast<ArtField*>(opaque_field);
}
+bool FieldVarHandle::Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result) {
+ ShadowFrameGetter getter(*shadow_frame, operands);
+ ArtField* field = GetField();
+ ObjPtr<Object> obj;
+ if (field->IsStatic()) {
+ DCHECK_LE(operands->GetNumberOfOperands(),
+ 2u * (Primitive::Is64BitType(GetVarType()->GetPrimitiveType()) ? 2u : 1u));
+ obj = field->GetDeclaringClass();
+ } else {
+ DCHECK_GE(operands->GetNumberOfOperands(), 1u);
+ DCHECK_LE(operands->GetNumberOfOperands(),
+ 1u + 2u * (Primitive::Is64BitType(GetVarType()->GetPrimitiveType()) ? 2u : 1u));
+ obj = getter.GetReference();
+ if (obj.IsNull()) {
+ ThrowNullPointerExceptionForCoordinate();
+ return false;
+ }
+ }
+ DCHECK(!obj.IsNull());
+
+ const MemberOffset offset = field->GetOffset();
+ const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
+ switch (primitive_type) {
+ case Primitive::Type::kPrimNot:
+ return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimBoolean:
+ return FieldAccessor<uint8_t>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimByte:
+ return FieldAccessor<int8_t>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimChar:
+ return FieldAccessor<uint16_t>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimShort:
+ return FieldAccessor<int16_t>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimInt:
+ return FieldAccessor<int32_t>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimFloat:
+ return FieldAccessor<float>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimLong:
+ return FieldAccessor<int64_t>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimDouble:
+ return FieldAccessor<double>::Dispatch(access_mode, obj, offset, &getter, result);
+ case Primitive::kPrimVoid:
+ break;
+ }
+ LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type;
+ UNREACHABLE();
+}
+
Class* FieldVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
return static_class_.Read();
}
@@ -412,6 +1652,94 @@ void FieldVarHandle::VisitRoots(RootVisitor* visitor) {
GcRoot<Class> FieldVarHandle::static_class_;
+bool ArrayElementVarHandle::Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result) {
+ ShadowFrameGetter getter(*shadow_frame, operands);
+
+ // The target array is the first co-ordinate type preceeding var type arguments.
+ ObjPtr<Object> raw_array(getter.GetReference());
+ if (raw_array == nullptr) {
+ ThrowNullPointerExceptionForCoordinate();
+ return false;
+ }
+
+ ObjPtr<Array> target_array(raw_array->AsArray());
+
+ // The target array element is the second co-ordinate type preceeding var type arguments.
+ const int target_element = getter.Get();
+ if (!target_array->CheckIsValidIndex(target_element)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return false;
+ }
+
+ const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
+ switch (primitive_type) {
+ case Primitive::Type::kPrimNot: {
+ MemberOffset target_element_offset =
+ target_array->AsObjectArray<Object>()->OffsetOfElement(target_element);
+ return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode,
+ target_array,
+ target_element_offset,
+ &getter,
+ result);
+ }
+ case Primitive::Type::kPrimBoolean:
+ return PrimitiveArrayElementAccessor<uint8_t>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimByte:
+ return PrimitiveArrayElementAccessor<int8_t>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimChar:
+ return PrimitiveArrayElementAccessor<uint16_t>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimShort:
+ return PrimitiveArrayElementAccessor<int16_t>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimInt:
+ return PrimitiveArrayElementAccessor<int32_t>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimLong:
+ return PrimitiveArrayElementAccessor<int64_t>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimFloat:
+ return PrimitiveArrayElementAccessor<float>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimDouble:
+ return PrimitiveArrayElementAccessor<double>::Dispatch(access_mode,
+ target_array,
+ target_element,
+ &getter,
+ result);
+ case Primitive::Type::kPrimVoid:
+ break;
+ }
+ LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type;
+ UNREACHABLE();
+}
+
Class* ArrayElementVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
return static_class_.Read();
}
@@ -437,6 +1765,90 @@ bool ByteArrayViewVarHandle::GetNativeByteOrder() {
return GetFieldBoolean(NativeByteOrderOffset());
}
+bool ByteArrayViewVarHandle::Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result) {
+ ShadowFrameGetter getter(*shadow_frame, operands);
+
+ // The byte array is the first co-ordinate type preceeding var type arguments.
+ ObjPtr<Object> raw_byte_array(getter.GetReference());
+ if (raw_byte_array == nullptr) {
+ ThrowNullPointerExceptionForCoordinate();
+ return false;
+ }
+
+ ObjPtr<ByteArray> byte_array(raw_byte_array->AsByteArray());
+
+ // The offset in the byte array element is the second co-ordinate type.
+ const int32_t data_offset = getter.Get();
+
+ // Bounds check requested access.
+ const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
+ if (!CheckElementIndex(primitive_type, data_offset, byte_array->GetLength())) {
+ return false;
+ }
+
+ int8_t* const data = byte_array->GetData();
+ bool byte_swap = !GetNativeByteOrder();
+ switch (primitive_type) {
+ case Primitive::Type::kPrimNot:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimVoid:
+ // These are not supported for byte array views and not instantiable.
+ break;
+ case Primitive::kPrimChar:
+ return ByteArrayViewAccessor<uint16_t>::Dispatch(access_mode,
+ data,
+ data_offset,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimShort:
+ return ByteArrayViewAccessor<int16_t>::Dispatch(access_mode,
+ data,
+ data_offset,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimInt:
+ return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode,
+ data,
+ data_offset,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimFloat:
+ // Treated as a bitwise representation. See javadoc comments for
+ // java.lang.invoke.MethodHandles.byteArrayViewVarHandle().
+ return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode,
+ data,
+ data_offset,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimLong:
+ return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode,
+ data,
+ data_offset,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimDouble:
+ // Treated as a bitwise representation. See javadoc comments for
+ // java.lang.invoke.MethodHandles.byteArrayViewVarHandle().
+ return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode,
+ data,
+ data_offset,
+ byte_swap,
+ &getter,
+ result);
+ }
+ LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type;
+ UNREACHABLE();
+}
+
Class* ByteArrayViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
return static_class_.Read();
}
@@ -462,6 +1874,122 @@ bool ByteBufferViewVarHandle::GetNativeByteOrder() {
return GetFieldBoolean(NativeByteOrderOffset());
}
+bool ByteBufferViewVarHandle::Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result) {
+ ShadowFrameGetter getter(*shadow_frame, operands);
+
+ // The byte buffer is the first co-ordinate argument preceeding var type arguments.
+ ObjPtr<Object> byte_buffer(getter.GetReference());
+ if (byte_buffer == nullptr) {
+ ThrowNullPointerExceptionForCoordinate();
+ return false;
+ }
+
+ // The byte index for access is the second co-ordinate
+ // argument. This is relative to the offset field of the ByteBuffer.
+ const int32_t byte_index = getter.Get();
+
+ // Check access_mode is compatible with ByteBuffer's read-only property.
+ bool is_read_only = byte_buffer->GetFieldBoolean(
+ GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_isReadOnly));
+ if (is_read_only && !IsReadOnlyAccessMode(access_mode)) {
+ ThrowReadOnlyBufferException();
+ return false;
+ }
+
+ // The native_address is only set for ByteBuffer instances backed by native memory.
+ const int64_t native_address =
+ byte_buffer->GetField64(GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_address));
+
+ // Determine offset and limit for accesses.
+ int32_t byte_buffer_offset;
+ if (native_address == 0l) {
+ // Accessing a heap allocated byte buffer.
+ byte_buffer_offset = byte_buffer->GetField32(
+ GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_offset));
+ } else {
+ // Accessing direct memory.
+ byte_buffer_offset = 0;
+ }
+ const int32_t byte_buffer_limit = byte_buffer->GetField32(
+ GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_limit));
+
+ const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
+ if (!CheckElementIndex(primitive_type, byte_index, byte_buffer_offset, byte_buffer_limit)) {
+ return false;
+ }
+ const int32_t checked_offset32 = byte_buffer_offset + byte_index;
+
+ int8_t* data;
+ if (native_address == 0) {
+ ObjPtr<ByteArray> heap_byte_array = byte_buffer->GetFieldObject<ByteArray>(
+ GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_hb));
+ data = heap_byte_array->GetData();
+ } else {
+ data = reinterpret_cast<int8_t*>(static_cast<uint32_t>(native_address));
+ }
+
+ bool byte_swap = !GetNativeByteOrder();
+ switch (primitive_type) {
+ case Primitive::kPrimChar:
+ return ByteArrayViewAccessor<uint16_t>::Dispatch(access_mode,
+ data,
+ checked_offset32,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimShort:
+ return ByteArrayViewAccessor<int16_t>::Dispatch(access_mode,
+ data,
+ checked_offset32,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimInt:
+ return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode,
+ data,
+ checked_offset32,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimFloat:
+ // Treated as a bitwise representation. See javadoc comments for
+ // java.lang.invoke.MethodHandles.byteArrayViewVarHandle().
+ return ByteArrayViewAccessor<int32_t>::Dispatch(access_mode,
+ data,
+ checked_offset32,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimLong:
+ return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode,
+ data,
+ checked_offset32,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::kPrimDouble:
+ // Treated as a bitwise representation. See javadoc comments for
+ // java.lang.invoke.MethodHandles.byteArrayViewVarHandle().
+ return ByteArrayViewAccessor<int64_t>::Dispatch(access_mode,
+ data,
+ checked_offset32,
+ byte_swap,
+ &getter,
+ result);
+ case Primitive::Type::kPrimNot:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimVoid:
+ // These are not supported for byte array views and not instantiable.
+ break;
+ }
+ LOG(FATAL) << "Unreachable: Unexpected primitive " << primitive_type;
+ UNREACHABLE();
+}
+
Class* ByteBufferViewVarHandle::StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
return static_class_.Read();
}
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index 7b48669bba..6565af7f06 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -18,18 +18,24 @@
#define ART_RUNTIME_MIRROR_VAR_HANDLE_H_
#include "handle.h"
+#include "interpreter/shadow_frame.h"
#include "gc_root.h"
+#include "jvalue.h"
#include "object.h"
namespace art {
template<class T> class Handle;
+class InstructionOperands;
+
struct VarHandleOffsets;
struct FieldVarHandleOffsets;
struct ArrayElementVarHandleOffsets;
struct ByteArrayViewVarHandleOffsets;
struct ByteBufferViewVarHandleOffsets;
+class ShadowFrameGetter;
+
namespace mirror {
class MethodType;
@@ -44,6 +50,10 @@ class MANAGED VarHandle : public Object {
// (array, index, old, new).
static constexpr int kMaxAccessorParameters = 4;
+ // The maximum number of VarType parameters a VarHandle accessor
+ // method can take.
+ static constexpr size_t kMaxVarTypeParameters = 2;
+
// Enumeration of the possible access modes. This mirrors the enum
// in java.lang.invoke.VarHandle.
enum class AccessMode : uint32_t {
@@ -101,11 +111,16 @@ class MANAGED VarHandle : public Object {
// supported operation so the MethodType can be used when raising a
// WrongMethodTypeException exception.
MethodType* GetMethodTypeForAccessMode(Thread* self, AccessMode accessMode)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
- static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
- return static_class_.Read();
- }
+ bool Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Gets the variable type that is operated on by this VarHandle instance.
+ Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_);
// Gets the return type descriptor for a named accessor method,
// nullptr if accessor_method is not supported.
@@ -115,12 +130,13 @@ class MANAGED VarHandle : public Object {
// VarHandle access method, such as "setOpaque". Returns false otherwise.
static bool GetAccessModeByMethodName(const char* method_name, AccessMode* access_mode);
+
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
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:
- Class* GetVarType() REQUIRES_SHARED(Locks::mutator_lock_);
Class* GetCoordinateType0() REQUIRES_SHARED(Locks::mutator_lock_);
Class* GetCoordinateType1() REQUIRES_SHARED(Locks::mutator_lock_);
int32_t GetAccessModesBitMask() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -163,6 +179,12 @@ class MANAGED VarHandle : public Object {
// The corresponding managed class in libart java.lang.invoke.FieldVarHandle.
class MANAGED FieldVarHandle : public VarHandle {
public:
+ bool Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ArtField* GetField() REQUIRES_SHARED(Locks::mutator_lock_);
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -190,6 +212,12 @@ class MANAGED FieldVarHandle : public VarHandle {
// The corresponding managed class in libart java.lang.invoke.ArrayElementVarHandle.
class MANAGED ArrayElementVarHandle : public VarHandle {
public:
+ bool Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -207,6 +235,12 @@ class MANAGED ArrayElementVarHandle : public VarHandle {
// The corresponding managed class in libart java.lang.invoke.ByteArrayViewVarHandle.
class MANAGED ByteArrayViewVarHandle : public VarHandle {
public:
+ bool Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -234,10 +268,13 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle {
// The corresponding managed class in libart java.lang.invoke.ByteBufferViewVarHandle.
class MANAGED ByteBufferViewVarHandle : public VarHandle {
public:
- bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
+ bool Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ InstructionOperands* operands,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
- static ByteBufferViewVarHandle* Create(Thread* const self, bool native_byte_order)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ bool GetNativeByteOrder() REQUIRES_SHARED(Locks::mutator_lock_);
static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_);
static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -245,6 +282,21 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle {
static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
private:
+ bool AccessHeapBuffer(AccessMode access_mode,
+ ObjPtr<Object> byte_buffer,
+ int buffer_offset,
+ ObjPtr<ByteArray> heap_byte_array,
+ ShadowFrameGetter* getter,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool AccessFixedMemory(AccessMode access_mode,
+ ObjPtr<Object> byte_buffer,
+ int buffer_offset,
+ ShadowFrameGetter* getter,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
static MemberOffset NativeByteOrderOffset() {
return MemberOffset(OFFSETOF_MEMBER(ByteBufferViewVarHandle, native_byte_order_));
}
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index e844fd4436..d9fa07f207 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -327,7 +327,7 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) {
EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I")));
EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V")));
EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
- EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V")));
}
// Check compatibility - "GetAndUpdate" pattern
@@ -336,7 +336,7 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) {
EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I")));
EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
- EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
}
// Check synthesized method types match expected forms.
@@ -461,8 +461,8 @@ TEST_F(VarHandleTest, StaticFieldVarHandle) {
EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I")));
EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I")));
- EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)D")));
- EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
+ EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V")));
}
// Check compatibility - "GetAndUpdate" pattern
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 6ea9a7ad62..b49209c4cf 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -549,6 +549,55 @@ static jstring DexFile_getDexFileStatus(JNIEnv* env,
return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str());
}
+// Return an array specifying the optimization status of the given file.
+// The array specification is [compiler_filter, compiler_reason].
+static jobjectArray DexFile_getDexFileOptimizationStatus(JNIEnv* env,
+ jclass,
+ jstring javaFilename,
+ jstring javaInstructionSet) {
+ ScopedUtfChars filename(env, javaFilename);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ ScopedUtfChars instruction_set(env, javaInstructionSet);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ const InstructionSet target_instruction_set = GetInstructionSetFromString(
+ instruction_set.c_str());
+ if (target_instruction_set == InstructionSet::kNone) {
+ ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
+ std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
+ env->ThrowNew(iae.get(), message.c_str());
+ return nullptr;
+ }
+
+ std::string compilation_filter;
+ std::string compilation_reason;
+ OatFileAssistant::GetOptimizationStatus(
+ filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason);
+
+ ScopedLocalRef<jstring> j_compilation_filter(env, env->NewStringUTF(compilation_filter.c_str()));
+ if (j_compilation_filter.get() == nullptr) {
+ return nullptr;
+ }
+ ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str()));
+ if (j_compilation_reason.get() == nullptr) {
+ return nullptr;
+ }
+
+ // Now create output array and copy the set into it.
+ jobjectArray result = env->NewObjectArray(2,
+ WellKnownClasses::java_lang_String,
+ nullptr);
+ env->SetObjectArrayElement(result, 0, j_compilation_filter.get());
+ env->SetObjectArrayElement(result, 1, j_compilation_reason.get());
+
+ return result;
+}
+
static jint DexFile_getDexOptNeeded(JNIEnv* env,
jclass,
jstring javaFilename,
@@ -801,7 +850,9 @@ static JNINativeMethod gMethods[] = {
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(DexFile, getDexFileOutputPaths,
"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J")
+ NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
};
void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 57a429cf1e..505b745200 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -405,18 +405,15 @@ static void PreloadDexCachesResolveMethod(ObjPtr<mirror::DexCache> dex_cache, ui
}
const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
- ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType(
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ ObjPtr<mirror::Class> klass = class_linker->LookupResolvedType(
method_id.class_idx_, dex_cache, /* class_loader */ nullptr);
if (klass == nullptr) {
return;
}
- ArtMethod* method = klass->IsInterface()
- ? klass->FindInterfaceMethod(dex_cache, method_idx, kRuntimePointerSize)
- : klass->FindClassMethod(dex_cache, method_idx, kRuntimePointerSize);
- if (method == nullptr) {
- return;
- }
- dex_cache->SetResolvedMethod(method_idx, method, kRuntimePointerSize);
+ // Call FindResolvedMethod to populate the dex cache.
+ class_linker->FindResolvedMethod(klass, dex_cache, /* class_loader */ nullptr, method_idx);
}
struct DexCacheStats {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 648a464b6e..12400e26ea 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -173,7 +173,7 @@ enum {
DEBUG_JAVA_DEBUGGABLE = 1 << 8,
DISABLE_VERIFIER = 1 << 9,
ONLY_USE_SYSTEM_OAT_FILES = 1 << 10,
- DISABLE_HIDDEN_API_CHECKS = 1 << 11,
+ ENABLE_HIDDEN_API_CHECKS = 1 << 11,
DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12,
};
@@ -282,7 +282,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
runtime_flags = EnableDebugFeatures(runtime_flags);
- bool do_hidden_api_checks = true;
+ bool do_hidden_api_checks = false;
if ((runtime_flags & DISABLE_VERIFIER) != 0) {
Runtime::Current()->DisableVerifier();
@@ -294,9 +294,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
runtime_flags &= ~ONLY_USE_SYSTEM_OAT_FILES;
}
- if ((runtime_flags & DISABLE_HIDDEN_API_CHECKS) != 0) {
- do_hidden_api_checks = false;
- runtime_flags &= ~DISABLE_HIDDEN_API_CHECKS;
+ if ((runtime_flags & ENABLE_HIDDEN_API_CHECKS) != 0) {
+ do_hidden_api_checks = true;
+ runtime_flags &= ~ENABLE_HIDDEN_API_CHECKS;
}
if (runtime_flags != 0) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 2091a27ffd..e518553292 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -97,7 +97,8 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath);
+ return hiddenapi::ShouldBlockAccessToMember(
+ member, self, IsCallerInBootClassPath, hiddenapi::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
@@ -175,6 +176,12 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean
return soa.AddLocalReference<jclass>(c.Get());
}
+static jclass Class_getPrimitiveClass(JNIEnv* env, jclass, jstring name) {
+ ScopedFastNativeObjectAccess soa(env);
+ ObjPtr<mirror::Class> klass = mirror::Class::GetPrimitiveClass(soa.Decode<mirror::String>(name));
+ return soa.AddLocalReference<jclass>(klass);
+}
+
static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
@@ -868,6 +875,7 @@ static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Class, getInnerClassFlags, "(I)I"),
FAST_NATIVE_METHOD(Class, getInnerClassName, "()Ljava/lang/String;"),
FAST_NATIVE_METHOD(Class, getInterfacesInternal, "()[Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, getPrimitiveClass, "(Ljava/lang/String;)Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
FAST_NATIVE_METHOD(Class, getPublicDeclaredFields, "()[Ljava/lang/reflect/Field;"),
FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"),
diff --git a/runtime/native/java_lang_Void.cc b/runtime/native/java_lang_Void.cc
deleted file mode 100644
index af83dd1a79..0000000000
--- a/runtime/native/java_lang_Void.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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 "java_lang_Void.h"
-
-#include "nativehelper/jni_macros.h"
-
-#include "class_linker-inl.h"
-#include "jni_internal.h"
-#include "native_util.h"
-#include "runtime.h"
-#include "scoped_fast_native_object_access-inl.h"
-
-namespace art {
-
-static jclass Void_lookupType(JNIEnv* env, jclass) {
- ScopedFastNativeObjectAccess soa(env);
- return soa.AddLocalReference<jclass>(
- Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kPrimitiveVoid));
-}
-
-static JNINativeMethod gMethods[] = {
- FAST_NATIVE_METHOD(Void, lookupType, "()Ljava/lang/Class;"),
-};
-
-void register_java_lang_Void(JNIEnv* env) {
- REGISTER_NATIVE_METHODS("java/lang/Void");
-}
-
-} // namespace art
diff --git a/runtime/native/java_lang_Void.h b/runtime/native/java_lang_Void.h
deleted file mode 100644
index 8777d8068c..0000000000
--- a/runtime/native/java_lang_Void.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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_NATIVE_JAVA_LANG_VOID_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_
-
-#include <jni.h>
-
-namespace art {
-
-void register_java_lang_Void(JNIEnv* env);
-
-} // namespace art
-
-#endif // ART_RUNTIME_NATIVE_JAVA_LANG_VOID_H_
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 099d77edaa..4d4bab764c 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -345,6 +345,9 @@ void DumpNativeStack(std::ostream& os,
} else {
os << it->map.name;
}
+ if (it->map.offset != 0) {
+ os << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+ }
os << " (";
if (!it->func_name.empty()) {
os << it->func_name;
diff --git a/runtime/oat.h b/runtime/oat.h
index 8f81010a06..af14b3e601 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -45,6 +45,7 @@ class PACKED(4) OatHeader {
static constexpr const char* kClassPathKey = "classpath";
static constexpr const char* kBootClassPathKey = "bootclasspath";
static constexpr const char* kConcurrentCopying = "concurrent-copying";
+ static constexpr const char* kCompilationReasonKey = "compilation-reason";
static constexpr const char kTrueValue[] = "true";
static constexpr const char kFalseValue[] = "false";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index dc4bae3415..0852da5644 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -633,6 +633,15 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
const uint8_t* dex_file_pointer = nullptr;
if (UNLIKELY(dex_file_offset == 0U)) {
if (uncompressed_dex_files_ == nullptr) {
+ // Do not support mixed-mode oat files.
+ if (i > 0) {
+ *error_msg = StringPrintf("In oat file '%s', unsupported uncompressed-dex-file for dex "
+ "file %zu (%s)",
+ GetLocation().c_str(),
+ i,
+ dex_file_location.c_str());
+ return false;
+ }
uncompressed_dex_files_.reset(new std::vector<std::unique_ptr<const DexFile>>());
// No dex files, load it from location.
const ArtDexFileLoader dex_file_loader;
@@ -652,9 +661,31 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
return false;
}
}
+ // The oat file may be out of date wrt/ the dex-file location. We need to be defensive
+ // here and ensure that at least the number of dex files still matches.
+ // Note: actual checksum comparisons are the duty of the OatFileAssistant and will be
+ // done after loading the OatFile.
+ if (uncompressed_dex_files_->size() != dex_file_count) {
+ *error_msg = StringPrintf("In oat file '%s', expected %u uncompressed dex files, but "
+ "found %zu in '%s'",
+ GetLocation().c_str(),
+ dex_file_count,
+ uncompressed_dex_files_->size(),
+ dex_file_location.c_str());
+ return false;
+ }
}
dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin();
} else {
+ // Do not support mixed-mode oat files.
+ if (uncompressed_dex_files_ != nullptr) {
+ *error_msg = StringPrintf("In oat file '%s', unsupported embedded dex-file for dex file "
+ "%zu (%s)",
+ GetLocation().c_str(),
+ i,
+ dex_file_location.c_str());
+ return false;
+ }
if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
"offset %u of %zu but the size of dex file header is %zu",
@@ -1893,6 +1924,10 @@ std::string OatFile::GetClassLoaderContext() const {
return GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
}
+const char* OatFile::GetCompilationReason() const {
+ return GetOatHeader().GetStoreValueByKey(OatHeader::kCompilationReasonKey);
+}
+
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 46c692e568..802adc3a36 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -130,6 +130,8 @@ class OatFile {
std::string GetClassLoaderContext() const;
+ const char* GetCompilationReason() const;
+
const std::string& GetLocation() const {
return location_;
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 15a5954396..0170073e29 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -1262,4 +1262,27 @@ std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
}
return std::unique_ptr<OatFile>();
}
+
+// TODO(calin): we could provide a more refined status here
+// (e.g. run from uncompressed apk, run with vdex but not oat etc). It will allow us to
+// track more experiments but adds extra complexity.
+void OatFileAssistant::GetOptimizationStatus(
+ const std::string& filename,
+ InstructionSet isa,
+ std::string* out_compilation_filter,
+ std::string* out_compilation_reason) {
+ // Try to load the oat file as we would do at runtime.
+ OatFileAssistant oat_file_assistant(filename.c_str(), isa, true /* load_executable */);
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+
+ if (oat_file == nullptr) {
+ *out_compilation_filter = "run-from-apk";
+ *out_compilation_reason = "unknown";
+ } else {
+ *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter());
+ const char* reason = oat_file->GetCompilationReason();
+ *out_compilation_reason = reason == nullptr ? "unknown" : reason;
+ }
+}
+
} // namespace art
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index a6140304c2..a184807b73 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -226,6 +226,20 @@ class OatFileAssistant {
// dex file. The returned description is for debugging purposes only.
std::string GetStatusDump();
+ // Computes the optimization status of the given dex file. The result is
+ // returned via the two output parameters.
+ // - out_compilation_filter: the level of optimizations (compiler filter)
+ // - out_compilation_reason: the optimization reason. The reason might
+ // be "unknown" if the compiler artifacts were not annotated during optimizations.
+ //
+ // This method will try to mimic the runtime effect of loading the dex file.
+ // For example, if there is no usable oat file, the compiler filter will be set
+ // to "run-from-apk".
+ static void GetOptimizationStatus(const std::string& filename,
+ InstructionSet isa,
+ std::string* out_compilation_filter,
+ std::string* out_compilation_reason);
+
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 50f5e7a0d5..72f7d02892 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -43,7 +43,27 @@ namespace art {
static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4]
static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr;
-class OatFileAssistantTest : public DexoptTest {};
+class OatFileAssistantTest : public DexoptTest {
+ public:
+ void VerifyOptimizationStatus(const std::string& file,
+ const std::string& expected_filter,
+ const std::string& expected_reason) {
+ std::string compilation_filter;
+ std::string compilation_reason;
+ OatFileAssistant::GetOptimizationStatus(
+ file, kRuntimeISA, &compilation_filter, &compilation_reason);
+
+ ASSERT_EQ(expected_filter, compilation_filter);
+ ASSERT_EQ(expected_reason, compilation_reason);
+ }
+
+ void VerifyOptimizationStatus(const std::string& file,
+ CompilerFilter::Filter expected_filter,
+ const std::string& expected_reason) {
+ VerifyOptimizationStatus(
+ file, CompilerFilter::NameOfFilter(expected_filter), expected_reason);
+ }
+};
class OatFileAssistantNoDex2OatTest : public DexoptTest {
public:
@@ -107,6 +127,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+
+ VerifyOptimizationStatus(dex_location, "run-from-apk", "unknown");
}
// Case: We have no DEX file and no OAT file.
@@ -136,7 +158,7 @@ TEST_F(OatFileAssistantTest, OdexUpToDate) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
- GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
// For the use of oat location by making the dex parent not writable.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
@@ -154,6 +176,8 @@ TEST_F(OatFileAssistantTest, OdexUpToDate) {
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+
+ VerifyOptimizationStatus(dex_location, CompilerFilter::kSpeed, "install");
}
// Case: We have a DEX file and a PIC ODEX file, but no OAT file. We load the dex
@@ -221,6 +245,8 @@ TEST_F(OatFileAssistantTest, OatUpToDate) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+
+ VerifyOptimizationStatus(dex_location, CompilerFilter::kSpeed, "unknown");
}
// Case: Passing valid file descriptors of updated odex/vdex filesalong with
@@ -381,6 +407,8 @@ TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) {
// Make sure we don't crash in this case when we dump the status. We don't
// care what the actual dumped value is.
oat_file_assistant.GetStatusDump();
+
+ VerifyOptimizationStatus(dex_location, "run-from-apk", "unknown");
}
// Case: We have a DEX file and empty VDEX and ODEX files.
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index 8d864018ab..89812f370f 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -88,4 +88,46 @@ TEST_F(OatFileTest, LoadOat) {
EXPECT_EQ(odex_file->GetVdexFile()->Begin(), odex_file->VdexBegin());
}
+TEST_F(OatFileTest, ChangingMultiDexUncompressed) {
+ std::string dex_location = GetScratchDir() + "/MultiDexUncompressed.jar";
+
+ Copy(GetTestDexFileName("MultiDexUncompressed"), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kQuicken);
+
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+
+ // Ensure we can load that file. Just a precondition.
+ {
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file != nullptr);
+ ASSERT_EQ(2u, odex_file->GetOatDexFiles().size());
+ }
+
+ // Now replace the source.
+ Copy(GetTestDexFileName("MainUncompressed"), dex_location);
+
+ // And try to load again.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ EXPECT_TRUE(odex_file == nullptr);
+ EXPECT_NE(std::string::npos, error_msg.find("expected 2 uncompressed dex files, but found 1"))
+ << error_msg;
+}
+
} // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 92eb703338..c7f73d14db 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -330,8 +330,10 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define("-Xtarget-sdk-version:_")
.WithType<int>()
.IntoKey(M::TargetSdkVersion)
- .Define("-Xno-hidden-api-checks")
- .IntoKey(M::NoHiddenApiChecks)
+ .Define("-Xhidden-api-checks")
+ .IntoKey(M::HiddenApiChecks)
+ .Define("-Xuse-stderr-logger")
+ .IntoKey(M::UseStderrLogger)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b26b09c156..d0aec116a4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -105,6 +105,7 @@
#include "mirror/method_type.h"
#include "mirror/stack_trace_element.h"
#include "mirror/throwable.h"
+#include "mirror/var_handle.h"
#include "monitor.h"
#include "native/dalvik_system_DexFile.h"
#include "native/dalvik_system_VMDebug.h"
@@ -119,7 +120,6 @@
#include "native/java_lang_Thread.h"
#include "native/java_lang_Throwable.h"
#include "native/java_lang_VMClassLoader.h"
-#include "native/java_lang_Void.h"
#include "native/java_lang_invoke_MethodHandleImpl.h"
#include "native/java_lang_ref_FinalizerReference.h"
#include "native/java_lang_ref_Reference.h"
@@ -265,7 +265,7 @@ Runtime::Runtime()
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
safe_mode_(false),
- do_hidden_api_checks_(true),
+ do_hidden_api_checks_(false),
pending_hidden_api_warning_(false),
dedupe_hidden_api_warnings_(true),
always_set_hidden_api_warning_flag_(false),
@@ -1082,10 +1082,16 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
// Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc.
env_snapshot_.TakeSnapshot();
- RuntimeArgumentMap runtime_options(std::move(runtime_options_in));
+ using Opt = RuntimeArgumentMap;
+ Opt runtime_options(std::move(runtime_options_in));
ScopedTrace trace(__FUNCTION__);
CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
+ // Early override for logging output.
+ if (runtime_options.Exists(Opt::UseStderrLogger)) {
+ android::base::SetLogger(android::base::StderrLogger);
+ }
+
MemMap::Init();
// Try to reserve a dedicated fault page. This is allocated for clobbered registers and sentinels.
@@ -1112,7 +1118,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
}
}
- using Opt = RuntimeArgumentMap;
VLOG(startup) << "Runtime::Init -verbose:startup enabled";
QuasiAtomic::Startup();
@@ -1176,14 +1181,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
// by default and we only enable them if:
// (a) runtime was started with a flag that enables the checks, or
// (b) Zygote forked a new process that is not exempt (see ZygoteHooks).
- // TODO(dbrazdil): Turn the NoHiddenApiChecks negative flag into a positive one
- // to clean up this logic.
- if (kIsTargetBuild && IsAotCompiler() && !runtime_options.Exists(Opt::NoHiddenApiChecks)) {
- // dex2oat on target without -Xno-hidden-api-checks.
- do_hidden_api_checks_ = !IsCompilingBootImage();
- } else {
- do_hidden_api_checks_ = false;
- }
+ do_hidden_api_checks_ = runtime_options.Exists(Opt::HiddenApiChecks);
DCHECK(!is_zygote_ || !do_hidden_api_checks_)
<< "Zygote should not be started with hidden API checks";
@@ -1747,7 +1745,6 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_java_lang_Thread(env);
register_java_lang_Throwable(env);
register_java_lang_VMClassLoader(env);
- register_java_lang_Void(env);
register_java_util_concurrent_atomic_AtomicLong(env);
register_libcore_util_CharsetUtils(env);
register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
@@ -1934,6 +1931,11 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) {
mirror::EmulatedStackFrame::VisitRoots(visitor);
mirror::ClassExt::VisitRoots(visitor);
mirror::CallSite::VisitRoots(visitor);
+ mirror::VarHandle::VisitRoots(visitor);
+ mirror::FieldVarHandle::VisitRoots(visitor);
+ mirror::ArrayElementVarHandle::VisitRoots(visitor);
+ mirror::ByteArrayViewVarHandle::VisitRoots(visitor);
+ mirror::ByteBufferViewVarHandle::VisitRoots(visitor);
// Visit all the primitive array types classes.
mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor); // BooleanArray
mirror::PrimitiveArray<int8_t>::VisitRoots(visitor); // ByteArray
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 7ab9be5c5b..c7f650ea3f 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -325,7 +325,7 @@ class Runtime {
// instead.
void VisitImageRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
- // Visit all of the roots we can do safely do concurrently.
+ // Visit all of the roots we can safely visit concurrently.
void VisitConcurrentRoots(RootVisitor* visitor,
VisitRootFlags flags = kVisitRootFlagAllRoots)
REQUIRES(!Locks::classlinker_classes_lock_, !Locks::trace_lock_)
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index e78d952c1c..dba648e785 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -119,7 +119,7 @@ RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
RUNTIME_OPTIONS_KEY (verifier::VerifyMode, \
Verify, verifier::VerifyMode::kEnable)
RUNTIME_OPTIONS_KEY (int, TargetSdkVersion, Runtime::kUnsetSdkVersion)
-RUNTIME_OPTIONS_KEY (Unit, NoHiddenApiChecks)
+RUNTIME_OPTIONS_KEY (Unit, HiddenApiChecks)
RUNTIME_OPTIONS_KEY (std::string, NativeBridge)
RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10)
RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
@@ -150,5 +150,6 @@ RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullpt
RUNTIME_OPTIONS_KEY (bool, SlowDebug, false)
RUNTIME_OPTIONS_KEY (unsigned int, GlobalRefAllocStackTraceLimit, 0) // 0 = off
+RUNTIME_OPTIONS_KEY (Unit, UseStderrLogger)
#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index 54d2f00106..3b1d5f8c4a 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -24,6 +24,9 @@
#include "mirror/class.h"
#include "runtime.h"
+// Build flag for the bitstring subtype check runtime hooks.
+constexpr bool kBitstringSubtypeCheckEnabled = false;
+
/**
* Any node in a tree can have its path (from the root to the node) represented as a string by
* concatenating the path of the parent to that of the current node.
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 9dc92f3788..2ee7f9deda 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -663,8 +663,8 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz
child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer);
stack_size = FixStackSize(stack_size);
- // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing to
- // assign it.
+ // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing
+ // to assign it.
env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
reinterpret_cast<jlong>(child_thread));
@@ -839,7 +839,8 @@ Thread* Thread::Attach(const char* thread_name,
if (create_peer) {
self->CreatePeer(thread_name, as_daemon, thread_group);
if (self->IsExceptionPending()) {
- // We cannot keep the exception around, as we're deleting self. Try to be helpful and log it.
+ // We cannot keep the exception around, as we're deleting self. Try to be helpful and log
+ // it.
{
ScopedObjectAccess soa(self);
LOG(ERROR) << "Exception creating thread peer:";
@@ -3435,6 +3436,9 @@ bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const {
return object != nullptr && object->GetLockOwnerThreadId() == GetThreadId();
}
+extern "C" StackReference<mirror::Object>* artQuickGetProxyThisObjectReference(ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor).
template <typename RootVisitor, bool kPrecise = false>
class ReferenceMapVisitor : public StackVisitor {
@@ -3535,7 +3539,7 @@ class ReferenceMapVisitor : public StackVisitor {
if (!m->IsNative() && !m->IsRuntimeMethod() && (!m->IsProxyMethod() || m->IsConstructor())) {
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
DCHECK(method_header->IsOptimized());
- auto* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>(
+ StackReference<mirror::Object>* vreg_base = reinterpret_cast<StackReference<mirror::Object>*>(
reinterpret_cast<uintptr_t>(cur_quick_frame));
uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
CodeInfo code_info = method_header->GetOptimizedCodeInfo();
@@ -3550,7 +3554,7 @@ class ReferenceMapVisitor : public StackVisitor {
BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, map);
for (size_t i = 0; i < number_of_bits; ++i) {
if (stack_mask.LoadBit(i)) {
- auto* ref_addr = vreg_base + i;
+ StackReference<mirror::Object>* ref_addr = vreg_base + i;
mirror::Object* ref = ref_addr->AsMirrorPtr();
if (ref != nullptr) {
mirror::Object* new_ref = ref;
@@ -3579,6 +3583,19 @@ class ReferenceMapVisitor : public StackVisitor {
}
}
}
+ } else if (!m->IsStatic() && !m->IsRuntimeMethod() && m->IsProxyMethod()) {
+ // If this is a non-static proxy method, visit its target (`this` object).
+ DCHECK(!m->IsNative());
+ StackReference<mirror::Object>* ref_addr =
+ artQuickGetProxyThisObjectReference(cur_quick_frame);
+ mirror::Object* ref = ref_addr->AsMirrorPtr();
+ if (ref != nullptr) {
+ mirror::Object* new_ref = ref;
+ visitor_(&new_ref, -1, this);
+ if (ref != new_ref) {
+ ref_addr->Assign(new_ref);
+ }
+ }
}
}
@@ -3773,9 +3790,9 @@ void Thread::VisitRoots(RootVisitor* visitor) {
void Thread::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
if ((flags & VisitRootFlags::kVisitRootFlagPrecise) != 0) {
- VisitRoots<true>(visitor);
+ VisitRoots</* kPrecise */ true>(visitor);
} else {
- VisitRoots<false>(visitor);
+ VisitRoots</* kPrecise */ false>(visitor);
}
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index afb3224944..66e578f312 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3899,21 +3899,13 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
}
ObjPtr<mirror::Class> klass = klass_type.GetClass();
const RegType& referrer = GetDeclaringClass();
- auto* cl = Runtime::Current()->GetClassLinker();
- auto pointer_size = cl->GetImagePointerSize();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx, pointer_size);
if (res_method == nullptr) {
- // Try to find the method with the appropriate lookup for the klass type (interface or not).
- // If this lookup does not match `method_type`, errors shall be reported below.
- if (klass->IsInterface()) {
- res_method = klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
- } else {
- res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
- }
- if (res_method != nullptr) {
- dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
- }
+ res_method = class_linker->FindResolvedMethod(
+ klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx);
}
// Record result of method resolution attempt. The klass resolution has recorded whether
@@ -4432,6 +4424,8 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) {
expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name);
} else if (klass == mirror::VarHandle::StaticClass()) {
expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name);
+ // TODO: add compiler support for VarHandle accessor methods (b/71781600)
+ Fail(VERIFY_ERROR_FORCE_INTERPRETER);
} else {
Fail(VERIFY_ERROR_BAD_CLASS_HARD)
<< "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor();
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 5564684c4f..30aefede20 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -278,7 +278,7 @@ RegTypeCache::RegTypeCache(bool can_load_classes, ScopedArenaAllocator& allocato
klass_entries_(allocator.Adapter(kArenaAllocVerifier)),
can_load_classes_(can_load_classes),
allocator_(allocator) {
- DCHECK(can_suspend || !can_load_classes) << "Cannot load classes is suspension is disabled!";
+ DCHECK(can_suspend || !can_load_classes) << "Cannot load classes if suspension is disabled!";
if (kIsDebugBuild && can_suspend) {
Thread::Current()->AssertThreadSuspensionIsAllowable(gAborting == 0);
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 5fe10f5c12..67ea64be74 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -72,6 +72,7 @@ jclass WellKnownClasses::java_lang_System;
jclass WellKnownClasses::java_lang_Thread;
jclass WellKnownClasses::java_lang_ThreadGroup;
jclass WellKnownClasses::java_lang_Throwable;
+jclass WellKnownClasses::java_nio_ByteBuffer;
jclass WellKnownClasses::java_nio_DirectByteBuffer;
jclass WellKnownClasses::java_util_ArrayList;
jclass WellKnownClasses::java_util_Collections;
@@ -142,6 +143,11 @@ jfieldID WellKnownClasses::java_lang_Throwable_stackState;
jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions;
jfieldID WellKnownClasses::java_lang_reflect_Executable_artMethod;
jfieldID WellKnownClasses::java_lang_reflect_Proxy_h;
+jfieldID WellKnownClasses::java_nio_ByteBuffer_address;
+jfieldID WellKnownClasses::java_nio_ByteBuffer_hb;
+jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
+jfieldID WellKnownClasses::java_nio_ByteBuffer_limit;
+jfieldID WellKnownClasses::java_nio_ByteBuffer_offset;
jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity;
jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress;
jfieldID WellKnownClasses::java_util_ArrayList_array;
@@ -277,7 +283,27 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) {
}
#undef STRING_INIT_LIST
+class ScopedHiddenApiExemption {
+ public:
+ explicit ScopedHiddenApiExemption(Runtime* runtime)
+ : runtime_(runtime),
+ initially_enabled_(runtime_->AreHiddenApiChecksEnabled()) {
+ runtime_->SetHiddenApiChecksEnabled(false);
+ }
+
+ ~ScopedHiddenApiExemption() {
+ runtime_->SetHiddenApiChecksEnabled(initially_enabled_);
+ }
+
+ private:
+ Runtime* runtime_;
+ const bool initially_enabled_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption);
+};
+
void WellKnownClasses::Init(JNIEnv* env) {
+ ScopedHiddenApiExemption hiddenapi_exemption(Runtime::Current());
+
dalvik_annotation_optimization_CriticalNative =
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
@@ -318,6 +344,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_Thread = CacheClass(env, "java/lang/Thread");
java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
+ java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer");
java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
java_util_ArrayList = CacheClass(env, "java/util/ArrayList");
java_util_Collections = CacheClass(env, "java/util/Collections");
@@ -379,6 +406,11 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
java_lang_reflect_Executable_artMethod = CacheField(env, java_lang_reflect_Executable, false, "artMethod", "J");
+ java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J");
+ java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B");
+ java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
+ java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I");
+ java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I");
java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I");
java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "address", "J");
java_util_ArrayList_array = CacheField(env, java_util_ArrayList, false, "elementData", "[Ljava/lang/Object;");
@@ -462,6 +494,7 @@ void WellKnownClasses::Clear() {
java_lang_Throwable = nullptr;
java_util_ArrayList = nullptr;
java_util_Collections = nullptr;
+ java_nio_ByteBuffer = nullptr;
java_nio_DirectByteBuffer = nullptr;
libcore_reflect_AnnotationFactory = nullptr;
libcore_reflect_AnnotationMember = nullptr;
@@ -530,6 +563,11 @@ void WellKnownClasses::Clear() {
java_lang_Throwable_stackTrace = nullptr;
java_lang_Throwable_stackState = nullptr;
java_lang_Throwable_suppressedExceptions = nullptr;
+ java_nio_ByteBuffer_address = nullptr;
+ java_nio_ByteBuffer_hb = nullptr;
+ java_nio_ByteBuffer_isReadOnly = nullptr;
+ java_nio_ByteBuffer_limit = nullptr;
+ java_nio_ByteBuffer_offset = nullptr;
java_nio_DirectByteBuffer_capacity = nullptr;
java_nio_DirectByteBuffer_effectiveDirectAddress = nullptr;
java_util_ArrayList_array = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 9e0b079b7b..d5d7033132 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -85,6 +85,7 @@ struct WellKnownClasses {
static jclass java_lang_Throwable;
static jclass java_util_ArrayList;
static jclass java_util_Collections;
+ static jclass java_nio_ByteBuffer;
static jclass java_nio_DirectByteBuffer;
static jclass libcore_reflect_AnnotationFactory;
static jclass libcore_reflect_AnnotationMember;
@@ -153,8 +154,14 @@ struct WellKnownClasses {
static jfieldID java_lang_Throwable_stackTrace;
static jfieldID java_lang_Throwable_stackState;
static jfieldID java_lang_Throwable_suppressedExceptions;
+ static jfieldID java_nio_ByteBuffer_address;
+ static jfieldID java_nio_ByteBuffer_hb;
+ static jfieldID java_nio_ByteBuffer_isReadOnly;
+ static jfieldID java_nio_ByteBuffer_limit;
+ static jfieldID java_nio_ByteBuffer_offset;
static jfieldID java_nio_DirectByteBuffer_capacity;
static jfieldID java_nio_DirectByteBuffer_effectiveDirectAddress;
+
static jfieldID java_util_ArrayList_array;
static jfieldID java_util_ArrayList_size;
static jfieldID java_util_Collections_EMPTY_LIST;