diff options
248 files changed, 3333 insertions, 1474 deletions
diff --git a/Android.bp b/Android.bp index 59f7abf138..34a646915f 100644 --- a/Android.bp +++ b/Android.bp @@ -15,6 +15,7 @@ art_static_dependencies = [ "libbase", "liblz4", "liblzma", + "libmetricslogger_static", ] subdirs = [ diff --git a/Android.mk b/Android.mk index 64b9400f66..d6472be895 100644 --- a/Android.mk +++ b/Android.mk @@ -110,22 +110,33 @@ TEST_ART_ADB_ROOT_AND_REMOUNT := \ # Sync test files to the target, depends upon all things that must be pushed to the target. .PHONY: test-art-target-sync -# Check if we need to sync. In case ART_TEST_ANDROID_ROOT is not empty, -# the code below uses 'adb push' instead of 'adb sync', which does not -# check if the files on the device have changed. +# Check if we need to sync. In case ART_TEST_CHROOT or ART_TEST_ANDROID_ROOT +# is not empty, the code below uses 'adb push' instead of 'adb sync', +# which does not check if the files on the device have changed. +# TODO: Remove support for ART_TEST_ANDROID_ROOT when it is no longer needed. ifneq ($(ART_TEST_NO_SYNC),true) +# Sync system and data partitions. ifeq ($(ART_TEST_ANDROID_ROOT),) +ifeq ($(ART_TEST_CHROOT),) test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) adb sync system && adb sync data else test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) $(TEST_ART_ADB_ROOT_AND_REMOUNT) - adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT) -# Push the contents of the `data` dir into `/data` on the device. If -# `/data` already exists on the device, it is not overwritten, but its -# contents are updated. - adb push $(PRODUCT_OUT)/data / + adb wait-for-device + adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/ + adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/ +endif +else +test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS) + $(TEST_ART_ADB_ROOT_AND_REMOUNT) + adb wait-for-device + adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT) +# Push the contents of the `data` dir into `$(ART_TEST_CHROOT)/data` on the device (note +# that $(ART_TEST_CHROOT) can be empty). If `$(ART_TEST_CHROOT)/data` already exists on +# the device, it is not overwritten, but its content is updated. + adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/ endif endif diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index 4c2d4d72d8..8cd0d8bc9f 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -23,8 +23,8 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "mirror/throwable.h" #include "nativehelper/ScopedLocalRef.h" #include "runtime-inl.h" diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc index 7e0a5362c9..2f38b78eaf 100644 --- a/benchmark/jobject-benchmark/jobject_benchmark.cc +++ b/benchmark/jobject-benchmark/jobject_benchmark.cc @@ -16,7 +16,7 @@ #include "jni.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index b46f6771ea..3daaf0156e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -472,17 +472,32 @@ define define-art-gtest-rule-target $$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe) +ifeq ($(ART_TEST_CHROOT),) +# Non-chroot configuration. +maybe_art_test_chroot := +maybe_chroot_command := +else +# Chroot configuration. +maybe_art_test_chroot := $(ART_TEST_CHROOT)/ +maybe_chroot_command := chroot $(ART_TEST_CHROOT) +endif + +# File witnessing the success of the gtest, the presence of which means the gtest's success. +gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID + +$$(gtest_rule): GTEST_WITNESS := $$(gtest_witness) + .PHONY: $$(gtest_rule) $$(gtest_rule): test-art-target-sync - $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $$(GTEST_WITNESS) + $(hide) adb shell rm $$(GTEST_WITNESS) + $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \ - && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \ - && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \ - && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(GTEST_WITNESS)" \ + && (adb pull $$(GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -490,21 +505,27 @@ $$(gtest_rule): test-art-target-sync ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule) ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule) +# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's +# success. +valgrind_gtest_witness := \ + $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID + +valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness) + .PHONY: valgrind-$$(gtest_rule) valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync - $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID - $(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE) + $(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS) + $(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS) + $(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE) $(hide) $$(call ART_TEST_SKIP,$$@) && \ - (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ + (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \ - $$$$ANDROID_ROOT/bin/valgrind \ + $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \ --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \ --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \ - && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \ - && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \ - && $$(call ART_TEST_PASSED,$$@)) \ + && touch $$(VALGRIND_GTEST_WITNESS)" \ + && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@)) $(hide) rm -f /tmp/$$@-$$$$PPID @@ -514,10 +535,13 @@ valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-syn ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) # Clear locally defined variables. - valgrind_gtest_rule := - gtest_rule := - gtest_exe := + valgrind_gtest_witness := + gtest_witness := + maybe_chroot_command := + maybe_art_test_chroot := gtest_target_exe := + gtest_exe := + gtest_rule := endef # define-art-gtest-rule-target ART_VALGRIND_DEPENDENCIES := \ @@ -595,10 +619,9 @@ valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIE ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule) # Clear locally defined variables. - valgrind_gtest_rule := - gtest_rule := - gtest_exe := gtest_deps := + gtest_exe := + gtest_rule := endef # define-art-gtest-rule-host # Define the rules to build and run host and target gtests. diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 5a25a6ceb6..48da7551a7 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -643,6 +643,16 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> return Result::SuccessNoValue(); } + if (option == "profile-aot-code") { + existing.profile_aot_code_ = true; + return Result::SuccessNoValue(); + } + + if (option == "save-without-jit-notifications") { + existing.wait_for_jit_notifications_to_save_ = false; + return Result::SuccessNoValue(); + } + // The rest of these options are always the wildcard from '-Xps-*' std::string suffix = RemovePrefix(option); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fbbb4e960f..39ed825001 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -57,7 +57,7 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "intrinsics_enum.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linker/linker_patch.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 2d82d79c4a..933be4f004 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -51,6 +51,7 @@ CompilerOptions::CompilerOptions() implicit_suspend_checks_(false), compile_pic_(false), dump_timings_(false), + dump_pass_timings_(false), dump_stats_(false), verbose_methods_(), abort_on_hard_verifier_failure_(false), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index cdd9d4de00..cee989b315 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -270,6 +270,10 @@ class CompilerOptions FINAL { return dump_timings_; } + bool GetDumpPassTimings() const { + return dump_pass_timings_; + } + bool GetDumpStats() const { return dump_stats_; } @@ -316,6 +320,7 @@ class CompilerOptions FINAL { bool implicit_suspend_checks_; bool compile_pic_; bool dump_timings_; + bool dump_pass_timings_; bool dump_stats_; // Vector of methods to have verbose output enabled for. diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index 3b18db09fc..32fc887b8e 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -85,6 +85,10 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string options->dump_timings_ = true; } + if (map.Exists(Base::DumpPassTimings)) { + options->dump_pass_timings_ = true; + } + if (map.Exists(Base::DumpStats)) { options->dump_stats_ = true; } @@ -146,6 +150,9 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .Define({"--dump-timings"}) .IntoKey(Map::DumpTimings) + .Define({"--dump-pass-timings"}) + .IntoKey(Map::DumpPassTimings) + .Define({"--dump-stats"}) .IntoKey(Map::DumpStats) diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index acddae7299..529d43fc72 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -60,6 +60,7 @@ COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode) COMPILER_OPTIONS_KEY (Unit, DumpTimings) +COMPILER_OPTIONS_KEY (Unit, DumpPassTimings) COMPILER_OPTIONS_KEY (Unit, DumpStats) #undef COMPILER_OPTIONS_KEY diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 730a1a63e8..c643af787d 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -28,8 +28,8 @@ #include "dex/dex_file.h" #include "gtest/gtest.h" #include "indirect_reference_table.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 8cb1998f7f..0902bf2bce 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -37,7 +37,7 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #include "thread.h" #include "utils/arm/managed_register_arm.h" #include "utils/arm64/managed_register_arm64.h" diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 231017f55e..f57333741c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -736,6 +736,46 @@ void CodeGenerator::GenerateLoadClassRuntimeCall(HLoadClass* cls) { } } +void CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary( + HLoadMethodHandle* method_handle, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_handle->InputCount(), 1u); + LocationSummary* locations = + new (method_handle->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_handle, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle) { + LocationSummary* locations = method_handle->GetLocations(); + MoveConstant(locations->GetTemp(0), method_handle->GetMethodHandleIndex()); + CheckEntrypointTypes<kQuickResolveMethodHandle, void*, uint32_t>(); + InvokeRuntime(kQuickResolveMethodHandle, method_handle, method_handle->GetDexPc()); +} + +void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( + HLoadMethodType* method_type, + Location runtime_proto_index_location, + Location runtime_return_location) { + DCHECK_EQ(method_type->InputCount(), 1u); + LocationSummary* locations = + new (method_type->GetBlock()->GetGraph()->GetAllocator()) LocationSummary( + method_type, LocationSummary::kCallOnMainOnly); + locations->SetInAt(0, Location::NoLocation()); + locations->AddTemp(runtime_proto_index_location); + locations->SetOut(runtime_return_location); +} + +void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) { + LocationSummary* locations = method_type->GetLocations(); + MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex()); + CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>(); + InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc()); +} + static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) { Runtime* runtime = Runtime::Current(); DCHECK(runtime->IsAotCompiler()); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index f0c4ee01cc..bcb25997f4 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -564,6 +564,16 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { Location runtime_return_location); void GenerateLoadClassRuntimeCall(HLoadClass* cls); + static void CreateLoadMethodHandleRuntimeCallLocationSummary(HLoadMethodHandle* method_handle, + Location runtime_handle_index_location, + Location runtime_return_location); + void GenerateLoadMethodHandleRuntimeCall(HLoadMethodHandle* method_handle); + + static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type, + Location runtime_type_index_location, + Location runtime_return_location); + void GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type); + uint32_t GetBootImageOffset(HLoadClass* load_class); uint32_t GetBootImageOffset(HLoadString* load_string); uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index d4cfab82de..6f173e19f5 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -5144,6 +5144,26 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA } } +void LocationsBuilderARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARM64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static MemOperand GetExceptionTlsAddress() { return MemOperand(tr, Thread::ExceptionOffset<kArm64PointerSize>().Int32Value()); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 58ce9aa9f0..859e1597c6 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7527,6 +7527,26 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ } } +void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + Location location = LocationFrom(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 25e2eddbfa..7f3441fdf4 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -8226,6 +8226,26 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF } } +void LocationsBuilderMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset<kMipsPointerSize>().Int32Value(); } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 5b07b55cbb..ee32b96daf 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -6262,6 +6262,26 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S } } +void LocationsBuilderMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc); +} + +void InstructionCodeGeneratorMIPS64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + static int32_t GetExceptionTlsOffset() { return Thread::ExceptionOffset<kMips64PointerSize>().Int32Value(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 82d1fda878..9e315381b1 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6539,6 +6539,26 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE } } +void LocationsBuilderX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) { + InvokeRuntimeCallingConvention calling_convention; + Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0)); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) { LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 322b0cfc4c..f7397046d7 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5908,6 +5908,26 @@ void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) { } } +void LocationsBuilderX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodHandle(HLoadMethodHandle* load) { + codegen_->GenerateLoadMethodHandleRuntimeCall(load); +} + +void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) { + // Custom calling convention: RAX serves as both input and output. + Location location = Location::RegisterLocation(RAX); + CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location); +} + +void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) { + codegen_->GenerateLoadMethodTypeRuntimeCall(load); +} + void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) { // We assume the class to not be null. SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathX86_64( diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 54d4644580..d65ad40565 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -386,6 +386,18 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << load_class->NeedsAccessCheck() << std::noboolalpha; } + void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + StartAttributeStream("method_handle_index") << load_method_handle->GetMethodHandleIndex(); + } + + void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE { + StartAttributeStream("load_kind") << "RuntimeCall"; + const DexFile& dex_file = load_method_type->GetDexFile(); + const DexFile::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex()); + StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id); + } + void VisitLoadString(HLoadString* load_string) OVERRIDE { StartAttributeStream("load_kind") << load_string->GetLoadKind(); } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 9647dd5d41..35a39456a2 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1896,6 +1896,20 @@ bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) } } +void HInstructionBuilder::BuildLoadMethodHandle(uint16_t proto_idx, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodHandle* load_method_handle = + new (allocator_) HLoadMethodHandle(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + AppendInstruction(load_method_handle); +} + +void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) { + const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); + HLoadMethodType* load_method_type = + new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + AppendInstruction(load_method_type); +} + void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t destination, uint8_t reference, @@ -2927,6 +2941,20 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::CONST_METHOD_HANDLE: { + uint16_t method_handle_idx = instruction.VRegB_21c(); + BuildLoadMethodHandle(method_handle_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + + case Instruction::CONST_METHOD_TYPE: { + uint16_t proto_idx = instruction.VRegB_21c(); + BuildLoadMethodType(proto_idx, dex_pc); + UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); + break; + } + case Instruction::MOVE_EXCEPTION: { AppendInstruction(new (allocator_) HLoadException(dex_pc)); UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction()); diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f78829232d..95ffa6b054 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -45,6 +45,7 @@ class VariableSizedHandleScope; namespace mirror { class Class; +class MethodType; } // namespace mirror class HInstructionBuilder : public ValueObject { @@ -239,6 +240,12 @@ class HInstructionBuilder : public ValueObject { bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); + // Builds a `HLoadMethodHandle` loading the given `method_handle_idx`. + void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc); + + // Builds a `HLoadMethodType` loading the given `proto_idx`. + void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc); + // Returns the outer-most compiling method's class. ObjPtr<mirror::Class> GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 0fe16725f3..ca84d421a7 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2666,10 +2666,10 @@ bool InstructionSimplifierVisitor::TryHandleAssociativeAndCommutativeOperation( HConstant* const2; HBinaryOperation* y; - if (instruction->InstructionTypeEquals(left) && right->IsConstant()) { + if (instruction->GetKind() == left->GetKind() && right->IsConstant()) { const2 = right->AsConstant(); y = left->AsBinaryOperation(); - } else if (left->IsConstant() && instruction->InstructionTypeEquals(right)) { + } else if (left->IsConstant() && instruction->GetKind() == right->GetKind()) { const2 = left->AsConstant(); y = right->AsBinaryOperation(); } else { diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f784f8f7f3..5f2833e9a6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1680,10 +1680,9 @@ bool HCondition::IsBeforeWhenDisregardMoves(HInstruction* instruction) const { } bool HInstruction::Equals(const HInstruction* other) const { - if (!InstructionTypeEquals(other)) return false; - DCHECK_EQ(GetKind(), other->GetKind()); - if (!InstructionDataEquals(other)) return false; + if (GetKind() != other->GetKind()) return false; if (GetType() != other->GetType()) return false; + if (!InstructionDataEquals(other)) return false; HConstInputsRef inputs = GetInputs(); HConstInputsRef other_inputs = other->GetInputs(); if (inputs.size() != other_inputs.size()) return false; @@ -1698,7 +1697,7 @@ bool HInstruction::Equals(const HInstruction* other) const { std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs) { #define DECLARE_CASE(type, super) case HInstruction::k##type: os << #type; break; switch (rhs) { - FOR_EACH_INSTRUCTION(DECLARE_CASE) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_CASE) default: os << "Unknown instruction kind " << static_cast<int>(rhs); break; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 79d733060b..a7c2d0b125 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -41,6 +41,7 @@ #include "intrinsics_enum.h" #include "locations.h" #include "mirror/class.h" +#include "mirror/method_type.h" #include "offsets.h" #include "utils/intrusive_forward_list.h" @@ -1382,6 +1383,8 @@ class HLoopInformationOutwardIterator : public ValueObject { M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ M(LoadException, Instruction) \ + M(LoadMethodHandle, Instruction) \ + M(LoadMethodType, Instruction) \ M(LoadString, Instruction) \ M(LongConstant, Constant) \ M(Max, Instruction) \ @@ -1525,9 +1528,6 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) H##type& operator=(const H##type&) = delete; \ public: \ const char* DebugName() const OVERRIDE { return #type; } \ - bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \ - return other->Is##type(); \ - } \ HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE { \ DCHECK(IsClonable()); \ return new (arena) H##type(*this->As##type()); \ @@ -1537,10 +1537,7 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #define DECLARE_ABSTRACT_INSTRUCTION(type) \ private: \ H##type& operator=(const H##type&) = delete; \ - public: \ - bool Is##type() const { return As##type() != nullptr; } \ - const H##type* As##type() const { return this; } \ - H##type* As##type() { return this; } + public: #define DEFAULT_COPY_CONSTRUCTOR(type) \ explicit H##type(const H##type& other) = default; @@ -1959,12 +1956,15 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { public: #define DECLARE_KIND(type, super) k##type, enum InstructionKind { - FOR_EACH_INSTRUCTION(DECLARE_KIND) + FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_KIND) kLastInstructionKind }; #undef DECLARE_KIND HInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) + : HInstruction(kind, DataType::Type::kVoid, side_effects, dex_pc) {} + + HInstruction(InstructionKind kind, DataType::Type type, SideEffects side_effects, uint32_t dex_pc) : previous_(nullptr), next_(nullptr), block_(nullptr), @@ -1979,6 +1979,7 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { side_effects_(side_effects), reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { SetPackedField<InstructionKindField>(kind); + SetPackedField<TypeField>(type); SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact()); } @@ -2036,7 +2037,9 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { virtual void Accept(HGraphVisitor* visitor) = 0; virtual const char* DebugName() const = 0; - virtual DataType::Type GetType() const { return DataType::Type::kVoid; } + DataType::Type GetType() const { + return TypeField::Decode(GetPackedFields()); + } virtual bool NeedsEnvironment() const { return false; } @@ -2228,19 +2231,17 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { void MoveBeforeFirstUserAndOutOfLoops(); #define INSTRUCTION_TYPE_CHECK(type, super) \ - bool Is##type() const; \ - const H##type* As##type() const; \ - H##type* As##type(); + bool Is##type() const; - FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK -#define INSTRUCTION_TYPE_CHECK(type, super) \ - bool Is##type() const { return (As##type() != nullptr); } \ - virtual const H##type* As##type() const { return nullptr; } \ - virtual H##type* As##type() { return nullptr; } - FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) -#undef INSTRUCTION_TYPE_CHECK +#define INSTRUCTION_TYPE_CAST(type, super) \ + const H##type* As##type() const; \ + H##type* As##type(); + + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST) +#undef INSTRUCTION_TYPE_CAST // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for @@ -2266,11 +2267,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // meanings? split and rename? virtual bool CanBeMoved() const { return false; } - // Returns whether the two instructions are of the same kind. - virtual bool InstructionTypeEquals(const HInstruction* other ATTRIBUTE_UNUSED) const { - return false; - } - // Returns whether any data encoded in the two instructions is equal. // This method does not look at the inputs. Both instructions must be // of the same type, otherwise the method has undefined behavior. @@ -2342,13 +2338,18 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { static constexpr size_t kFieldInstructionKind = kFlagReferenceTypeIsExact + 1; static constexpr size_t kFieldInstructionKindSize = MinimumBitsToStore(static_cast<size_t>(InstructionKind::kLastInstructionKind - 1)); - static constexpr size_t kNumberOfGenericPackedBits = + static constexpr size_t kFieldType = kFieldInstructionKind + kFieldInstructionKindSize; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast)); + static constexpr size_t kNumberOfGenericPackedBits = kFieldType + kFieldTypeSize; static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; static_assert(kNumberOfGenericPackedBits <= kMaxNumberOfPackedBits, "Too many generic packed fields"); + using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>; + const HUserRecord<HInstruction*> InputRecordAt(size_t i) const { return GetInputRecords()[i]; } @@ -2595,6 +2596,15 @@ class HVariableInputSizeInstruction : public HInstruction { ArenaAllocKind kind) : HInstruction(inst_kind, side_effects, dex_pc), inputs_(number_of_inputs, allocator->Adapter(kind)) {} + HVariableInputSizeInstruction(InstructionKind inst_kind, + DataType::Type type, + SideEffects side_effects, + uint32_t dex_pc, + ArenaAllocator* allocator, + size_t number_of_inputs, + ArenaAllocKind kind) + : HInstruction(inst_kind, type, side_effects, dex_pc), + inputs_(number_of_inputs, allocator->Adapter(kind)) {} DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction); @@ -2602,11 +2612,16 @@ class HVariableInputSizeInstruction : public HInstruction { }; template<size_t N> -class HTemplateInstruction: public HInstruction { +class HExpression : public HInstruction { public: - HTemplateInstruction<N>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) + HExpression<N>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) : HInstruction(kind, side_effects, dex_pc), inputs_() {} - virtual ~HTemplateInstruction() {} + HExpression<N>(InstructionKind kind, + DataType::Type type, + SideEffects side_effects, + uint32_t dex_pc) + : HInstruction(kind, type, side_effects, dex_pc), inputs_() {} + virtual ~HExpression() {} using HInstruction::GetInputRecords; // Keep the const version visible. ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { @@ -2614,7 +2629,7 @@ class HTemplateInstruction: public HInstruction { } protected: - DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<N>); + DEFAULT_COPY_CONSTRUCTOR(Expression<N>); private: std::array<HUserRecord<HInstruction*>, N> inputs_; @@ -2622,14 +2637,13 @@ class HTemplateInstruction: public HInstruction { friend class SsaBuilder; }; -// HTemplateInstruction specialization for N=0. +// HExpression specialization for N=0. template<> -class HTemplateInstruction<0>: public HInstruction { +class HExpression<0> : public HInstruction { public: - explicit HTemplateInstruction<0>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc) - : HInstruction(kind, side_effects, dex_pc) {} + using HInstruction::HInstruction; - virtual ~HTemplateInstruction() {} + virtual ~HExpression() {} using HInstruction::GetInputRecords; // Keep the const version visible. ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { @@ -2637,46 +2651,18 @@ class HTemplateInstruction<0>: public HInstruction { } protected: - DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>); + DEFAULT_COPY_CONSTRUCTOR(Expression<0>); private: friend class SsaBuilder; }; -template<intptr_t N> -class HExpression : public HTemplateInstruction<N> { - public: - using HInstruction::InstructionKind; - HExpression<N>(InstructionKind kind, - DataType::Type type, - SideEffects side_effects, - uint32_t dex_pc) - : HTemplateInstruction<N>(kind, side_effects, dex_pc) { - this->template SetPackedField<TypeField>(type); - } - virtual ~HExpression() {} - - DataType::Type GetType() const OVERRIDE { - return TypeField::Decode(this->GetPackedFields()); - } - - protected: - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = - MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast)); - static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; - static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, - "Too many packed fields."); - using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>; - DEFAULT_COPY_CONSTRUCTOR(Expression<N>); -}; - // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow // instruction that branches to the exit block. -class HReturnVoid FINAL : public HTemplateInstruction<0> { +class HReturnVoid FINAL : public HExpression<0> { public: explicit HReturnVoid(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kReturnVoid, SideEffects::None(), dex_pc) { + : HExpression(kReturnVoid, SideEffects::None(), dex_pc) { } bool IsControlFlow() const OVERRIDE { return true; } @@ -2689,10 +2675,10 @@ class HReturnVoid FINAL : public HTemplateInstruction<0> { // Represents dex's RETURN opcodes. A HReturn is a control flow // instruction that branches to the exit block. -class HReturn FINAL : public HTemplateInstruction<1> { +class HReturn FINAL : public HExpression<1> { public: explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kReturn, SideEffects::None(), dex_pc) { + : HExpression(kReturn, SideEffects::None(), dex_pc) { SetRawInputAt(0, value); } @@ -2713,13 +2699,13 @@ class HPhi FINAL : public HVariableInputSizeInstruction { uint32_t dex_pc = kNoDexPc) : HVariableInputSizeInstruction( kPhi, + ToPhiType(type), SideEffects::None(), dex_pc, allocator, number_of_inputs, kArenaAllocPhiInputs), reg_number_(reg_number) { - SetPackedField<TypeField>(ToPhiType(type)); DCHECK_NE(GetType(), DataType::Type::kVoid); // Phis are constructed live and marked dead if conflicting or unused. // Individual steps of SsaBuilder should assume that if a phi has been @@ -2737,7 +2723,6 @@ class HPhi FINAL : public HVariableInputSizeInstruction { bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); } - DataType::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); } void SetType(DataType::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), @@ -2796,14 +2781,10 @@ class HPhi FINAL : public HVariableInputSizeInstruction { DEFAULT_COPY_CONSTRUCTOR(Phi); private: - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = - MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast)); - static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagIsLive = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>; const uint32_t reg_number_; }; @@ -2811,10 +2792,10 @@ class HPhi FINAL : public HVariableInputSizeInstruction { // The exit instruction is the only instruction of the exit block. // Instructions aborting the method (HThrow and HReturn) must branch to the // exit block. -class HExit FINAL : public HTemplateInstruction<0> { +class HExit FINAL : public HExpression<0> { public: explicit HExit(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kExit, SideEffects::None(), dex_pc) { + : HExpression(kExit, SideEffects::None(), dex_pc) { } bool IsControlFlow() const OVERRIDE { return true; } @@ -2826,10 +2807,10 @@ class HExit FINAL : public HTemplateInstruction<0> { }; // Jumps from one block to another. -class HGoto FINAL : public HTemplateInstruction<0> { +class HGoto FINAL : public HExpression<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kGoto, SideEffects::None(), dex_pc) { + : HExpression(kGoto, SideEffects::None(), dex_pc) { } bool IsClonable() const OVERRIDE { return true; } @@ -3096,10 +3077,10 @@ class HDoubleConstant FINAL : public HConstant { // Conditional branch. A block ending with an HIf instruction must have // two successors. -class HIf FINAL : public HTemplateInstruction<1> { +class HIf FINAL : public HExpression<1> { public: explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kIf, SideEffects::None(), dex_pc) { + : HExpression(kIf, SideEffects::None(), dex_pc) { SetRawInputAt(0, input); } @@ -3126,7 +3107,7 @@ class HIf FINAL : public HTemplateInstruction<1> { // non-exceptional control flow. // Normal-flow successor is stored at index zero, exception handlers under // higher indices in no particular order. -class HTryBoundary FINAL : public HTemplateInstruction<0> { +class HTryBoundary FINAL : public HExpression<0> { public: enum class BoundaryKind { kEntry, @@ -3135,7 +3116,7 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kTryBoundary, SideEffects::None(), dex_pc) { + : HExpression(kTryBoundary, SideEffects::None(), dex_pc) { SetPackedField<BoundaryKindField>(kind); } @@ -3219,6 +3200,7 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { uint32_t dex_pc) : HVariableInputSizeInstruction( kDeoptimize, + guard->GetType(), SideEffects::CanTriggerGC(), dex_pc, allocator, @@ -3242,10 +3224,6 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { DeoptimizationKind GetDeoptimizationKind() const { return GetPackedField<DeoptimizeKindField>(); } - DataType::Type GetType() const OVERRIDE { - return GuardsAnInput() ? GuardedInput()->GetType() : DataType::Type::kVoid; - } - bool GuardsAnInput() const { return InputCount() == 2; } @@ -3288,6 +3266,7 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { // with regard to other passes. HShouldDeoptimizeFlag(ArenaAllocator* allocator, uint32_t dex_pc) : HVariableInputSizeInstruction(kShouldDeoptimizeFlag, + DataType::Type::kInt32, SideEffects::None(), dex_pc, allocator, @@ -3295,8 +3274,6 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { kArenaAllocCHA) { } - DataType::Type GetType() const OVERRIDE { return DataType::Type::kInt32; } - // We do all CHA guard elimination/motion in a single pass, after which there is no // further guard elimination/motion since a guard might have been used for justification // of the elimination of another guard. Therefore, we pretend this guard cannot be moved @@ -3360,7 +3337,7 @@ class HClassTableGet FINAL : public HExpression<1> { DEFAULT_COPY_CONSTRUCTOR(ClassTableGet); private: - static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTableKindSize = MinimumBitsToStore(static_cast<size_t>(TableKind::kLast)); static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; @@ -3375,13 +3352,13 @@ class HClassTableGet FINAL : public HExpression<1> { // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will // have one successor for each entry in the switch table, and the final successor // will be the block containing the next Dex opcode. -class HPackedSwitch FINAL : public HTemplateInstruction<1> { +class HPackedSwitch FINAL : public HExpression<1> { public: HPackedSwitch(int32_t start_value, uint32_t num_entries, HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kPackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kPackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); @@ -3611,7 +3588,7 @@ class HCondition : public HBinaryOperation { protected: // Needed if we merge a HCompare into a HCondition. - static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits; static constexpr size_t kFieldComparisonBiasSize = MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast)); static constexpr size_t kNumberOfConditionPackedBits = @@ -4131,7 +4108,7 @@ class HCompare FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: - static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits; static constexpr size_t kFieldComparisonBiasSize = MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast)); static constexpr size_t kNumberOfComparePackedBits = @@ -4210,7 +4187,7 @@ class HNewInstance FINAL : public HExpression<1> { DEFAULT_COPY_CONSTRUCTOR(NewInstance); private: - static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -4251,8 +4228,6 @@ class HInvoke : public HVariableInputSizeInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - DataType::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } InvokeType GetInvokeType() const { @@ -4305,16 +4280,11 @@ class HInvoke : public HVariableInputSizeInstruction { static constexpr size_t kFieldInvokeType = kNumberOfGenericPackedBits; static constexpr size_t kFieldInvokeTypeSize = MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType)); - static constexpr size_t kFieldReturnType = - kFieldInvokeType + kFieldInvokeTypeSize; - static constexpr size_t kFieldReturnTypeSize = - MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast)); - static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kFlagCanThrow = kFieldInvokeType + kFieldInvokeTypeSize; static constexpr size_t kFlagAlwaysThrows = kFlagCanThrow + 1; static constexpr size_t kNumberOfInvokePackedBits = kFlagAlwaysThrows + 1; static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using InvokeTypeField = BitField<InvokeType, kFieldInvokeType, kFieldInvokeTypeSize>; - using ReturnTypeField = BitField<DataType::Type, kFieldReturnType, kFieldReturnTypeSize>; HInvoke(InstructionKind kind, ArenaAllocator* allocator, @@ -4327,6 +4297,7 @@ class HInvoke : public HVariableInputSizeInstruction { InvokeType invoke_type) : HVariableInputSizeInstruction( kind, + return_type, SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. dex_pc, allocator, @@ -4337,7 +4308,6 @@ class HInvoke : public HVariableInputSizeInstruction { dex_method_index_(dex_method_index), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { - SetPackedField<ReturnTypeField>(return_type); SetPackedField<InvokeTypeField>(invoke_type); SetPackedFlag<kFlagCanThrow>(true); } @@ -4550,7 +4520,7 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { } bool CanBeNull() const OVERRIDE { - return GetPackedField<ReturnTypeField>() == DataType::Type::kReference && !IsStringInit(); + return GetType() == DataType::Type::kReference && !IsStringInit(); } // Get the index of the special input, if any. @@ -5146,8 +5116,6 @@ class HDivZeroCheck FINAL : public HExpression<1> { SetRawInputAt(0, value); } - DataType::Type GetType() const OVERRIDE { return InputAt(0)->GetType(); } - bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -5500,7 +5468,7 @@ class HParameterValue FINAL : public HExpression<0> { private: // Whether or not the parameter value corresponds to 'this' argument. - static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsThis = kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, @@ -5742,7 +5710,7 @@ class HInstanceFieldGet FINAL : public HExpression<1> { const FieldInfo field_info_; }; -class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { +class HInstanceFieldSet FINAL : public HExpression<2> { public: HInstanceFieldSet(HInstruction* object, HInstruction* value, @@ -5754,9 +5722,9 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { uint16_t declaring_class_def_index, const DexFile& dex_file, uint32_t dex_pc) - : HTemplateInstruction(kInstanceFieldSet, - SideEffects::FieldWriteOfType(field_type, is_volatile), - dex_pc), + : HExpression(kInstanceFieldSet, + SideEffects::FieldWriteOfType(field_type, is_volatile), + dex_pc), field_info_(field, field_offset, field_type, @@ -5882,13 +5850,13 @@ class HArrayGet FINAL : public HExpression<2> { // a particular HArrayGet is actually a String.charAt() by looking at the type // of the input but that requires holding the mutator lock, so we prefer to use // a flag, so that code generators don't need to do the locking. - static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); }; -class HArraySet FINAL : public HTemplateInstruction<3> { +class HArraySet FINAL : public HExpression<3> { public: HArraySet(HInstruction* array, HInstruction* index, @@ -5910,7 +5878,7 @@ class HArraySet FINAL : public HTemplateInstruction<3> { DataType::Type expected_component_type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction(kArraySet, side_effects, dex_pc) { + : HExpression(kArraySet, side_effects, dex_pc) { SetPackedField<ExpectedComponentTypeField>(expected_component_type); SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == DataType::Type::kReference); SetPackedFlag<kFlagValueCanBeNull>(true); @@ -6039,7 +6007,7 @@ class HArrayLength FINAL : public HExpression<1> { // determine whether a particular HArrayLength is actually a String.length() by // looking at the type of the input but that requires holding the mutator lock, so // we prefer to use a flag, so that code generators don't need to do the locking. - static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringLength = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); @@ -6080,13 +6048,13 @@ class HBoundsCheck FINAL : public HExpression<2> { DEFAULT_COPY_CONSTRUCTOR(BoundsCheck); private: - static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagIsStringCharAt = kNumberOfGenericPackedBits; }; -class HSuspendCheck FINAL : public HTemplateInstruction<0> { +class HSuspendCheck FINAL : public HExpression<0> { public: explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc), + : HExpression(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) { } @@ -6112,10 +6080,10 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { // Pseudo-instruction which provides the native debugger with mapping information. // It ensures that we can generate line number and local variables at this point. -class HNativeDebugInfo : public HTemplateInstruction<0> { +class HNativeDebugInfo : public HExpression<0> { public: explicit HNativeDebugInfo(uint32_t dex_pc) - : HTemplateInstruction<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) { + : HExpression<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) { } bool NeedsEnvironment() const OVERRIDE { @@ -6174,7 +6142,10 @@ class HLoadClass FINAL : public HInstruction { bool is_referrers_class, uint32_t dex_pc, bool needs_access_check) - : HInstruction(kLoadClass, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(kLoadClass, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), special_input_(HUserRecord<HInstruction*>(current_method)), type_index_(type_index), dex_file_(dex_file), @@ -6285,10 +6256,6 @@ class HLoadClass FINAL : public HInstruction { &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - DataType::Type GetType() const OVERRIDE { - return DataType::Type::kReference; - } - Handle<mirror::Class> GetClass() const { return klass_; } @@ -6399,7 +6366,10 @@ class HLoadString FINAL : public HInstruction { dex::StringIndex string_index, const DexFile& dex_file, uint32_t dex_pc) - : HInstruction(kLoadString, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(kLoadString, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), special_input_(HUserRecord<HInstruction*>(current_method)), string_index_(string_index), dex_file_(dex_file) { @@ -6474,10 +6444,6 @@ class HLoadString FINAL : public HInstruction { &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); } - DataType::Type GetType() const OVERRIDE { - return DataType::Type::kReference; - } - DECLARE_INSTRUCTION(LoadString); protected: @@ -6535,6 +6501,94 @@ inline void HLoadString::AddSpecialInput(HInstruction* special_input) { special_input->AddUseAt(this, 0); } +class HLoadMethodHandle FINAL : public HInstruction { + public: + HLoadMethodHandle(HCurrentMethod* current_method, + uint16_t method_handle_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodHandle, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord<HInstruction*>(current_method)), + method_handle_idx_(method_handle_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetMethodHandleIndex() const { return method_handle_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodHandle); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodHandle); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord<HInstruction*> special_input_; + + const uint16_t method_handle_idx_; + const DexFile& dex_file_; +}; + +class HLoadMethodType FINAL : public HInstruction { + public: + HLoadMethodType(HCurrentMethod* current_method, + uint16_t proto_idx, + const DexFile& dex_file, + uint32_t dex_pc) + : HInstruction(kLoadMethodType, + DataType::Type::kReference, + SideEffectsForArchRuntimeCalls(), + dex_pc), + special_input_(HUserRecord<HInstruction*>(current_method)), + proto_idx_(proto_idx), + dex_file_(dex_file) { + } + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + bool IsClonable() const OVERRIDE { return true; } + + uint16_t GetProtoIndex() const { return proto_idx_; } + + const DexFile& GetDexFile() const { return dex_file_; } + + static SideEffects SideEffectsForArchRuntimeCalls() { + return SideEffects::CanTriggerGC(); + } + + DECLARE_INSTRUCTION(LoadMethodType); + + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadMethodType); + + private: + // The special input is the HCurrentMethod for kRuntimeCall. + HUserRecord<HInstruction*> special_input_; + + const uint16_t proto_idx_; + const DexFile& dex_file_; +}; + /** * Performs an initialization check on its Class object input. */ @@ -6633,7 +6687,7 @@ class HStaticFieldGet FINAL : public HExpression<1> { const FieldInfo field_info_; }; -class HStaticFieldSet FINAL : public HTemplateInstruction<2> { +class HStaticFieldSet FINAL : public HExpression<2> { public: HStaticFieldSet(HInstruction* cls, HInstruction* value, @@ -6645,9 +6699,9 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { uint16_t declaring_class_def_index, const DexFile& dex_file, uint32_t dex_pc) - : HTemplateInstruction(kStaticFieldSet, - SideEffects::FieldWriteOfType(field_type, is_volatile), - dex_pc), + : HExpression(kStaticFieldSet, + SideEffects::FieldWriteOfType(field_type, is_volatile), + dex_pc), field_info_(field, field_offset, field_type, @@ -6714,16 +6768,14 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { const uint32_t field_index_; }; -class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { +class HUnresolvedInstanceFieldSet FINAL : public HExpression<2> { public: HUnresolvedInstanceFieldSet(HInstruction* obj, HInstruction* value, DataType::Type field_type, uint32_t field_index, uint32_t dex_pc) - : HTemplateInstruction(kUnresolvedInstanceFieldSet, - SideEffects::AllExceptGCDependency(), - dex_pc), + : HExpression(kUnresolvedInstanceFieldSet, SideEffects::AllExceptGCDependency(), dex_pc), field_index_(field_index) { SetPackedField<FieldTypeField>(field_type); DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType())); @@ -6784,15 +6836,13 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { const uint32_t field_index_; }; -class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { +class HUnresolvedStaticFieldSet FINAL : public HExpression<1> { public: HUnresolvedStaticFieldSet(HInstruction* value, DataType::Type field_type, uint32_t field_index, uint32_t dex_pc) - : HTemplateInstruction(kUnresolvedStaticFieldSet, - SideEffects::AllExceptGCDependency(), - dex_pc), + : HExpression(kUnresolvedStaticFieldSet, SideEffects::AllExceptGCDependency(), dex_pc), field_index_(field_index) { SetPackedField<FieldTypeField>(field_type); DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType())); @@ -6841,10 +6891,10 @@ class HLoadException FINAL : public HExpression<0> { // Implicit part of move-exception which clears thread-local exception storage. // Must not be removed because the runtime expects the TLS to get cleared. -class HClearException FINAL : public HTemplateInstruction<0> { +class HClearException FINAL : public HExpression<0> { public: explicit HClearException(uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kClearException, SideEffects::AllWrites(), dex_pc) { + : HExpression(kClearException, SideEffects::AllWrites(), dex_pc) { } DECLARE_INSTRUCTION(ClearException); @@ -6853,10 +6903,10 @@ class HClearException FINAL : public HTemplateInstruction<0> { DEFAULT_COPY_CONSTRUCTOR(ClearException); }; -class HThrow FINAL : public HTemplateInstruction<1> { +class HThrow FINAL : public HExpression<1> { public: HThrow(HInstruction* exception, uint32_t dex_pc) - : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) { + : HExpression(kThrow, SideEffects::CanTriggerGC(), dex_pc) { SetRawInputAt(0, exception); } @@ -6897,6 +6947,7 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); class HTypeCheckInstruction : public HVariableInputSizeInstruction { public: HTypeCheckInstruction(InstructionKind kind, + DataType::Type type, HInstruction* object, HInstruction* target_class_or_null, TypeCheckKind check_kind, @@ -6908,6 +6959,7 @@ class HTypeCheckInstruction : public HVariableInputSizeInstruction { SideEffects side_effects) : HVariableInputSizeInstruction( kind, + type, side_effects, dex_pc, allocator, @@ -7010,6 +7062,7 @@ class HInstanceOf FINAL : public HTypeCheckInstruction { HIntConstant* bitstring_path_to_root, HIntConstant* bitstring_mask) : HTypeCheckInstruction(kInstanceOf, + DataType::Type::kBool, object, target_class_or_null, check_kind, @@ -7020,8 +7073,6 @@ class HInstanceOf FINAL : public HTypeCheckInstruction { bitstring_mask, SideEffectsForArchRuntimeCalls(check_kind)) {} - DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; } - bool NeedsEnvironment() const OVERRIDE { return CanCallRuntime(GetTypeCheckKind()); } @@ -7074,7 +7125,7 @@ class HBoundType FINAL : public HExpression<1> { private: // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this // is false then CanBeNull() cannot be true). - static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagUpperCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -7099,6 +7150,7 @@ class HCheckCast FINAL : public HTypeCheckInstruction { HIntConstant* bitstring_path_to_root, HIntConstant* bitstring_mask) : HTypeCheckInstruction(kCheckCast, + DataType::Type::kVoid, object, target_class_or_null, check_kind, @@ -7148,13 +7200,12 @@ enum MemBarrierKind { }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); -class HMemoryBarrier FINAL : public HTemplateInstruction<0> { +class HMemoryBarrier FINAL : public HExpression<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction( - kMemoryBarrier, - SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays. - dex_pc) { + : HExpression(kMemoryBarrier, + SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays. + dex_pc) { SetPackedField<BarrierKindField>(barrier_kind); } @@ -7331,7 +7382,7 @@ class HConstructorFence FINAL : public HVariableInputSizeInstruction { DEFAULT_COPY_CONSTRUCTOR(ConstructorFence); }; -class HMonitorOperation FINAL : public HTemplateInstruction<1> { +class HMonitorOperation FINAL : public HExpression<1> { public: enum class OperationKind { kEnter, @@ -7340,10 +7391,9 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) - : HTemplateInstruction( - kMonitorOperation, - SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. - dex_pc) { + : HExpression(kMonitorOperation, + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { SetPackedField<OperationKindField>(kind); SetRawInputAt(0, object); } @@ -7493,10 +7543,10 @@ std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs); static constexpr size_t kDefaultNumberOfMoves = 4; -class HParallelMove FINAL : public HTemplateInstruction<0> { +class HParallelMove FINAL : public HExpression<0> { public: explicit HParallelMove(ArenaAllocator* allocator, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(kParallelMove, SideEffects::None(), dex_pc), + : HExpression(kParallelMove, SideEffects::None(), dex_pc), moves_(allocator->Adapter(kArenaAllocMoveOperands)) { moves_.reserve(kDefaultNumberOfMoves); } @@ -7788,8 +7838,30 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern(); } +// Implement HInstruction::Is##type() for concrete instructions. +#define INSTRUCTION_TYPE_CHECK(type, super) \ + inline bool HInstruction::Is##type() const { return GetKind() == k##type; } + FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) +#undef INSTRUCTION_TYPE_CHECK + +// Implement HInstruction::Is##type() for abstract instructions. +#define INSTRUCTION_TYPE_CHECK_RESULT(type, super) \ + std::is_base_of<BaseType, H##type>::value, #define INSTRUCTION_TYPE_CHECK(type, super) \ - inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \ + inline bool HInstruction::Is##type() const { \ + DCHECK_LT(GetKind(), kLastInstructionKind); \ + using BaseType = H##type; \ + static constexpr bool results[] = { \ + FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK_RESULT) \ + }; \ + return results[static_cast<size_t>(GetKind())]; \ + } + + FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) +#undef INSTRUCTION_TYPE_CHECK +#undef INSTRUCTION_TYPE_CHECK_RESULT + +#define INSTRUCTION_TYPE_CAST(type, super) \ inline const H##type* HInstruction::As##type() const { \ return Is##type() ? down_cast<const H##type*>(this) : nullptr; \ } \ @@ -7797,8 +7869,9 @@ inline bool IsZeroBitPattern(HInstruction* instruction) { return Is##type() ? static_cast<H##type*>(this) : nullptr; \ } - FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK) -#undef INSTRUCTION_TYPE_CHECK + FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CAST) +#undef INSTRUCTION_TYPE_CAST + // Create space in `blocks` for adding `number_of_new_blocks` entries // starting at location `at`. Blocks after `at` are moved accordingly. diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h index d0e0fef946..05b27a7810 100644 --- a/compiler/optimizing/nodes_mips.h +++ b/compiler/optimizing/nodes_mips.h @@ -39,14 +39,14 @@ class HMipsComputeBaseMethodAddress : public HExpression<0> { }; // Mips version of HPackedSwitch that holds a pointer to the base method address. -class HMipsPackedSwitch FINAL : public HTemplateInstruction<2> { +class HMipsPackedSwitch FINAL : public HExpression<2> { public: HMipsPackedSwitch(int32_t start_value, int32_t num_entries, HInstruction* input, HMipsComputeBaseMethodAddress* method_base, uint32_t dex_pc) - : HTemplateInstruction(kMipsPackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kMipsPackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 1a484e1944..c5e9a8d036 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -79,13 +79,14 @@ class HVecOperation : public HVariableInputSizeInstruction { size_t vector_length, uint32_t dex_pc) : HVariableInputSizeInstruction(kind, + kSIMDType, side_effects, dex_pc, allocator, number_of_inputs, kArenaAllocVectorNode), vector_length_(vector_length) { - SetPackedField<TypeField>(packed_type); + SetPackedField<PackedTypeField>(packed_type); DCHECK_LT(1u, vector_length); } @@ -99,14 +100,9 @@ class HVecOperation : public HVariableInputSizeInstruction { return vector_length_ * DataType::Size(GetPackedType()); } - // Returns the type of the vector operation. - DataType::Type GetType() const OVERRIDE { - return kSIMDType; - } - // Returns the true component type packed in a vector. DataType::Type GetPackedType() const { - return GetPackedField<TypeField>(); + return GetPackedField<PackedTypeField>(); } // Assumes vector nodes cannot be moved by default. Each concrete implementation @@ -185,12 +181,12 @@ class HVecOperation : public HVariableInputSizeInstruction { protected: // Additional packed bits. - static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; - static constexpr size_t kFieldTypeSize = + static constexpr size_t kFieldPackedType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldPackedTypeSize = MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast)); - static constexpr size_t kNumberOfVectorOpPackedBits = kFieldType + kFieldTypeSize; + static constexpr size_t kNumberOfVectorOpPackedBits = kFieldPackedType + kFieldPackedTypeSize; static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>; + using PackedTypeField = BitField<DataType::Type, kFieldPackedType, kFieldPackedTypeSize>; DEFAULT_COPY_CONSTRUCTOR(VecOperation); @@ -358,11 +354,9 @@ class HVecExtractScalar FINAL : public HVecUnaryOperation { DCHECK(HasConsistentPackedTypes(input, packed_type)); DCHECK_LT(index, vector_length); DCHECK_EQ(index, 0u); - } - - // Yields a single component in the vector. - DataType::Type GetType() const OVERRIDE { - return GetPackedType(); + // Yields a single component in the vector. + // Overrides the kSIMDType set by the VecOperation constructor. + SetPackedField<TypeField>(packed_type); } // An extract needs to stay in place, since SIMD registers are not diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index 4c32be7d15..d1e7f68edb 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -89,14 +89,14 @@ class HX86FPNeg FINAL : public HExpression<2> { }; // X86 version of HPackedSwitch that holds a pointer to the base method address. -class HX86PackedSwitch FINAL : public HTemplateInstruction<2> { +class HX86PackedSwitch FINAL : public HExpression<2> { public: HX86PackedSwitch(int32_t start_value, int32_t num_entries, HInstruction* input, HX86ComputeBaseMethodAddress* method_base, uint32_t dex_pc) - : HTemplateInstruction(kX86PackedSwitch, SideEffects::None(), dex_pc), + : HExpression(kX86PackedSwitch, SideEffects::None(), dex_pc), start_value_(start_value), num_entries_(num_entries) { SetRawInputAt(0, input); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 6e2c99444c..c4977decd9 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -109,7 +109,7 @@ class PassObserver : public ValueObject { : graph_(graph), last_seen_graph_size_(0), cached_method_name_(), - timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()), + timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpPassTimings()), timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true), disasm_info_(graph->GetAllocator()), visualizer_oss_(), diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 59733397bf..831bccc90a 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -17,7 +17,7 @@ #include "prepare_for_register_allocation.h" #include "dex/dex_file_types.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "optimizing_compiler_stats.h" #include "well_known_classes.h" diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index c47c69af67..ecfa790b91 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -59,6 +59,18 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassCla return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); } +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodHandleClassHandle() { + return GetRootHandle(handles_, + ClassLinker::kJavaLangInvokeMethodHandleImpl, + &method_handle_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() { + return GetRootHandle(handles_, + ClassLinker::kJavaLangInvokeMethodType, + &method_type_class_handle_); +} + ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_); } @@ -89,6 +101,8 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitLoadClass(HLoadClass* load_class) OVERRIDE; void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE; void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE; + void VisitLoadMethodHandle(HLoadMethodHandle* instr) OVERRIDE; + void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE; void VisitLoadString(HLoadString* instr) OVERRIDE; void VisitLoadException(HLoadException* instr) OVERRIDE; void VisitNewArray(HNewArray* instr) OVERRIDE; @@ -668,6 +682,17 @@ void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodHandle(HLoadMethodHandle* instr) { + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create( + handle_cache_->GetMethodHandleClassHandle(), + /* is_exact */ true)); +} + +void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true)); +} + void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) { instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true)); diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 400852f4dc..d36d592708 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -75,6 +75,8 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle GetObjectClassHandle(); ReferenceTypeInfo::TypeHandle GetClassClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodHandleClassHandle(); + ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle(); ReferenceTypeInfo::TypeHandle GetStringClassHandle(); ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); @@ -83,6 +85,8 @@ class ReferenceTypePropagation : public HOptimization { ReferenceTypeInfo::TypeHandle object_class_handle_; ReferenceTypeInfo::TypeHandle class_class_handle_; + ReferenceTypeInfo::TypeHandle method_handle_class_handle_; + ReferenceTypeInfo::TypeHandle method_type_class_handle_; ReferenceTypeInfo::TypeHandle string_class_handle_; ReferenceTypeInfo::TypeHandle throwable_class_handle_; }; diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index f71cb5b784..4f394d5e16 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -68,12 +68,10 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(ArrayGet , unused) \ M(ArrayLength , unused) \ M(ArraySet , unused) \ - M(BinaryOperation , unused) \ M(BoundsCheck , unused) \ M(Div , unused) \ M(InstanceFieldGet , unused) \ M(InstanceOf , unused) \ - M(Invoke , unused) \ M(LoadString , unused) \ M(Mul , unused) \ M(NewArray , unused) \ @@ -108,6 +106,10 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(VecLoad , unused) \ M(VecStore , unused) +#define FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(M) \ + M(BinaryOperation , unused) \ + M(Invoke , unused) + #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \ M(BitwiseNegatedRight, unused) \ M(MultiplyAccumulate, unused) \ @@ -119,6 +121,7 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { void Visit##type(H##type* instruction) OVERRIDE; FOR_EACH_SCHEDULED_COMMON_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_SCHEDULED_ABSTRACT_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 57360e74a3..26aa434c0d 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -18,7 +18,7 @@ #include "base/arena_allocator.h" #include "base/malloc_arena_pool.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #ifdef ART_ENABLE_CODEGEN_arm #include "utils/arm/assembler_arm_vixl.h" diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index 065c3de23c..2c428fac7e 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -37,6 +37,29 @@ namespace arm { #define ___ asm_.GetVIXLAssembler()-> #endif +vixl::aarch32::Register AsVIXLRegister(ArmManagedRegister reg) { + CHECK(reg.IsCoreRegister()); + return vixl::aarch32::Register(reg.RegId()); +} + +static inline vixl::aarch32::SRegister AsVIXLSRegister(ArmManagedRegister reg) { + CHECK(reg.IsSRegister()); + return vixl::aarch32::SRegister(reg.RegId() - kNumberOfCoreRegIds); +} + +static inline vixl::aarch32::DRegister AsVIXLDRegister(ArmManagedRegister reg) { + CHECK(reg.IsDRegister()); + return vixl::aarch32::DRegister(reg.RegId() - kNumberOfCoreRegIds - kNumberOfSRegIds); +} + +static inline vixl::aarch32::Register AsVIXLRegisterPairLow(ArmManagedRegister reg) { + return vixl::aarch32::Register(reg.AsRegisterPairLow()); +} + +static inline vixl::aarch32::Register AsVIXLRegisterPairHigh(ArmManagedRegister reg) { + return vixl::aarch32::Register(reg.AsRegisterPairHigh()); +} + void ArmVIXLJNIMacroAssembler::FinalizeCode() { for (const std::unique_ptr< ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) { @@ -60,7 +83,7 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, ArrayRef<const ManagedRegister> callee_save_regs, const ManagedRegisterEntrySpills& entry_spills) { CHECK_ALIGNED(frame_size, kStackAlignment); - CHECK(r0.Is(method_reg.AsArm().AsVIXLRegister())); + CHECK(r0.Is(AsVIXLRegister(method_reg.AsArm()))); // Push callee saves and link register. RegList core_spill_mask = 1 << LR; @@ -104,13 +127,13 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, ManagedRegisterSpill spill = entry_spills.at(i); offset += spill.getSize(); } else if (reg.IsCoreRegister()) { - asm_.StoreToOffset(kStoreWord, reg.AsVIXLRegister(), sp, offset); + asm_.StoreToOffset(kStoreWord, AsVIXLRegister(reg), sp, offset); offset += 4; } else if (reg.IsSRegister()) { - asm_.StoreSToOffset(reg.AsVIXLSRegister(), sp, offset); + asm_.StoreSToOffset(AsVIXLSRegister(reg), sp, offset); offset += 4; } else if (reg.IsDRegister()) { - asm_.StoreDToOffset(reg.AsVIXLDRegister(), sp, offset); + asm_.StoreDToOffset(AsVIXLDRegister(reg), sp, offset); offset += 8; } } @@ -208,76 +231,71 @@ void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, si } else if (src.IsCoreRegister()) { CHECK_EQ(4u, size); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(AsVIXLRegister(src)); + asm_.StoreToOffset(kStoreWord, AsVIXLRegister(src), sp, dest.Int32Value()); } else if (src.IsRegisterPair()) { CHECK_EQ(8u, size); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairLow(), sp, dest.Int32Value()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegisterPairHigh(), sp, dest.Int32Value() + 4); + asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairLow(src), sp, dest.Int32Value()); + asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairHigh(src), sp, dest.Int32Value() + 4); } else if (src.IsSRegister()) { CHECK_EQ(4u, size); - asm_.StoreSToOffset(src.AsVIXLSRegister(), sp, dest.Int32Value()); + asm_.StoreSToOffset(AsVIXLSRegister(src), sp, dest.Int32Value()); } else { CHECK_EQ(8u, size); CHECK(src.IsDRegister()) << src; - asm_.StoreDToOffset(src.AsVIXLDRegister(), sp, dest.Int32Value()); + asm_.StoreDToOffset(AsVIXLDRegister(src), sp, dest.Int32Value()); } } void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { - ArmManagedRegister src = msrc.AsArm(); - CHECK(src.IsCoreRegister()) << src; + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(src); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { - ArmManagedRegister src = msrc.AsArm(); - CHECK(src.IsCoreRegister()) << src; + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(src.AsVIXLRegister()); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(src); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off, ManagedRegister mscratch) { - ArmManagedRegister src = msrc.AsArm(); - ArmManagedRegister scratch = mscratch.AsArm(); - asm_.StoreToOffset(kStoreWord, src.AsVIXLRegister(), sp, dest.Int32Value()); + vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm()); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); + asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, in_off.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value() + 4); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, sp, in_off.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4); } void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } -void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest, - ManagedRegister base, +void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister mdest, + ManagedRegister mbase, MemberOffset offs, bool unpoison_reference) { - ArmManagedRegister dst = dest.AsArm(); - CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst; + vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm()); + vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister(), base.AsArm().AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, - dst.AsVIXLRegister(), - base.AsArm().AsVIXLRegister(), - offs.Int32Value()); + temps.Exclude(dest, base); + asm_.LoadFromOffset(kLoadWord, dest, base, offs.Int32Value()); if (unpoison_reference) { - asm_.MaybeUnpoisonHeapReference(dst.AsVIXLRegister()); + asm_.MaybeUnpoisonHeapReference(dest); } } @@ -294,13 +312,12 @@ void ArmVIXLJNIMacroAssembler::LoadRawPtr(ManagedRegister dest ATTRIBUTE_UNUSED, void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, - ManagedRegister scratch) { - ArmManagedRegister mscratch = scratch.AsArm(); - CHECK(mscratch.IsCoreRegister()) << mscratch; + ManagedRegister mscratch) { + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(mscratch.AsVIXLRegister()); - asm_.LoadImmediate(mscratch.AsVIXLRegister(), imm); - asm_.StoreToOffset(kStoreWord, mscratch.AsVIXLRegister(), sp, dest.Int32Value()); + temps.Exclude(scratch); + asm_.LoadImmediate(scratch, imm); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } void ArmVIXLJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { @@ -313,23 +330,21 @@ void ArmVIXLJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst, return Load(m_dst.AsArm(), tr, src.Int32Value(), size); } -void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) { - ArmManagedRegister dst = m_dst.AsArm(); - CHECK(dst.IsCoreRegister()) << dst; +void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) { + vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, dst.AsVIXLRegister(), tr, offs.Int32Value()); + temps.Exclude(dest); + asm_.LoadFromOffset(kLoadWord, dest, tr, offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, tr, thr_offs.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, fr_offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED, @@ -341,12 +356,11 @@ void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIB void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.AddConstant(scratch.AsVIXLRegister(), sp, fr_offs.Int32Value()); - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), tr, thr_offs.Int32Value()); + temps.Exclude(scratch); + asm_.AddConstant(scratch, sp, fr_offs.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value()); } void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) { @@ -363,43 +377,43 @@ void ArmVIXLJNIMacroAssembler::ZeroExtend(ManagedRegister mreg ATTRIBUTE_UNUSED, UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm"; } -void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, - ManagedRegister m_src, +void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst, + ManagedRegister msrc, size_t size ATTRIBUTE_UNUSED) { - ArmManagedRegister dst = m_dst.AsArm(); - ArmManagedRegister src = m_src.AsArm(); + ArmManagedRegister dst = mdst.AsArm(); + ArmManagedRegister src = msrc.AsArm(); if (!dst.Equals(src)) { if (dst.IsCoreRegister()) { CHECK(src.IsCoreRegister()) << src; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dst.AsVIXLRegister()); - ___ Mov(dst.AsVIXLRegister(), src.AsVIXLRegister()); + temps.Exclude(AsVIXLRegister(dst)); + ___ Mov(AsVIXLRegister(dst), AsVIXLRegister(src)); } else if (dst.IsDRegister()) { if (src.IsDRegister()) { - ___ Vmov(F64, dst.AsVIXLDRegister(), src.AsVIXLDRegister()); + ___ Vmov(F64, AsVIXLDRegister(dst), AsVIXLDRegister(src)); } else { // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi}) CHECK(src.IsRegisterPair()) << src; - ___ Vmov(dst.AsVIXLDRegister(), src.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairHigh()); + ___ Vmov(AsVIXLDRegister(dst), AsVIXLRegisterPairLow(src), AsVIXLRegisterPairHigh(src)); } } else if (dst.IsSRegister()) { if (src.IsSRegister()) { - ___ Vmov(F32, dst.AsVIXLSRegister(), src.AsVIXLSRegister()); + ___ Vmov(F32, AsVIXLSRegister(dst), AsVIXLSRegister(src)); } else { // VMOV Sn, Rn (Sn = Rn) CHECK(src.IsCoreRegister()) << src; - ___ Vmov(dst.AsVIXLSRegister(), src.AsVIXLRegister()); + ___ Vmov(AsVIXLSRegister(dst), AsVIXLRegister(src)); } } else { CHECK(dst.IsRegisterPair()) << dst; CHECK(src.IsRegisterPair()) << src; // Ensure that the first move doesn't clobber the input of the second. if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) { - ___ Mov(dst.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairLow()); - ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh()); + ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src)); + ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src)); } else { - ___ Mov(dst.AsVIXLRegisterPairHigh(), src.AsVIXLRegisterPairHigh()); - ___ Mov(dst.AsVIXLRegisterPairLow(), src.AsVIXLRegisterPairLow()); + ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src)); + ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src)); } } } @@ -407,21 +421,20 @@ void ArmVIXLJNIMacroAssembler::Move(ManagedRegister m_dst, void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, FrameOffset src, - ManagedRegister scratch, + ManagedRegister mscratch, size_t size) { - ArmManagedRegister temp = scratch.AsArm(); - CHECK(temp.IsCoreRegister()) << temp; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); CHECK(size == 4 || size == 8) << size; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(temp.AsVIXLRegister()); + temps.Exclude(scratch); if (size == 4) { - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); } else if (size == 8) { - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value()); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value()); - asm_.LoadFromOffset(kLoadWord, temp.AsVIXLRegister(), sp, src.Int32Value() + 4); - asm_.StoreToOffset(kStoreWord, temp.AsVIXLRegister(), sp, dest.Int32Value() + 4); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value() + 4); + asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4); } } @@ -471,48 +484,44 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, FrameOffset handle_scope_offset, ManagedRegister min_reg, bool null_allowed) { - ArmManagedRegister out_reg = mout_reg.AsArm(); - ArmManagedRegister in_reg = min_reg.AsArm(); - CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; - CHECK(out_reg.IsCoreRegister()) << out_reg; + vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm()); + vixl::aarch32::Register in_reg = + min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(out_reg.AsVIXLRegister()); + temps.Exclude(out_reg); if (null_allowed) { // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) - if (in_reg.IsNoRegister()) { - asm_.LoadFromOffset(kLoadWord, - out_reg.AsVIXLRegister(), - sp, - handle_scope_offset.Int32Value()); + if (!in_reg.IsValid()) { + asm_.LoadFromOffset(kLoadWord, out_reg, sp, handle_scope_offset.Int32Value()); in_reg = out_reg; } - temps.Exclude(in_reg.AsVIXLRegister()); - ___ Cmp(in_reg.AsVIXLRegister(), 0); + temps.Exclude(in_reg); + ___ Cmp(in_reg, 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) { - if (!out_reg.Equals(in_reg)) { + if (!out_reg.Is(in_reg)) { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 3 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(eq, 0xc); - ___ mov(eq, out_reg.AsVIXLRegister(), 0); - asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + ___ mov(eq, out_reg, 0); + asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne); } else { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 2 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); - asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne); } } else { // TODO: Implement this (old arm assembler would have crashed here). UNIMPLEMENTED(FATAL); } } else { - asm_.AddConstant(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.AddConstant(out_reg, sp, handle_scope_offset.Int32Value()); } } @@ -520,31 +529,30 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset, ManagedRegister mscratch, bool null_allowed) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); if (null_allowed) { - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, handle_scope_offset.Int32Value()); // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is // the address in the handle scope holding the reference. // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) - ___ Cmp(scratch.AsVIXLRegister(), 0); + ___ Cmp(scratch, 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) { ExactAssemblyScope guard(asm_.GetVIXLAssembler(), 2 * vixl32::kMaxInstructionSizeInBytes, CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); - asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); + asm_.AddConstantInIt(scratch, sp, handle_scope_offset.Int32Value(), ne); } else { // TODO: Implement this (old arm assembler would have crashed here). UNIMPLEMENTED(FATAL); } } else { - asm_.AddConstant(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value()); + asm_.AddConstant(scratch, sp, handle_scope_offset.Int32Value()); } - asm_.StoreToOffset(kStoreWord, scratch.AsVIXLRegister(), sp, out_off.Int32Value()); + asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value()); } void ArmVIXLJNIMacroAssembler::LoadReferenceFromHandleScope( @@ -566,32 +574,23 @@ void ArmVIXLJNIMacroAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED, void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) { - ArmManagedRegister base = mbase.AsArm(); - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(base.IsCoreRegister()) << base; - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm()); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); - asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), - base.AsVIXLRegister(), - offset.Int32Value()); - ___ Blx(scratch.AsVIXLRegister()); + temps.Exclude(scratch); + asm_.LoadFromOffset(kLoadWord, scratch, base, offset.Int32Value()); + ___ Blx(scratch); // TODO: place reference map on call. } void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { - ArmManagedRegister scratch = mscratch.AsArm(); - CHECK(scratch.IsCoreRegister()) << scratch; + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); // Call *(*(SP + base) + offset) - asm_.LoadFromOffset(kLoadWord, scratch.AsVIXLRegister(), sp, base.Int32Value()); - asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), - scratch.AsVIXLRegister(), - offset.Int32Value()); - ___ Blx(scratch.AsVIXLRegister()); + asm_.LoadFromOffset(kLoadWord, scratch, sp, base.Int32Value()); + asm_.LoadFromOffset(kLoadWord, scratch, scratch, offset.Int32Value()); + ___ Blx(scratch); // TODO: place reference map on call } @@ -602,8 +601,8 @@ void ArmVIXLJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UN void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister mtr) { UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(mtr.AsArm().AsVIXLRegister()); - ___ Mov(mtr.AsArm().AsVIXLRegister(), tr); + temps.Exclude(AsVIXLRegister(mtr.AsArm())); + ___ Mov(AsVIXLRegister(mtr.AsArm()), tr); } void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, @@ -611,19 +610,19 @@ void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset, asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value()); } -void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) { +void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) { CHECK_ALIGNED(stack_adjust, kStackAlignment); - ArmManagedRegister scratch = m_scratch.AsArm(); + vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(scratch.AsVIXLRegister()); + temps.Exclude(scratch); exception_blocks_.emplace_back( - new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust)); + new ArmVIXLJNIMacroAssembler::ArmException(mscratch.AsArm(), stack_adjust)); asm_.LoadFromOffset(kLoadWord, - scratch.AsVIXLRegister(), + scratch, tr, Thread::ExceptionOffset<kArmPointerSize>().Int32Value()); - ___ Cmp(scratch.AsVIXLRegister(), 0); + ___ Cmp(scratch, 0); vixl32::Label* label = exception_blocks_.back()->Entry(); ___ BPreferNear(ne, label); // TODO: think about using CBNZ here. @@ -640,19 +639,18 @@ void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label) { void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label, JNIMacroUnaryCondition condition, - ManagedRegister test) { + ManagedRegister mtest) { CHECK(label != nullptr); + vixl::aarch32::Register test = AsVIXLRegister(mtest.AsArm()); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(test.AsArm().AsVIXLRegister()); + temps.Exclude(test); switch (condition) { case JNIMacroUnaryCondition::kZero: - ___ CompareAndBranchIfZero(test.AsArm().AsVIXLRegister(), - ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; case JNIMacroUnaryCondition::kNotZero: - ___ CompareAndBranchIfNonZero(test.AsArm().AsVIXLRegister(), - ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); + ___ CompareAndBranchIfNonZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); break; default: LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition); @@ -672,12 +670,13 @@ void ArmVIXLJNIMacroAssembler::EmitExceptionPoll( DecreaseFrameSize(exception->stack_adjust_); } + vixl::aarch32::Register scratch = AsVIXLRegister(exception->scratch_); UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(exception->scratch_.AsVIXLRegister()); + temps.Exclude(scratch); // Pass exception object as argument. // Don't care about preserving r0 as this won't return. - ___ Mov(r0, exception->scratch_.AsVIXLRegister()); - temps.Include(exception->scratch_.AsVIXLRegister()); + ___ Mov(r0, scratch); + temps.Include(scratch); // TODO: check that exception->scratch_ is dead by this point. vixl32::Register temp = temps.Acquire(); ___ Ldr(temp, @@ -698,26 +697,27 @@ void ArmVIXLJNIMacroAssembler::Load(ArmManagedRegister if (dest.IsNoRegister()) { CHECK_EQ(0u, size) << dest; } else if (dest.IsCoreRegister()) { - CHECK(!dest.AsVIXLRegister().Is(sp)) << dest; + vixl::aarch32::Register dst = AsVIXLRegister(dest); + CHECK(!dst.Is(sp)) << dest; UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); - temps.Exclude(dest.AsVIXLRegister()); + temps.Exclude(dst); if (size == 1u) { - ___ Ldrb(dest.AsVIXLRegister(), MemOperand(base, offset)); + ___ Ldrb(dst, MemOperand(base, offset)); } else { CHECK_EQ(4u, size) << dest; - ___ Ldr(dest.AsVIXLRegister(), MemOperand(base, offset)); + ___ Ldr(dst, MemOperand(base, offset)); } } else if (dest.IsRegisterPair()) { CHECK_EQ(8u, size) << dest; - ___ Ldr(dest.AsVIXLRegisterPairLow(), MemOperand(base, offset)); - ___ Ldr(dest.AsVIXLRegisterPairHigh(), MemOperand(base, offset + 4)); + ___ Ldr(AsVIXLRegisterPairLow(dest), MemOperand(base, offset)); + ___ Ldr(AsVIXLRegisterPairHigh(dest), MemOperand(base, offset + 4)); } else if (dest.IsSRegister()) { - ___ Vldr(dest.AsVIXLSRegister(), MemOperand(base, offset)); + ___ Vldr(AsVIXLSRegister(dest), MemOperand(base, offset)); } else { CHECK(dest.IsDRegister()) << dest; - ___ Vldr(dest.AsVIXLDRegister(), MemOperand(base, offset)); + ___ Vldr(AsVIXLDRegister(dest), MemOperand(base, offset)); } } diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h index 26f23b2ed6..e42572dc32 100644 --- a/compiler/utils/arm/managed_register_arm.h +++ b/compiler/utils/arm/managed_register_arm.h @@ -20,15 +20,8 @@ #include <android-base/logging.h> #include "constants_arm.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" -// TODO(VIXL): Make VIXL compile with -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#include "aarch32/macro-assembler-aarch32.h" -#pragma GCC diagnostic pop - namespace art { namespace arm { @@ -97,31 +90,16 @@ class ArmManagedRegister : public ManagedRegister { return static_cast<Register>(id_); } - vixl::aarch32::Register AsVIXLRegister() const { - CHECK(IsCoreRegister()); - return vixl::aarch32::Register(id_); - } - constexpr SRegister AsSRegister() const { CHECK(IsSRegister()); return static_cast<SRegister>(id_ - kNumberOfCoreRegIds); } - vixl::aarch32::SRegister AsVIXLSRegister() const { - CHECK(IsSRegister()); - return vixl::aarch32::SRegister(id_ - kNumberOfCoreRegIds); - } - constexpr DRegister AsDRegister() const { CHECK(IsDRegister()); return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); } - vixl::aarch32::DRegister AsVIXLDRegister() const { - CHECK(IsDRegister()); - return vixl::aarch32::DRegister(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); - } - constexpr SRegister AsOverlappingDRegisterLow() const { CHECK(IsOverlappingDRegister()); DRegister d_reg = AsDRegister(); @@ -150,20 +128,12 @@ class ArmManagedRegister : public ManagedRegister { return FromRegId(AllocIdLow()).AsCoreRegister(); } - vixl::aarch32::Register AsVIXLRegisterPairLow() const { - return vixl::aarch32::Register(AsRegisterPairLow()); - } - constexpr Register AsRegisterPairHigh() const { CHECK(IsRegisterPair()); // Appropriate mapping of register ids allows to use AllocIdHigh(). return FromRegId(AllocIdHigh()).AsCoreRegister(); } - vixl::aarch32::Register AsVIXLRegisterPairHigh() const { - return vixl::aarch32::Register(AsRegisterPairHigh()); - } - constexpr bool IsCoreRegister() const { CHECK(IsValidManagedRegister()); return (0 <= id_) && (id_ < kNumberOfCoreRegIds); @@ -255,16 +225,16 @@ class ArmManagedRegister : public ManagedRegister { return FromDRegister(static_cast<DRegister>(r)); } - private: - constexpr bool IsValidManagedRegister() const { - return (0 <= id_) && (id_ < kNumberOfRegIds); - } - int RegId() const { CHECK(!IsNoRegister()); return id_; } + private: + constexpr bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + int AllocId() const { CHECK(IsValidManagedRegister() && !IsOverlappingDRegister() && !IsRegisterPair()); diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 9ce7ec9a97..0513890aa8 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -20,7 +20,6 @@ #include <android-base/logging.h> #include "arch/arm64/registers_arm64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 674dc9a78b..19c405e517 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -153,7 +153,7 @@ const char* const VixlJniHelpersResults[] = { " 21c: f8d9 8034 ldr.w r8, [r9, #52] ; 0x34\n", " 220: 4770 bx lr\n", " 222: 4660 mov r0, ip\n", - " 224: f8d9 c2c4 ldr.w ip, [r9, #708] ; 0x2c4\n", + " 224: f8d9 c2cc ldr.w ip, [r9, #716] ; 0x2cc\n", " 228: 47e0 blx ip\n", nullptr }; diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc index 9a69ffd3dd..0f858926df 100644 --- a/compiler/utils/mips/assembler_mips32r5_test.cc +++ b/compiler/utils/mips/assembler_mips32r5_test.cc @@ -45,6 +45,16 @@ class AssemblerMIPS32r5Test : public AssemblerTest<mips::MipsAssembler, uint32_t, mips::VectorRegister> Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector<uint8_t> data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS32r5Test() : instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) { } diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc index 691c33f3e7..3d876ca613 100644 --- a/compiler/utils/mips/assembler_mips32r6_test.cc +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -45,6 +45,16 @@ class AssemblerMIPS32r6Test : public AssemblerTest<mips::MipsAssembler, uint32_t, mips::VectorRegister> Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector<uint8_t> data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS32r6Test() : instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r6", nullptr)) { } diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index b027d3a549..f94d074299 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -43,6 +43,16 @@ class AssemblerMIPSTest : public AssemblerTest<mips::MipsAssembler, mips::FRegister, uint32_t> Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector<uint8_t> data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + protected: // Get the typically used name for this architecture, e.g., aarch64, x86-64, ... std::string GetArchitectureString() OVERRIDE { diff --git a/compiler/utils/mips/managed_register_mips.h b/compiler/utils/mips/managed_register_mips.h index 66204e70e3..18d5821e61 100644 --- a/compiler/utils/mips/managed_register_mips.h +++ b/compiler/utils/mips/managed_register_mips.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_ #include "constants_mips.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index fb5f12be93..a53ff7cc2b 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -48,6 +48,16 @@ class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler, uint32_t, mips64::VectorRegister> Base; + // These tests were taking too long, so we hide the DriverStr() from AssemblerTest<> + // and reimplement it without the verification against `assembly_string`. b/73903608 + void DriverStr(const std::string& assembly_string ATTRIBUTE_UNUSED, + const std::string& test_name ATTRIBUTE_UNUSED) { + GetAssembler()->FinalizeCode(); + std::vector<uint8_t> data(GetAssembler()->CodeSize()); + MemoryRegion code(data.data(), data.size()); + GetAssembler()->FinalizeInstructions(code); + } + AssemblerMIPS64Test() : instruction_set_features_(Mips64InstructionSetFeatures::FromVariant("default", nullptr)) {} diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h index 3980199b1e..94166d32b7 100644 --- a/compiler/utils/mips64/managed_register_mips64.h +++ b/compiler/utils/mips64/managed_register_mips64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_ #include "constants_mips64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h index c0c2b650e9..8810bfa2f1 100644 --- a/compiler/utils/x86/managed_register_x86.h +++ b/compiler/utils/x86/managed_register_x86.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_ #include "constants_x86.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h index 32af672670..6760882965 100644 --- a/compiler/utils/x86_64/managed_register_x86_64.h +++ b/compiler/utils/x86_64/managed_register_x86_64.h @@ -18,7 +18,6 @@ #define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_ #include "constants_x86_64.h" -#include "debug/dwarf/register.h" #include "utils/managed_register.h" namespace art { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 4fafca9e1b..18548baf7f 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -294,6 +294,8 @@ art_cc_binary { use_clang_lld: true, }, }, + // b/79417743, oatdump 32-bit tests failed with clang lld + use_clang_lld: false, ldflags: [ // We need this because GC stress mode makes use of // _Unwind_GetIP and _Unwind_Backtrace and the symbols are also diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index df38ee3a34..6b65aca943 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -78,7 +78,7 @@ #include "gc/space/space-inl.h" #include "gc/verification.h" #include "interpreter/unstarted_runtime.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "linker/buffered_output_stream.h" #include "linker/elf_writer.h" #include "linker/elf_writer_quick.h" @@ -350,6 +350,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --dump-timings: display a breakdown of where time was spent"); UsageError(""); + UsageError(" --dump-pass-timings: display a breakdown of time spent in optimization"); + UsageError(" passes for each compiled method."); + UsageError(""); UsageError(" -g"); UsageError(" --generate-debug-info: Generate debug information for native debugging,"); UsageError(" such as stack unwinding information, ELF symbols and DWARF sections."); @@ -1254,10 +1257,10 @@ class Dex2Oat FINAL { if (stored_class_loader_context_ == nullptr) { Usage("Option --stored-class-loader-context has an incorrect format: %s", stored_context_arg.c_str()); - } else if (!class_loader_context_->VerifyClassLoaderContextMatch( + } else if (class_loader_context_->VerifyClassLoaderContextMatch( stored_context_arg, /*verify_names*/ false, - /*verify_checksums*/ false)) { + /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) { Usage( "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'", stored_context_arg.c_str(), diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index bd1e6df93d..9e5cd8035c 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -52,7 +52,7 @@ #include "image.h" #include "imt_conflict_table.h" #include "subtype_check.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "lock_word.h" #include "mirror/array-inl.h" diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 1699153e7e..df0641cc7f 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -497,7 +497,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(76U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(162 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc index 1e0f5ac6ae..457addf114 100644 --- a/libdexfile/dex/dex_file_loader.cc +++ b/libdexfile/dex/dex_file_loader.cc @@ -191,6 +191,8 @@ std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) { std::string base_location = GetBaseLocation(dex_location); const char* suffix = dex_location + base_location.size(); DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator); + // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. + // Do not run this code on a small stack, e.g. in signal handler. UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr)); if (path != nullptr && path.get() != base_location) { return std::string(path.get()) + suffix; diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h index 28cdfc13ce..01532203eb 100644 --- a/libdexfile/dex/dex_file_loader.h +++ b/libdexfile/dex/dex_file_loader.h @@ -71,7 +71,7 @@ class DexFileLoader { // 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. + // the dex_location where its 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 diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index ba65fc9b28..a32f64e49f 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -1761,63 +1761,13 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c return true; } -bool DexFileVerifier::CheckIntraSectionIterateByType(size_t offset, - uint32_t count, - DexFile::MapItemType type) { - switch (type) { - case DexFile::kDexTypeHeaderItem: - return CheckIntraSectionIterate<DexFile::kDexTypeHeaderItem>(offset, count); - case DexFile::kDexTypeStringIdItem: - return CheckIntraSectionIterate<DexFile::kDexTypeStringIdItem>(offset, count); - case DexFile::kDexTypeTypeIdItem: - return CheckIntraSectionIterate<DexFile::kDexTypeTypeIdItem>(offset, count); - case DexFile::kDexTypeProtoIdItem: - return CheckIntraSectionIterate<DexFile::kDexTypeProtoIdItem>(offset, count); - case DexFile::kDexTypeFieldIdItem: - return CheckIntraSectionIterate<DexFile::kDexTypeFieldIdItem>(offset, count); - case DexFile::kDexTypeMethodIdItem: - return CheckIntraSectionIterate<DexFile::kDexTypeMethodIdItem>(offset, count); - case DexFile::kDexTypeClassDefItem: - return CheckIntraSectionIterate<DexFile::kDexTypeClassDefItem>(offset, count); - case DexFile::kDexTypeCallSiteIdItem: - return CheckIntraSectionIterate<DexFile::kDexTypeCallSiteIdItem>(offset, count); - case DexFile::kDexTypeMethodHandleItem: - return CheckIntraSectionIterate<DexFile::kDexTypeMethodHandleItem>(offset, count); - case DexFile::kDexTypeMapList: - return CheckIntraSectionIterate<DexFile::kDexTypeMapList>(offset, count); - case DexFile::kDexTypeTypeList: - return CheckIntraSectionIterate<DexFile::kDexTypeTypeList>(offset, count); - case DexFile::kDexTypeAnnotationSetRefList: - return CheckIntraSectionIterate<DexFile::kDexTypeAnnotationSetRefList>(offset, count); - case DexFile::kDexTypeAnnotationSetItem: - return CheckIntraSectionIterate<DexFile::kDexTypeAnnotationSetItem>(offset, count); - case DexFile::kDexTypeClassDataItem: - return CheckIntraSectionIterate<DexFile::kDexTypeClassDataItem>(offset, count); - case DexFile::kDexTypeCodeItem: - return CheckIntraSectionIterate<DexFile::kDexTypeCodeItem>(offset, count); - case DexFile::kDexTypeStringDataItem: - return CheckIntraSectionIterate<DexFile::kDexTypeStringDataItem>(offset, count); - case DexFile::kDexTypeDebugInfoItem: - return CheckIntraSectionIterate<DexFile::kDexTypeDebugInfoItem>(offset, count); - case DexFile::kDexTypeAnnotationItem: - return CheckIntraSectionIterate<DexFile::kDexTypeAnnotationItem>(offset, count); - case DexFile::kDexTypeEncodedArrayItem: - return CheckIntraSectionIterate<DexFile::kDexTypeEncodedArrayItem>(offset, count); - case DexFile::kDexTypeAnnotationsDirectoryItem: - return CheckIntraSectionIterate<DexFile::kDexTypeAnnotationsDirectoryItem>(offset, count); - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -} - -bool DexFileVerifier::CheckIntraIdSection(size_t offset, - uint32_t count, - DexFile::MapItemType type) { +template <DexFile::MapItemType kType> +bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count) { uint32_t expected_offset; uint32_t expected_size; // Get the expected offset and size from the header. - switch (type) { + switch (kType) { case DexFile::kDexTypeStringIdItem: expected_offset = header_->string_ids_off_; expected_size = header_->string_ids_size_; @@ -1843,7 +1793,7 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, expected_size = header_->class_defs_size_; break; default: - ErrorStringPrintf("Bad type for id section: %x", type); + ErrorStringPrintf("Bad type for id section: %x", kType); return false; } @@ -1857,7 +1807,7 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, return false; } - return CheckIntraSectionIterateByType(offset, count, type); + return CheckIntraSectionIterate<kType>(offset, count); } template <DexFile::MapItemType kType> @@ -1888,7 +1838,8 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count) { } bool DexFileVerifier::CheckIntraSection() { - const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_); + 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_; @@ -1927,17 +1878,22 @@ bool DexFileVerifier::CheckIntraSection() { 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_; + +#define CHECK_INTRA_ID_SECTION_CASE(type) \ + case type: \ + if (!CheckIntraIdSection<type>(section_offset, section_count)) { \ + return false; \ + } \ + offset = ptr_ - begin_; \ break; + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeStringIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeTypeIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeProtoIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeFieldIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeMethodIdItem) + CHECK_INTRA_ID_SECTION_CASE(DexFile::kDexTypeClassDefItem) +#undef CHECK_INTRA_ID_SECTION_CASE + case DexFile::kDexTypeMapList: if (UNLIKELY(section_count != 1)) { ErrorStringPrintf("Multiple map list items"); @@ -1951,80 +1907,34 @@ bool DexFileVerifier::CheckIntraSection() { ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); break; - case DexFile::kDexTypeMethodHandleItem: - CheckIntraSectionIterate<DexFile::kDexTypeMethodHandleItem>(section_offset, section_count); - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeCallSiteIdItem: - CheckIntraSectionIterate<DexFile::kDexTypeCallSiteIdItem>(section_offset, section_count); - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeTypeList: - if (!CheckIntraDataSection<DexFile::kDexTypeTypeList>(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationSetRefList: - if (!CheckIntraDataSection<DexFile::kDexTypeAnnotationSetRefList>(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationSetItem: - if (!CheckIntraDataSection<DexFile::kDexTypeAnnotationSetItem>(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeClassDataItem: - if (!CheckIntraDataSection<DexFile::kDexTypeClassDataItem>(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeCodeItem: - if (!CheckIntraDataSection<DexFile::kDexTypeCodeItem>(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeStringDataItem: - if (!CheckIntraDataSection<DexFile::kDexTypeStringDataItem>(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeDebugInfoItem: - if (!CheckIntraDataSection<DexFile::kDexTypeDebugInfoItem>(section_offset, section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationItem: - if (!CheckIntraDataSection<DexFile::kDexTypeAnnotationItem>(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeEncodedArrayItem: - if (!CheckIntraDataSection<DexFile::kDexTypeEncodedArrayItem>(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; - case DexFile::kDexTypeAnnotationsDirectoryItem: - if (!CheckIntraDataSection<DexFile::kDexTypeAnnotationsDirectoryItem>(section_offset, - section_count)) { - return false; - } - offset = ptr_ - begin_; - break; + +#define CHECK_INTRA_SECTION_ITERATE_CASE(type) \ + case type: \ + CheckIntraSectionIterate<type>(section_offset, section_count); \ + offset = ptr_ - begin_; \ + break; + CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeMethodHandleItem) + CHECK_INTRA_SECTION_ITERATE_CASE(DexFile::kDexTypeCallSiteIdItem) +#undef CHECK_INTRA_SECTION_ITERATE_CASE + +#define CHECK_INTRA_DATA_SECTION_CASE(type) \ + case type: \ + if (!CheckIntraDataSection<type>(section_offset, section_count)) { \ + return false; \ + } \ + offset = ptr_ - begin_; \ + break; + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeTypeList) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetRefList) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationSetItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeClassDataItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeCodeItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeStringDataItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeDebugInfoItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeEncodedArrayItem) + CHECK_INTRA_DATA_SECTION_CASE(DexFile::kDexTypeAnnotationsDirectoryItem) +#undef CHECK_INTRA_DATA_SECTION_CASE } if (offset == current_offset) { diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index eaffc6240a..04d8d71fa8 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -126,8 +126,8 @@ class DexFileVerifier { template <DexFile::MapItemType kType> bool CheckIntraSectionIterate(size_t offset, uint32_t count); - bool CheckIntraSectionIterateByType(size_t offset, uint32_t count, DexFile::MapItemType type); - bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); + template <DexFile::MapItemType kType> + bool CheckIntraIdSection(size_t offset, uint32_t count); template <DexFile::MapItemType kType> bool CheckIntraDataSection(size_t offset, uint32_t count); bool CheckIntraSection(); diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h index b62d044c6a..1aaeabd783 100644 --- a/libdexfile/dex/hidden_api_access_flags.h +++ b/libdexfile/dex/hidden_api_access_flags.h @@ -86,12 +86,10 @@ class HiddenApiAccessFlags { } static ALWAYS_INLINE ApiList DecodeFromRuntime(uint32_t runtime_access_flags) { - if ((runtime_access_flags & kAccIntrinsic) != 0) { - return kWhitelist; - } else { - uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift; - return static_cast<ApiList>(int_value); - } + // This is used in the fast path, only DCHECK here. + DCHECK_EQ(runtime_access_flags & kAccIntrinsic, 0u); + uint32_t int_value = (runtime_access_flags & kAccHiddenApiBits) >> kAccFlagsShift; + return static_cast<ApiList>(int_value); } static ALWAYS_INLINE uint32_t EncodeForRuntime(uint32_t runtime_access_flags, ApiList value) { diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index 2425a588df..be82fff65c 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -58,6 +58,7 @@ static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (ru 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) +static constexpr uint32_t kAccSkipHiddenApiChecks = 0x00100000; // 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_ diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc index 0e0c3c5116..748e24e27c 100644 --- a/libprofile/profile/profile_compilation_info.cc +++ b/libprofile/profile/profile_compilation_info.cc @@ -96,9 +96,7 @@ ProfileCompilationInfo::ProfileCompilationInfo() ProfileCompilationInfo::~ProfileCompilationInfo() { VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats()); - for (DexFileData* data : info_) { - delete data; - } + ClearData(); } void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, @@ -2106,4 +2104,12 @@ bool ProfileCompilationInfo::ProfileFilterFnAcceptAll( return true; } +void ProfileCompilationInfo::ClearData() { + for (DexFileData* data : info_) { + delete data; + } + info_.clear(); + profile_key_map_.clear(); +} + } // namespace art diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h index 32c796c363..e28c5f17b6 100644 --- a/libprofile/profile/profile_compilation_info.h +++ b/libprofile/profile/profile_compilation_info.h @@ -445,6 +445,9 @@ class ProfileCompilationInfo { // Checks if the profile is empty. bool IsEmpty() const; + // Clears all the data from the profile. + void ClearData(); + private: enum ProfileLoadStatus { kProfileLoadWouldOverwiteData, diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc index b0f96492df..b3262a7a14 100644 --- a/libprofile/profile/profile_compilation_info_test.cc +++ b/libprofile/profile/profile_compilation_info_test.cc @@ -1339,4 +1339,33 @@ TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) { ASSERT_TRUE(loaded_info.Equals(expected_info)); } + +TEST_F(ProfileCompilationInfoTest, ClearData) { + ProfileCompilationInfo info; + for (uint16_t i = 0; i < 10; i++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info)); + } + ASSERT_FALSE(IsEmpty(info)); + info.ClearData(); + ASSERT_TRUE(IsEmpty(info)); +} + +TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) { + ProfileCompilationInfo info; + for (uint16_t i = 0; i < 10; i++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info)); + } + info.ClearData(); + + ScratchFile profile; + ASSERT_TRUE(info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + ASSERT_TRUE(loaded_info.Equals(info)); +} + } // namespace art diff --git a/oatdump/Android.bp b/oatdump/Android.bp index be12c8e406..535acdff62 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -24,6 +24,8 @@ cc_defaults { shared_libs: ["libcutils"], }, }, + // b/79417743, oatdump 32-bit tests failed with clang lld + use_clang_lld: false, header_libs: [ "art_cmdlineparser_headers", ], diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 975d1948fe..be1ab7812a 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -48,8 +48,8 @@ #include "common_throws.h" #include "gc/heap.h" #include "handle_scope-inl.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/string-inl.h" #include "monitor.h" diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index ef5151990c..59f61e2ee4 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -43,7 +43,7 @@ #include "base/logging.h" // For gLogVerbosity. #include "base/mutex.h" #include "events-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "obj_ptr-inl.h" #include "object_tagging.h" #include "runtime.h" diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 73cc601e3e..82f3866c65 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -47,8 +47,8 @@ #include "base/strlcpy.h" #include "base/mutex.h" #include "events.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "jvmti.h" #include "ti_breakpoint.h" diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index d4e5df1d67..2f24d7ea3d 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -41,7 +41,7 @@ #include "dex/modifiers.h" #include "events-inl.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 74ffb84579..6a8ba48109 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -23,7 +23,7 @@ #include "base/mutex-inl.h" #include "events.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" #include "ti_breakpoint.h" diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index de678711fc..5cb4299293 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -44,8 +44,8 @@ #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" #include "instrumentation.h" -#include "jni_env_ext-inl.h" -#include "jni_internal.h" +#include "jni/jni_env_ext-inl.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "monitor.h" diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 699004298e..d9b8a84e55 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -41,7 +41,7 @@ #include "art_jvmti.h" #include "gc/allocation_listener.h" #include "instrumentation.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/object.h" diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index 136b1d3e1a..813aa8eb83 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -41,7 +41,7 @@ #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" #include "events-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 261fe3e810..c9d71b4857 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -54,8 +54,8 @@ #include "gc/heap.h" #include "gc_root.h" #include "handle.h" -#include "jni_env_ext-inl.h" -#include "jni_internal.h" +#include "jni/jni_env_ext-inl.h" +#include "jni/jni_internal.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h index 95278f4b2d..9b04841eb3 100644 --- a/openjdkjvmti/ti_class_loader-inl.h +++ b/openjdkjvmti/ti_class_loader-inl.h @@ -36,7 +36,7 @@ #include "art_field-inl.h" #include "handle.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object.h" #include "mirror/object_array-inl.h" #include "well_known_classes.h" diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index 3df5de909d..9a32849ed0 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -46,7 +46,7 @@ #include "instrumentation.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h index 142e2e1588..a3857e595a 100644 --- a/openjdkjvmti/ti_class_loader.h +++ b/openjdkjvmti/ti_class_loader.h @@ -45,7 +45,7 @@ #include "dex/dex_file.h" #include "dex/utf.h" #include "gc_root-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" #include "mirror/array-inl.h" diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc index c016966d21..328e2a1e40 100644 --- a/openjdkjvmti/ti_field.cc +++ b/openjdkjvmti/ti_field.cc @@ -36,7 +36,7 @@ #include "base/enums.h" #include "dex/dex_file_annotations.h" #include "dex/modifiers.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object_array-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index d0a7cf0657..d23370bc5c 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -26,8 +26,8 @@ #include "gc/heap.h" #include "gc_root-inl.h" #include "java_frame_root_info.h" -#include "jni_env_ext.h" -#include "jni_internal.h" +#include "jni/jni_env_ext.h" +#include "jni/jni_internal.h" #include "jvmti_weak_table-inl.h" #include "mirror/class.h" #include "mirror/object-inl.h" diff --git a/openjdkjvmti/ti_jni.cc b/openjdkjvmti/ti_jni.cc index dd2dda118a..b655d6a8e1 100644 --- a/openjdkjvmti/ti_jni.cc +++ b/openjdkjvmti/ti_jni.cc @@ -35,8 +35,8 @@ #include "art_jvmti.h" #include "base/mutex.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "runtime.h" #include "thread-current-inl.h" diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index b83310dc85..c0c312c490 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -44,7 +44,7 @@ #include "events-inl.h" #include "gc_root-inl.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index a23baa5095..8a726bca14 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -58,7 +58,7 @@ #include "jdwp/object_registry.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti_allocator.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index e14b7ae1c8..227eacd180 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -45,7 +45,7 @@ #include "dex/dex_file.h" #include "dex/utf.h" #include "gc_root-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvmti.h" #include "linear_alloc.h" #include "mirror/array-inl.h" diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc index cbb7b53bff..bcbab14cdd 100644 --- a/openjdkjvmti/ti_search.cc +++ b/openjdkjvmti/ti_search.cc @@ -41,7 +41,7 @@ #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object.h" #include "mirror/string.h" diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 4526be4cbe..eee8108b01 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -50,8 +50,8 @@ #include "dex/dex_file_types.h" #include "gc_root.h" #include "handle_scope-inl.h" -#include "jni_env_ext.h" -#include "jni_internal.h" +#include "jni/jni_env_ext.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/dex_cache.h" #include "nativehelper/scoped_local_ref.h" diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 414139c7b4..cabf9e8b09 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -43,7 +43,7 @@ #include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" #include "gc_root-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc index c0597ad0cc..e17e61fe62 100644 --- a/openjdkjvmti/ti_threadgroup.cc +++ b/openjdkjvmti/ti_threadgroup.cc @@ -37,7 +37,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object-inl.h" #include "mirror/string.h" diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 29a9b10ca2..8797553b07 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -48,7 +48,7 @@ #include "events-inl.h" #include "fault_handler.h" #include "gc_root-inl.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "jvalue.h" #include "jvmti.h" #include "linear_alloc.h" diff --git a/runtime/Android.bp b/runtime/Android.bp index 28bee3d67e..64e6796ba0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -100,7 +100,6 @@ cc_defaults { "interpreter/shadow_frame.cc", "interpreter/unstarted_runtime.cc", "java_frame_root_info.cc", - "java_vm_ext.cc", "jdwp/jdwp_event.cc", "jdwp/jdwp_expand_buf.cc", "jdwp/jdwp_handler.cc", @@ -108,14 +107,14 @@ cc_defaults { "jdwp/jdwp_request.cc", "jdwp/jdwp_socket.cc", "jdwp/object_registry.cc", - "jni_env_ext.cc", "jit/debugger_interface.cc", "jit/jit.cc", "jit/jit_code_cache.cc", "jit/profiling_info.cc", "jit/profile_saver.cc", - "jni_internal.cc", - "jobject_comparator.cc", + "jni/java_vm_ext.cc", + "jni/jni_env_ext.cc", + "jni/jni_internal.cc", "linear_alloc.cc", "managed_stack.cc", "method_handles.cc", @@ -397,6 +396,7 @@ cc_defaults { "libbacktrace", "liblz4", "liblog", + "libmetricslogger", // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted. "libcutils", // For common macros. @@ -422,6 +422,7 @@ gensrcs { srcs: [ "arch/instruction_set.h", "base/mutex.h", + "class_loader_context.h", "class_status.h", "debugger.h", "gc_root.h", @@ -580,7 +581,7 @@ art_cc_test { "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", "jdwp/jdwp_options_test.cc", - "java_vm_ext_test.cc", + "jni/java_vm_ext_test.cc", "method_handles_test.cc", "mirror/dex_cache_test.cc", "mirror/method_type_test.cc", @@ -620,7 +621,7 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ - "jni_internal_test.cc", + "jni/jni_internal_test.cc", "proxy_test.cc", "reflection_test.cc", ], diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index 801254fd30..608999b3bf 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -46,9 +46,11 @@ ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant( "cortex-a53", "cortex-a53.a57", "cortex-a53.a72", + "cortex-a55", "cortex-a57", "cortex-a72", "cortex-a73", + "cortex-a75", "exynos-m1", "denver", "kryo" diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index a930cc494e..cd00125de5 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1015,7 +1015,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1040,6 +1043,8 @@ END \name ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 42c9a846d0..d0f61c946c 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -52,6 +52,8 @@ Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( // Check to see if this is an expected variant. static const char* arm64_known_variants[] = { "cortex-a35", + "cortex-a55", + "cortex-a75", "exynos-m1", "exynos-m2", "exynos-m3", diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc index 7fd39b6b1b..b946f4f637 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc @@ -64,6 +64,26 @@ TEST(Arm64InstructionSetFeaturesTest, Arm64Features) { EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get())); EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str()); EXPECT_EQ(kryo_features->AsBitmap(), 0U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a55_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg)); + ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get())); + EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U); + + std::unique_ptr<const InstructionSetFeatures> cortex_a75_features( + InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg)); + ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg; + EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get())); + EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get())); + EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get())); + EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str()); + EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U); } } // namespace art diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 9ff5ebede3..ac5b2b8b88 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1533,7 +1533,10 @@ ENTRY \name END \name .endm -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name @@ -1577,6 +1580,8 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 9418caf98c..5d6e410101 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -203,6 +203,10 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct."); qpoints->pResolveString = art_quick_resolve_string; static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct."); + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodHandle), "Non-direct C stub marked direct."); + qpoints->pResolveMethodType = art_quick_resolve_method_type; + static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct."); // Field qpoints->pSet8Instance = art_quick_set8_instance; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index d8fe480719..c367ea60c2 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2027,8 +2027,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -2053,6 +2056,18 @@ END \name .endm /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast * path check for hit in strings cache has already been performed. diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8d2a7bd6c1..1f4f174e26 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1930,8 +1930,11 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -// Macro for string and type resolution and initialization. -// $a0 is both input and output. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. $a0 is both input and + * output. + */ .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY_NO_GP \name @@ -1953,6 +1956,18 @@ END \name .endm /* + * Entry from managed code to resolve a method handle. On entry, A0 holds the method handle + * index. On success the MethodHandle is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode + + /* + * Entry from managed code to resolve a method type. On entry, A0 holds the method type index. + * On success the MethodType is returned, otherwise an exception is raised. + */ +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode + + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast * path check for hit in strings cache has already been performed. diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 4be4b12611..78516e3aeb 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -24,7 +24,7 @@ #include "common_runtime_test.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "imt_conflict_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index df43aef94b..8ab4ce160f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -923,7 +923,10 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset // save ref containing registers for GC @@ -932,7 +935,7 @@ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset CFI_ADJUST_CFA_OFFSET(8) pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH eax // pass arg1 + PUSH eax // pass the index of the constant as arg1 call CALLVAR(cxx_name) // cxx_name(arg1, Thread*) addl MACRO_LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) @@ -1278,6 +1281,8 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFr ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 4f941e1c48..eb945ed366 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -951,12 +951,15 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO -// Macro for string and type resolution and initialization. + /* + * Macro for resolution and initialization of indexed DEX file + * constants such as classes and strings. + */ MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for GC // Outgoing argument set up - movl %eax, %edi // pass string index + movl %eax, %edi // pass the index of the constant as arg0 movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) testl %eax, %eax // If result is null, deliver the OOME. @@ -1298,6 +1301,8 @@ END_FUNCTION art_quick_alloc_object_initialized_region_tlab ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_handle, artResolveMethodHandleFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/art_field.h b/runtime/art_field.h index 29d71af358..f39af3900c 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -20,6 +20,7 @@ #include <jni.h> #include "dex/dex_file_types.h" +#include "dex/hidden_api_access_flags.h" #include "dex/modifiers.h" #include "dex/primitive.h" #include "gc_root.h" @@ -179,6 +180,10 @@ class ArtField FINAL { return (GetAccessFlags() & kAccVolatile) != 0; } + HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_) { + return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags()); + } + // Returns an instance field with this offset in the given class or null if not found. // If kExactOffset is true then we only find the matching offset, not the field containing the // offset. diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 1565644380..c1fac364bb 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -384,19 +384,68 @@ inline bool ArtMethod::HasSingleImplementation() { return (GetAccessFlags<kReadBarrierOption>() & kAccSingleImplementation) != 0; } -inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) { - switch (static_cast<Intrinsics>(ordinal)) { - case Intrinsics::kReferenceGetReferent: - case Intrinsics::kSystemArrayCopyChar: - case Intrinsics::kStringGetCharsNoCheck: - case Intrinsics::kVarHandleFullFence: - case Intrinsics::kVarHandleAcquireFence: - case Intrinsics::kVarHandleReleaseFence: - case Intrinsics::kVarHandleLoadLoadFence: - case Intrinsics::kVarHandleStoreStoreFence: - return true; - default: - return false; +inline HiddenApiAccessFlags::ApiList ArtMethod::GetHiddenApiAccessFlags() + REQUIRES_SHARED(Locks::mutator_lock_) { + if (UNLIKELY(IsIntrinsic())) { + switch (static_cast<Intrinsics>(GetIntrinsic())) { + case Intrinsics::kSystemArrayCopyChar: + case Intrinsics::kStringGetCharsNoCheck: + case Intrinsics::kReferenceGetReferent: + // These intrinsics are on the light greylist and will fail a DCHECK in + // SetIntrinsic() if their flags change on the respective dex methods. + // Note that the DCHECK currently won't fail if the dex methods are + // whitelisted, e.g. in the core image (b/77733081). As a result, we + // might print warnings but we won't change the semantics. + return HiddenApiAccessFlags::kLightGreylist; + case Intrinsics::kVarHandleFullFence: + case Intrinsics::kVarHandleAcquireFence: + case Intrinsics::kVarHandleReleaseFence: + case Intrinsics::kVarHandleLoadLoadFence: + case Intrinsics::kVarHandleStoreStoreFence: + case Intrinsics::kVarHandleCompareAndExchange: + case Intrinsics::kVarHandleCompareAndExchangeAcquire: + case Intrinsics::kVarHandleCompareAndExchangeRelease: + case Intrinsics::kVarHandleCompareAndSet: + case Intrinsics::kVarHandleGet: + case Intrinsics::kVarHandleGetAcquire: + case Intrinsics::kVarHandleGetAndAdd: + case Intrinsics::kVarHandleGetAndAddAcquire: + case Intrinsics::kVarHandleGetAndAddRelease: + case Intrinsics::kVarHandleGetAndBitwiseAnd: + case Intrinsics::kVarHandleGetAndBitwiseAndAcquire: + case Intrinsics::kVarHandleGetAndBitwiseAndRelease: + case Intrinsics::kVarHandleGetAndBitwiseOr: + case Intrinsics::kVarHandleGetAndBitwiseOrAcquire: + case Intrinsics::kVarHandleGetAndBitwiseOrRelease: + case Intrinsics::kVarHandleGetAndBitwiseXor: + case Intrinsics::kVarHandleGetAndBitwiseXorAcquire: + case Intrinsics::kVarHandleGetAndBitwiseXorRelease: + case Intrinsics::kVarHandleGetAndSet: + case Intrinsics::kVarHandleGetAndSetAcquire: + case Intrinsics::kVarHandleGetAndSetRelease: + case Intrinsics::kVarHandleGetOpaque: + case Intrinsics::kVarHandleGetVolatile: + case Intrinsics::kVarHandleSet: + case Intrinsics::kVarHandleSetOpaque: + case Intrinsics::kVarHandleSetRelease: + case Intrinsics::kVarHandleSetVolatile: + case Intrinsics::kVarHandleWeakCompareAndSet: + case Intrinsics::kVarHandleWeakCompareAndSetAcquire: + case Intrinsics::kVarHandleWeakCompareAndSetPlain: + case Intrinsics::kVarHandleWeakCompareAndSetRelease: + // These intrinsics are on the blacklist and will fail a DCHECK in + // SetIntrinsic() if their flags change on the respective dex methods. + // Note that the DCHECK currently won't fail if the dex methods are + // whitelisted, e.g. in the core image (b/77733081). Given that they are + // exclusively VarHandle intrinsics, they should not be used outside + // tests that do not enable hidden API checks. + return HiddenApiAccessFlags::kBlacklist; + default: + // Remaining intrinsics are public API. We DCHECK that in SetIntrinsic(). + return HiddenApiAccessFlags::kWhitelist; + } + } else { + return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags()); } } @@ -422,7 +471,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { bool is_default_conflict = IsDefaultConflicting(); bool is_compilable = IsCompilable(); bool must_count_locks = MustCountLocks(); - HiddenApiAccessFlags::ApiList hidden_api_list = GetHiddenApiAccessFlags(); + HiddenApiAccessFlags::ApiList hidden_api_flags = GetHiddenApiAccessFlags(); SetAccessFlags(new_value); DCHECK_EQ(java_flags, (GetAccessFlags() & kAccJavaFlagsMask)); DCHECK_EQ(is_constructor, IsConstructor()); @@ -436,14 +485,14 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { DCHECK_EQ(is_default_conflict, IsDefaultConflicting()); DCHECK_EQ(is_compilable, IsCompilable()); DCHECK_EQ(must_count_locks, MustCountLocks()); - if (kIsDebugBuild) { - if (IsHiddenIntrinsic(intrinsic)) { - // Special case some of our intrinsics because the access flags clash - // with the intrinsics ordinal. - DCHECK_EQ(HiddenApiAccessFlags::kWhitelist, GetHiddenApiAccessFlags()); - } else { - DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags()); - } + // Only DCHECK that we have preserved the hidden API access flags if the + // original method was not on the whitelist. This is because the core image + // does not have the access flags set (b/77733081). It is fine to hard-code + // these because (a) warnings on greylist do not change semantics, and + // (b) only VarHandle intrinsics are blacklisted at the moment and they + // should not be used outside tests with disabled API checks. + if (hidden_api_flags != HiddenApiAccessFlags::kWhitelist) { + DCHECK_EQ(hidden_api_flags, GetHiddenApiAccessFlags()); } } else { SetAccessFlags(new_value); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 41b01c251b..87fcb20698 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -35,7 +35,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/executable.h" diff --git a/runtime/art_method.h b/runtime/art_method.h index 64d293200f..acaa4a68a1 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -341,9 +341,7 @@ class ArtMethod FINAL { AddAccessFlags(kAccMustCountLocks); } - HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() { - return HiddenApiAccessFlags::DecodeFromRuntime(GetAccessFlags()); - } + HiddenApiAccessFlags::ApiList GetHiddenApiAccessFlags() REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if this method could be overridden by a default method. bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_); @@ -873,9 +871,6 @@ class ArtMethod FINAL { } while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags)); } - // Returns true if the given intrinsic is considered hidden. - bool IsHiddenIntrinsic(uint32_t ordinal); - DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits. }; diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 2f7d6ab98f..70ff40d32c 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -73,7 +73,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // Offset of field Thread::tlsPtr_.mterp_current_ibase. #define THREAD_CURRENT_IBASE_OFFSET \ - (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 162) * __SIZEOF_POINTER__) + (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 7921985b15..537216c198 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -261,12 +261,12 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string& } } -bool LocationIsOnSystem(const char* location) { - UniqueCPtr<const char[]> path(realpath(location, nullptr)); - return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str()); +bool LocationIsOnSystem(const char* path) { + UniqueCPtr<const char[]> full_path(realpath(path, nullptr)); + return path != nullptr && android::base::StartsWith(full_path.get(), GetAndroidRoot().c_str()); } -bool LocationIsOnSystemFramework(const char* location) { +bool LocationIsOnSystemFramework(const char* full_path) { std::string error_msg; std::string root_path = GetAndroidRootSafe(&error_msg); if (root_path.empty()) { @@ -275,12 +275,7 @@ bool LocationIsOnSystemFramework(const char* location) { return false; } std::string framework_path = root_path + "/framework/"; - - // Warning: Bionic implementation of realpath() allocates > 12KB on the stack. - // Do not run this code on a small stack, e.g. in signal handler. - UniqueCPtr<const char[]> path(realpath(location, nullptr)); - return path != nullptr && - android::base::StartsWith(path.get(), framework_path.c_str()); + return android::base::StartsWith(full_path, framework_path); } } // namespace art diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 73b464119e..da286d7e41 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -195,8 +195,8 @@ class ScopedContentionRecorder FINAL : public ValueObject { }; BaseMutex::BaseMutex(const char* name, LockLevel level) - : level_(level), - name_(name), + : name_(name), + level_(level), should_respond_to_empty_checkpoint_request_(false) { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); @@ -386,7 +386,7 @@ void BaseMutex::DumpContention(std::ostream& os) const { Mutex::Mutex(const char* name, LockLevel level, bool recursive) - : BaseMutex(name, level), exclusive_owner_(0), recursive_(recursive), recursion_count_(0) { + : BaseMutex(name, level), exclusive_owner_(0), recursion_count_(0), recursive_(recursive) { #if ART_USE_FUTEXES DCHECK_EQ(0, state_.load(std::memory_order_relaxed)); DCHECK_EQ(0, num_contenders_.load(std::memory_order_relaxed)); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 1cf4ddded4..602d183bbb 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -57,7 +57,7 @@ class Mutex; // partial ordering and thereby cause deadlock situations to fail checks. // // [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163 -enum LockLevel { +enum LockLevel : uint8_t { kLoggingLock = 0, kSwapMutexesLock, kUnexpectedSignalLock, @@ -142,20 +142,20 @@ enum LockLevel { }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); -const bool kDebugLocking = kIsDebugBuild; +constexpr bool kDebugLocking = kIsDebugBuild; // Record Log contention information, dumpable via SIGQUIT. #ifdef ART_USE_FUTEXES // To enable lock contention logging, set this to true. -const bool kLogLockContentions = false; +constexpr bool kLogLockContentions = false; #else // Keep this false as lock contention logging is supported only with // futex. -const bool kLogLockContentions = false; +constexpr bool kLogLockContentions = false; #endif -const size_t kContentionLogSize = 4; -const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; -const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; +constexpr size_t kContentionLogSize = 4; +constexpr size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0; +constexpr size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0; // Base class for all Mutex implementations class BaseMutex { @@ -196,9 +196,7 @@ class BaseMutex { void RecordContention(uint64_t blocked_tid, uint64_t owner_tid, uint64_t nano_time_blocked); void DumpContention(std::ostream& os) const; - const LockLevel level_; // Support for lock hierarchy. const char* const name_; - bool should_respond_to_empty_checkpoint_request_; // A log entry that records contention but makes no guarantee that either tid will be held live. struct ContentionLogEntry { @@ -221,6 +219,9 @@ class BaseMutex { }; ContentionLogData contention_log_data_[kContentionLogDataSize]; + const LockLevel level_; // Support for lock hierarchy. + bool should_respond_to_empty_checkpoint_request_; + public: bool HasEverContended() const { if (kLogLockContentions) { @@ -307,8 +308,10 @@ class LOCKABLE Mutex : public BaseMutex { pthread_mutex_t mutex_; Atomic<pid_t> exclusive_owner_; // Guarded by mutex_. Asynchronous reads are OK. #endif - const bool recursive_; // Can the lock be recursively held? + unsigned int recursion_count_; + const bool recursive_; // Can the lock be recursively held? + friend class ConditionVariable; DISALLOW_COPY_AND_ASSIGN(Mutex); }; diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 9a43790575..f8b977eea7 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -34,8 +34,8 @@ #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "gc/space/space.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/field.h" #include "mirror/method.h" diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 57c0d9dab2..4141a37366 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -79,11 +79,11 @@ #include "imtable-inl.h" #include "intern_table.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "mirror/call_site.h" #include "mirror/class-inl.h" @@ -7884,6 +7884,40 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass, return resolved; } +// Returns true if `method` is either null or hidden. +// Does not print any warnings if it is hidden. +static bool CheckNoSuchMethod(ArtMethod* method, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + return method == nullptr || + hiddenapi::GetMemberAction(method, + class_loader, + dex_cache, + hiddenapi::kNone) // do not print warnings + == hiddenapi::kDeny; +} + +ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader, + uint32_t method_idx) { + if (klass->IsInterface()) { + ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); + return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method; + } else { + // If there was an interface method with the same signature, we would have + // found it in the "copied" methods. Only DCHECK that the interface method + // really does not exist. + if (kIsDebugBuild) { + ArtMethod* method = + klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_); + DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader)); + } + return nullptr; + } +} + template <ClassLinker::ResolveMode kResolveMode> ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, @@ -7959,13 +7993,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, // If we had a method, or if we can find one with another lookup type, // it's an incompatible-class-change error. if (resolved == nullptr) { - if (klass->IsInterface()) { - resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size); - } else { - // If there was an interface method with the same signature, - // we would have found it also in the "copied" methods. - DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr); - } + resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); } if (resolved != nullptr) { ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index fa70f65bca..e935d1dfb8 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -329,6 +329,15 @@ class ClassLinker { uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); + // Find a method using the wrong lookup mechanism. If `klass` is an interface, + // search for a class method. If it is a class, search for an interface method. + // This is useful when throwing IncompatibleClassChangeError. + ArtMethod* FindIncompatibleMethod(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/class_loader_context.cc b/runtime/class_loader_context.cc index 4afc44cb91..2bd541118b 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -25,7 +25,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "runtime.h" @@ -672,9 +672,10 @@ static bool IsAbsoluteLocation(const std::string& location) { return !location.empty() && location[0] == '/'; } -bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec, - bool verify_names, - bool verify_checksums) const { +ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch( + const std::string& context_spec, + bool verify_names, + bool verify_checksums) const { if (verify_names || verify_checksums) { DCHECK(dex_files_open_attempted_); DCHECK(dex_files_open_result_); @@ -683,15 +684,21 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, verify_checksums)) { LOG(WARNING) << "Invalid class loader context: " << context_spec; - return false; + return VerificationResult::kMismatch; } // Special shared library contexts always match. They essentially instruct the runtime // to ignore the class path check because the oat file is known to be loaded in different // contexts. OatFileManager will further verify if the oat file can be loaded based on the // collision check. - if (special_shared_library_ || expected_context.special_shared_library_) { - return true; + if (expected_context.special_shared_library_) { + // Special case where we are the only entry in the class path. + if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) { + return VerificationResult::kVerifies; + } + return VerificationResult::kForcedToSkipChecks; + } else if (special_shared_library_) { + return VerificationResult::kForcedToSkipChecks; } if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) { @@ -699,7 +706,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << expected_context.class_loader_chain_.size() << ", actual=" << class_loader_chain_.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } for (size_t i = 0; i < class_loader_chain_.size(); i++) { @@ -710,14 +717,14 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << GetClassLoaderTypeName(expected_info.type) << ", found=" << GetClassLoaderTypeName(info.type) << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (info.classpath.size() != expected_info.classpath.size()) { LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i << ". expected=" << expected_info.classpath.size() << ", found=" << info.classpath.size() << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } if (verify_checksums) { @@ -772,7 +779,7 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } // Compare the checksums. @@ -781,11 +788,11 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex << ". expected=" << expected_info.checksums[k] << ", found=" << info.checksums[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; - return false; + return VerificationResult::kMismatch; } } } - return true; + return VerificationResult::kVerifies; } jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 1c83007f41..a4268aa09a 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -22,8 +22,10 @@ #include "arch/instruction_set.h" #include "base/dchecked_vector.h" +#include "dex/dex_file.h" #include "handle_scope.h" #include "mirror/class_loader.h" +#include "oat_file.h" #include "scoped_thread_state_change.h" namespace art { @@ -34,6 +36,18 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: + enum class VerificationResult { + kVerifies, + kForcedToSkipChecks, + kMismatch, + }; + + enum ClassLoaderType { + kInvalidClassLoader = 0, + kPathClassLoader = 1, + kDelegateLastClassLoader = 2 + }; + ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -109,7 +123,7 @@ class ClassLoaderContext { // This should be called after OpenDexFiles(). // Names are only verified if verify_names is true. // Checksums are only verified if verify_checksums is true. - bool VerifyClassLoaderContextMatch(const std::string& context_spec, + VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec, bool verify_names = true, bool verify_checksums = true) const; @@ -141,12 +155,6 @@ class ClassLoaderContext { static std::unique_ptr<ClassLoaderContext> Default(); private: - enum ClassLoaderType { - kInvalidClassLoader = 0, - kPathClassLoader = 1, - kDelegateLastClassLoader = 2 - }; - struct ClassLoaderInfo { // The type of this class loader. ClassLoaderType type; diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 4689ae4c3f..5e3f48c100 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -608,6 +608,17 @@ TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); } + +TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) { + std::string context_spec = "PCL[]"; + std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); + ASSERT_TRUE(context != nullptr); + PretendContextOpenedDexFiles(context.get()); + // Ensure that the special shared library marks as verified for the first thing in the class path. + ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary), + ClassLoaderContext::VerificationResult::kVerifies); +} + TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); @@ -619,28 +630,36 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), + ClassLoaderContext::VerificationResult::kVerifies); std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), + ClassLoaderContext::VerificationResult::kMismatch); std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; - ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), + ClassLoaderContext::VerificationResult::kMismatch); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { @@ -652,7 +671,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); std::string dex_location = GetTestDexFileName("ForClassLoaderA"); size_t pos = dex_location.rfind('/'); @@ -661,7 +681,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::string context_with_base_dir = context->EncodeContextForOatFile(parent); ASSERT_NE(context_with_base_dir, context_with_no_base_dir); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), + ClassLoaderContext::VerificationResult::kVerifies); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { @@ -669,7 +690,8 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultide std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), + ClassLoaderContext::VerificationResult::kVerifies); } } // namespace art diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h index 1439f11636..af42878e97 100644 --- a/runtime/class_loader_utils.h +++ b/runtime/class_loader_utils.h @@ -20,7 +20,7 @@ #include "art_field-inl.h" #include "base/mutex.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index f5b15ec239..75b091d98f 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -48,8 +48,8 @@ #include "gtest/gtest.h" #include "handle_scope-inl.h" #include "interpreter/unstarted_runtime.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "native/dalvik_system_DexFile.h" diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 28659cb11d..88628bbc50 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -51,7 +51,7 @@ #include "handle_scope-inl.h" #include "jdwp/jdwp_priv.h" #include "jdwp/object_registry.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/class.h" diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc index 415e451098..392ce1e7f5 100644 --- a/runtime/dex/art_dex_file_loader.cc +++ b/runtime/dex/art_dex_file_loader.cc @@ -534,7 +534,10 @@ std::unique_ptr<DexFile> ArtDexFileLoader::OpenCommon(const uint8_t* base, // Check if this dex file is located in the framework directory. // If it is, set a flag on the dex file. This is used by hidden API // policy decision logic. - if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) { + // Location can contain multidex suffix, so fetch its canonical version. Note + // that this will call `realpath`. + std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str()); + if (dex_file != nullptr && LocationIsOnSystemFramework(path.c_str())) { dex_file->SetIsPlatformDexFile(); } diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index aee397b65b..274a6df702 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -49,20 +49,31 @@ class ArtDexFileLoaderTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); std::string dex_location = GetTestDexFileName("Main"); + std::string multidex_location = GetTestDexFileName("MultiDex"); data_location_path_ = android_data_ + "/foo.jar"; system_location_path_ = GetAndroidRoot() + "/foo.jar"; system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; + data_multi_location_path_ = android_data_ + "/multifoo.jar"; + system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar"; + system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar"; Copy(dex_location, data_location_path_); Copy(dex_location, system_location_path_); Copy(dex_location, system_framework_location_path_); + + Copy(multidex_location, data_multi_location_path_); + Copy(multidex_location, system_multi_location_path_); + Copy(multidex_location, system_framework_multi_location_path_); } virtual void TearDown() { remove(data_location_path_.c_str()); remove(system_location_path_.c_str()); remove(system_framework_location_path_.c_str()); + remove(data_multi_location_path_.c_str()); + remove(system_multi_location_path_.c_str()); + remove(system_framework_multi_location_path_.c_str()); CommonRuntimeTest::TearDown(); } @@ -70,6 +81,9 @@ class ArtDexFileLoaderTest : public CommonRuntimeTest { std::string data_location_path_; std::string system_location_path_; std::string system_framework_location_path_; + std::string data_multi_location_path_; + std::string system_multi_location_path_; + std::string system_framework_multi_location_path_; }; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and @@ -390,6 +404,53 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); } + + dex_files.clear(); + + // Load multidex file from a non-system directory and check that it is not flagged as framework. + success = loader.Open(data_multi_location_path_.c_str(), + data_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr<const DexFile>& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load multidex file from a system, non-framework directory and check that it is not flagged + // as framework. + success = loader.Open(system_multi_location_path_.c_str(), + system_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr<const DexFile>& dex_file : dex_files) { + ASSERT_FALSE(dex_file->IsPlatformDexFile()); + } + + dex_files.clear(); + + // Load multidex file from a system/framework directory and check that it is flagged as a + // framework dex. + success = loader.Open(system_framework_multi_location_path_.c_str(), + system_framework_multi_location_path_, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success); + ASSERT_GT(dex_files.size(), 1u); + for (std::unique_ptr<const DexFile>& dex_file : dex_files) { + ASSERT_TRUE(dex_file->IsPlatformDexFile()); + } } } // namespace art diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc index 6f3354b724..c399b1c7fe 100644 --- a/runtime/dex/dex_file_annotations.cc +++ b/runtime/dex/dex_file_annotations.cc @@ -24,7 +24,7 @@ #include "art_method-inl.h" #include "class_linker-inl.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "mirror/field.h" #include "mirror/method.h" diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 137eb4fe1e..d4e7492f00 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -31,7 +31,7 @@ #include "imt_conflict_table.h" #include "imtable-inl.h" #include "indirect_reference_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index ffa138d5b1..a58946ae66 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -26,7 +26,7 @@ #include "entrypoints/quick/callee_save_frame.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" @@ -260,4 +260,26 @@ ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } +ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) { + Thread::PoisonObjectPointersIfDebug(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + return class_linker->ResolveMethodHandle(Thread::Current(), method_handle_idx, referrer); +} + +ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, + uint32_t proto_idx) { + Thread::PoisonObjectPointersIfDebug(); + ObjPtr<mirror::MethodType> method_type = + referrer->GetDexCache()->GetResolvedMethodType(proto_idx); + if (UNLIKELY(method_type == nullptr)) { + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + method_type = class_linker->ResolveMethodType(hs.Self(), proto_idx, dex_cache, class_loader); + } + return method_type; +} + } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index eb32153b16..0a3b5dfc93 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -34,6 +34,8 @@ namespace art { namespace mirror { class Array; class Class; +class MethodHandle; +class MethodType; class Object; class String; } // namespace mirror @@ -151,6 +153,15 @@ inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); +ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer, + uint32_t method_handle_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + +ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); + inline ObjPtr<mirror::String> ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 780e221129..a4083a4f81 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -18,7 +18,7 @@ #include "art_method-inl.h" #include "entrypoints/entrypoint_utils.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/object-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 2d0932a0c4..1804d9e64d 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -37,6 +37,8 @@ extern "C" void art_quick_check_instance_of(art::mirror::Object*, art::mirror::C extern "C" void* art_quick_initialize_static_storage(uint32_t); extern "C" void* art_quick_initialize_type(uint32_t); extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t); +extern "C" void* art_quick_resolve_method_handle(uint32_t); +extern "C" void* art_quick_resolve_method_type(uint32_t); extern "C" void* art_quick_resolve_string(uint32_t); // Field entrypoints. diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8c90800463..3f66045576 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -37,6 +37,8 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qp qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; qpoints->pInitializeType = art_quick_initialize_type; + qpoints->pResolveMethodHandle = art_quick_resolve_method_handle; + qpoints->pResolveMethodType = art_quick_resolve_method_type; qpoints->pResolveString = art_quick_resolve_string; // Field diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index cfb427f1ac..cf9ddd8aa8 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -183,6 +183,27 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type return result.Ptr(); } +extern "C" mirror::MethodHandle* artResolveMethodHandleFromCode(uint32_t method_handle_idx, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = + GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr<mirror::MethodHandle> result = ResolveMethodHandleFromCode(caller, method_handle_idx); + return result.Ptr(); +} + +extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, + CalleeSaveType::kSaveEverything); + ArtMethod* caller = caller_and_outer.caller; + ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, proto_idx); + return result.Ptr(); +} + extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 48a56f2fbf..3a8faca11d 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -38,6 +38,8 @@ V(InitializeStaticStorage, void*, uint32_t) \ V(InitializeTypeAndVerifyAccess, void*, uint32_t) \ V(InitializeType, void*, uint32_t) \ + V(ResolveMethodHandle, void*, uint32_t) \ + V(ResolveMethodType, void*, uint32_t) \ V(ResolveString, void*, uint32_t) \ \ V(Set8Instance, int, uint32_t, void*, int8_t) \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 1fdf439d3f..1337cd5fb2 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -183,7 +183,9 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodHandle, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodHandle, pResolveMethodType, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, pResolveString, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, sizeof(void*)); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 1e136bca2e..681ac2ef28 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -39,7 +39,7 @@ #include "gc/space/space-inl.h" #include "indirect_reference_table.h" #include "intern_table.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mark_sweep-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e85824de70..b004566ed1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -71,9 +71,9 @@ #include "heap-visit-objects-inl.h" #include "image.h" #include "intern_table.h" -#include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 356f3ecaa8..5be7b325d0 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -19,7 +19,7 @@ #include "base/time_utils.h" #include "base/utils.h" #include "collector/garbage_collector.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index 811d6db876..a12917140b 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -299,10 +299,17 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { public: Region() : idx_(static_cast<size_t>(-1)), - begin_(nullptr), top_(nullptr), end_(nullptr), - state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace), - objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)), - is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {} + live_bytes_(static_cast<size_t>(-1)), + begin_(nullptr), + thread_(nullptr), + top_(nullptr), + end_(nullptr), + objects_allocated_(0), + alloc_time_(0), + is_newly_allocated_(false), + is_a_tlab_(false), + state_(RegionState::kRegionStateAllocated), + type_(RegionType::kRegionTypeToSpace) {} void Init(size_t idx, uint8_t* begin, uint8_t* end) { idx_ = idx; @@ -496,22 +503,22 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { private: size_t idx_; // The region's index in the region space. + size_t live_bytes_; // The live bytes. Used to compute the live percent. uint8_t* begin_; // The begin address of the region. + Thread* thread_; // The owning thread if it's a tlab. // 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. - Thread* thread_; // The owning thread if it's a tlab. + RegionState state_; // The region state (see RegionState). + RegionType type_; // The region type (see RegionType). friend class RegionSpace; }; diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 0e72f275d2..9445ae0c8e 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <metricslogger/metrics_logger.h> + #include "hidden_api.h" #include <nativehelper/scoped_local_ref.h> @@ -22,11 +24,28 @@ #include "thread-current-inl.h" #include "well_known_classes.h" +using android::metricslogger::ComplexEventLogger; +using android::metricslogger::ACTION_HIDDEN_API_ACCESSED; +using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD; +using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED; +using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE; + namespace art { namespace hiddenapi { +// Set to true if we should always print a warning in logcat for all hidden API accesses, not just +// dark grey and black. This can be set to true for developer preview / beta builds, but should be +// false for public release builds. +// Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi +// as it affects whether or not we warn for light grey APIs that have been added to the exemptions +// list. +static constexpr bool kLogAllAccesses = true; + static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { + case kNone: + LOG(FATAL) << "Internal access to hidden API should not be logged"; + UNREACHABLE(); case kReflection: os << "reflection"; break; @@ -46,42 +65,47 @@ static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags: // GetMemberAction-related static_asserts. static_assert( - EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) && EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) && EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist), "Mismatch between EnforcementPolicy and ApiList enums"); static_assert( - EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList && + EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList && EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly, "EnforcementPolicy values ordering not correct"); namespace detail { MemberSignature::MemberSignature(ArtField* field) { - member_type_ = "field"; - signature_parts_ = { - field->GetDeclaringClass()->GetDescriptor(&tmp_), - "->", - field->GetName(), - ":", - field->GetTypeDescriptor() - }; + class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_); + member_name_ = field->GetName(); + type_signature_ = field->GetTypeDescriptor(); + type_ = kField; } MemberSignature::MemberSignature(ArtMethod* method) { - member_type_ = "method"; - signature_parts_ = { - method->GetDeclaringClass()->GetDescriptor(&tmp_), - "->", - method->GetName(), - method->GetSignature().ToString() - }; + // If this is a proxy method, print the signature of the interface method. + method = method->GetInterfaceMethodIfProxy( + Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + + class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_); + member_name_ = method->GetName(); + type_signature_ = method->GetSignature().ToString(); + type_ = kMethod; +} + +inline std::vector<const char*> MemberSignature::GetSignatureParts() const { + if (type_ == kField) { + return { class_name_.c_str(), "->", member_name_.c_str(), ":", type_signature_.c_str() }; + } else { + DCHECK_EQ(type_, kMethod); + return { class_name_.c_str(), "->", member_name_.c_str(), type_signature_.c_str() }; + } } bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const { size_t pos = 0; - for (const std::string& part : signature_parts_) { - size_t count = std::min(prefix.length() - pos, part.length()); + for (const char* part : GetSignatureParts()) { + size_t count = std::min(prefix.length() - pos, strlen(part)); if (prefix.compare(pos, count, part, 0, count) == 0) { pos += count; } else { @@ -103,42 +127,115 @@ bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) { } void MemberSignature::Dump(std::ostream& os) const { - for (std::string part : signature_parts_) { + for (const char* part : GetSignatureParts()) { os << part; } } void MemberSignature::WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) { - LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this) - << " (" << list << ", " << access_method << ")"; + LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") + << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")"; +} +// Convert an AccessMethod enum to a value for logging from the proto enum. +// This method may look odd (the enum values are current the same), but it +// prevents coupling the internal enum to the proto enum (which should never +// be changed) so that we are free to change the internal one if necessary in +// future. +inline static int32_t GetEnumValueForLog(AccessMethod access_method) { + switch (access_method) { + case kNone: + return android::metricslogger::ACCESS_METHOD_NONE; + case kReflection: + return android::metricslogger::ACCESS_METHOD_REFLECTION; + case kJNI: + return android::metricslogger::ACCESS_METHOD_JNI; + case kLinking: + return android::metricslogger::ACCESS_METHOD_LINKING; + default: + DCHECK(false); + } +} + +void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { + if (access_method == kLinking) { + // Linking warnings come from static analysis/compilation of the bytecode + // and can contain false positives (i.e. code that is never run). We choose + // not to log these in the event log. + return; + } + ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED); + log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method)); + if (action_taken == kDeny) { + log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1); + } + std::ostringstream signature_str; + Dump(signature_str); + log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str()); + log_maker.Record(); +} + +static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { + return true; +} + +static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) { + return !method->IsIntrinsic(); } template<typename T> -Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { +static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) { + member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( + member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist)); + } +} + +template<typename T> +Action GetMemberActionImpl(T* member, + HiddenApiAccessFlags::ApiList api_list, + Action action, + AccessMethod access_method) { + DCHECK_NE(action, kAllow); + // Get the signature, we need it later. MemberSignature member_signature(member); Runtime* runtime = Runtime::Current(); - if (action == kDeny) { - // If we were about to deny, check for an exemption first. - // Exempted APIs are treated as light grey list. + // Check for an exemption first. Exempted APIs are treated as white list. + // We only do this if we're about to deny, or if the app is debuggable. This is because: + // - we only print a warning for light greylist violations for debuggable apps + // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs. + // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever + // possible. + if (kLogAllAccesses || action == kDeny || runtime->IsJavaDebuggable()) { if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) { - action = kAllowButWarn; + action = kAllow; // Avoid re-examining the exemption list next time. - // Note this results in the warning below showing "light greylist", which - // seems like what one would expect. Exemptions effectively add new members to - // the light greylist. - member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime( - member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist)); + // Note this results in no warning for the member, which seems like what one would expect. + // Exemptions effectively adds new members to the whitelist. + MaybeWhitelistMember(runtime, member); + return kAllow; + } + + if (access_method != kNone) { + // Print a log message with information about this class member access. + // We do this if we're about to block access, or the app is debuggable. + member_signature.WarnAboutAccess(access_method, api_list); } } - // Print a log message with information about this class member access. - // We do this regardless of whether we block the access or not. - member_signature.WarnAboutAccess(access_method, - HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); + if (kIsTargetBuild) { + uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); + // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. + static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); + if (eventLogSampleRate != 0 && + (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) { + member_signature.LogAccessToEventLog(access_method, action); + } + } if (action == kDeny) { // Block access @@ -148,16 +245,15 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) // 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 (access_method != kNone) { + // Depending on a runtime flag, we might move the member into whitelist and + // skip the warning the next time the member is accessed. + MaybeWhitelistMember(runtime, member); - // If this action requires a UI warning, set the appropriate flag. - if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { - runtime->SetPendingHiddenApiWarning(true); + // If this action requires a UI warning, set the appropriate flag. + if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) { + runtime->SetPendingHiddenApiWarning(true); + } } return action; @@ -165,9 +261,11 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) // Need to instantiate this. template Action GetMemberActionImpl<ArtField>(ArtField* member, + HiddenApiAccessFlags::ApiList api_list, Action action, AccessMethod access_method); template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member, + HiddenApiAccessFlags::ApiList api_list, Action action, AccessMethod access_method); } // namespace detail diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index ffdeacbfff..8e21fd3b8f 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -33,7 +33,7 @@ namespace hiddenapi { // frameworks/base/core/java/android/content/pm/ApplicationInfo.java enum class EnforcementPolicy { kNoChecks = 0, - kAllLists = 1, // ban anything but whitelist + kJustWarn = 1, // keep checks enabled, but allow everything (enables logging) kDarkGreyAndBlackList = 2, // ban dark grey & blacklist kBlacklistOnly = 3, // ban blacklist violations only kMax = kBlacklistOnly, @@ -53,22 +53,37 @@ enum Action { }; enum AccessMethod { + kNone, // internal test that does not correspond to an actual access by app kReflection, kJNI, kLinking, }; -inline Action GetActionFromAccessFlags(uint32_t access_flags) { +// Do not change the values of items in this enum, as they are written to the +// event log for offline analysis. Any changes will interfere with that analysis. +enum AccessContextFlags { + // Accessed member is a field if this bit is set, else a method + kMemberIsField = 1 << 0, + // Indicates if access was denied to the member, instead of just printing a warning. + kAccessDenied = 1 << 1, +}; + +inline Action GetActionFromAccessFlags(HiddenApiAccessFlags::ApiList api_list) { + if (api_list == HiddenApiAccessFlags::kWhitelist) { + return kAllow; + } + EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { // Exit early. Nothing to enforce. return kAllow; } - HiddenApiAccessFlags::ApiList api_list = HiddenApiAccessFlags::DecodeFromRuntime(access_flags); - if (api_list == HiddenApiAccessFlags::kWhitelist) { - return kAllow; + // if policy is "just warn", always warn. We returned above for whitelist APIs. + if (policy == EnforcementPolicy::kJustWarn) { + return kAllowButWarn; } + DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList); // The logic below relies on equality of values in the enums EnforcementPolicy and // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc. if (static_cast<int>(policy) > static_cast<int>(api_list)) { @@ -87,9 +102,18 @@ namespace detail { // is used as a helper when matching prefixes, and when logging the signature. class MemberSignature { private: - std::string member_type_; - std::vector<std::string> signature_parts_; + enum MemberType { + kField, + kMethod, + }; + + std::string class_name_; + std::string member_name_; + std::string type_signature_; std::string tmp_; + MemberType type_; + + inline std::vector<const char*> GetSignatureParts() const; public: explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); @@ -105,45 +129,72 @@ class MemberSignature { bool IsExempted(const std::vector<std::string>& exemptions); void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list); + + void LogAccessToEventLog(AccessMethod access_method, Action action_taken); }; template<typename T> -Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) +Action GetMemberActionImpl(T* member, + HiddenApiAccessFlags::ApiList api_list, + Action action, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the caller is either loaded by the boot strap class loader or comes from // a dex file located in ${ANDROID_ROOT}/framework/. ALWAYS_INLINE -inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader, - ObjPtr<mirror::DexCache> caller_dex_cache) +inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller, + ObjPtr<mirror::ClassLoader> caller_class_loader, + ObjPtr<mirror::DexCache> caller_dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { if (caller_class_loader.IsNull()) { + // Boot class loader. return true; - } else if (caller_dex_cache.IsNull()) { - return false; - } else { + } + + if (!caller_dex_cache.IsNull()) { const DexFile* caller_dex_file = caller_dex_cache->GetDexFile(); - return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile(); + if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) { + // Caller is in a platform dex file. + return true; + } } + + if (!caller.IsNull() && + caller->ShouldSkipHiddenApiChecks() && + Runtime::Current()->IsJavaDebuggable()) { + // We are in debuggable mode and this caller has been marked trusted. + return true; + } + + return false; } } // namespace detail // Returns true if access to `member` should be denied to the caller of the -// reflective query. The decision is based on whether the caller is in the -// platform or not. Because different users of this function determine this -// in a different way, `fn_caller_in_platform(self)` is called and should -// return true if the caller is located in the platform. +// reflective query. The decision is based on whether the caller is trusted or +// not. Because different users of this function determine this in a different +// way, `fn_caller_is_trusted(self)` is called and should return true if the +// caller is allowed to access the platform. // This function might print warnings into the log if the member is hidden. template<typename T> inline Action GetMemberAction(T* member, Thread* self, - std::function<bool(Thread*)> fn_caller_in_platform, + std::function<bool(Thread*)> fn_caller_is_trusted, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Action action = GetActionFromAccessFlags(member->GetAccessFlags()); + // Decode hidden API access flags. + // NB Multiple threads might try to access (and overwrite) these simultaneously, + // causing a race. We only do that if access has not been denied, so the race + // cannot change Java semantics. We should, however, decode the access flags + // once and use it throughout this function, otherwise we may get inconsistent + // results, e.g. print whitelist warnings (b/78327881). + HiddenApiAccessFlags::ApiList api_list = member->GetHiddenApiAccessFlags(); + + Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags()); if (action == kAllow) { // Nothing to do. return action; @@ -151,19 +202,18 @@ inline Action GetMemberAction(T* member, // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. - if (fn_caller_in_platform(self)) { - // Caller in the platform. Exit. + if (fn_caller_is_trusted(self)) { + // Caller is trusted. Exit. return kAllow; } // Member is hidden and caller is not in the platform. - return detail::GetMemberActionImpl(member, action, access_method); + return detail::GetMemberActionImpl(member, api_list, action, access_method); } -inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller) - REQUIRES_SHARED(Locks::mutator_lock_) { +inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) { return !caller.IsNull() && - detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache()); + detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache()); } // Returns true if access to `member` should be denied to a caller loaded with @@ -175,10 +225,11 @@ inline Action GetMemberAction(T* member, ObjPtr<mirror::DexCache> caller_dex_cache, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { - bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); + bool is_caller_trusted = + detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache); return GetMemberAction(member, /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, + [is_caller_trusted] (Thread*) { return is_caller_trusted; }, access_method); } diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc index 5a31dd4972..ab0c2901ff 100644 --- a/runtime/hidden_api_test.cc +++ b/runtime/hidden_api_test.cc @@ -17,11 +17,13 @@ #include "hidden_api.h" #include "common_runtime_test.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" +#include "proxy_test.h" namespace art { using hiddenapi::detail::MemberSignature; +using hiddenapi::GetActionFromAccessFlags; class HiddenApiTest : public CommonRuntimeTest { protected: @@ -30,7 +32,7 @@ class HiddenApiTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); self_ = Thread::Current(); self_->TransitionFromSuspendedToRunnable(); - LoadDex("HiddenApiSignatures"); + jclass_loader_ = LoadDex("HiddenApiSignatures"); bool started = runtime_->Start(); CHECK(started); @@ -68,6 +70,7 @@ class HiddenApiTest : public CommonRuntimeTest { protected: Thread* self_; + jobject jclass_loader_; ArtField* class1_field1_; ArtField* class1_field12_; ArtMethod* class1_init_; @@ -84,6 +87,44 @@ class HiddenApiTest : public CommonRuntimeTest { ArtMethod* class3_method1_i_; }; +TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) { + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), hiddenapi::kAllow); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), + hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), + hiddenapi::kAllowButWarn); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), + hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), + hiddenapi::kDeny); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), + hiddenapi::kDeny); + + runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kWhitelist), + hiddenapi::kAllow); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kLightGreylist), + hiddenapi::kAllowButWarn); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kDarkGreylist), + hiddenapi::kAllowButWarnAndToast); + ASSERT_EQ(GetActionFromAccessFlags(HiddenApiAccessFlags::kBlacklist), + hiddenapi::kDeny); +} + TEST_F(HiddenApiTest, CheckMembersRead) { ASSERT_NE(nullptr, class1_field1_); ASSERT_NE(nullptr, class1_field12_); @@ -272,4 +313,56 @@ TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) { ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix)); } +TEST_F(HiddenApiTest, CheckMemberSignatureForProxyClass) { + ScopedObjectAccess soa(self_); + StackHandleScope<4> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader_))); + + // Find interface we will create a proxy for. + Handle<mirror::Class> h_iface(hs.NewHandle( + class_linker_->FindClass(soa.Self(), "Lmypackage/packagea/Interface;", class_loader))); + ASSERT_TRUE(h_iface != nullptr); + + // Create the proxy class. + std::vector<mirror::Class*> interfaces; + interfaces.push_back(h_iface.Get()); + Handle<mirror::Class> proxyClass = hs.NewHandle(proxy_test::GenerateProxyClass( + soa, jclass_loader_, runtime_->GetClassLinker(), "$Proxy1234", interfaces)); + ASSERT_TRUE(proxyClass != nullptr); + ASSERT_TRUE(proxyClass->IsProxyClass()); + ASSERT_TRUE(proxyClass->IsInitialized()); + + // Find the "method" virtual method. + ArtMethod* method = nullptr; + for (auto& m : proxyClass->GetDeclaredVirtualMethods(kRuntimePointerSize)) { + if (strcmp("method", m.GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName()) == 0) { + method = &m; + break; + } + } + ASSERT_TRUE(method != nullptr); + + // Find the "interfaces" static field. This is generated for all proxies. + ArtField* field = nullptr; + for (size_t i = 0; i < proxyClass->NumStaticFields(); ++i) { + ArtField* f = proxyClass->GetStaticField(i); + if (strcmp("interfaces", f->GetName()) == 0) { + field = f; + break; + } + } + ASSERT_TRUE(field != nullptr); + + // Test the signature. We expect the signature from the interface class. + std::ostringstream ss_method; + MemberSignature(method).Dump(ss_method); + ASSERT_EQ("Lmypackage/packagea/Interface;->method()V", ss_method.str()); + + // Test the signature. We expect the signature of the proxy class. + std::ostringstream ss_field; + MemberSignature(field).Dump(ss_field); + ASSERT_EQ("L$Proxy1234;->interfaces:[Ljava/lang/Class;", ss_field.str()); +} + } // namespace art diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 6143ba6fd4..950a54d61e 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -19,8 +19,8 @@ #include "base/mutator_locked_dumpable.h" #include "base/systrace.h" #include "base/utils.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "nth_caller_visitor.h" #include "reference_table.h" #include "runtime.h" diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index 836bbe711f..3171eeb861 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -24,7 +24,7 @@ #include "dex/dex_file.h" #include "gc/scoped_gc_critical_section.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index 510f5f00a6..df1eb2b561 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -17,7 +17,7 @@ #include "object_registry.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" #include "obj_ptr-inl.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 2c0fbadc1d..0684b461ae 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -27,8 +27,8 @@ #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit_code_cache.h" +#include "jni/java_vm_ext.h" #include "mirror/method_handle_impl.h" #include "mirror/var_handle.h" #include "oat_file_manager.h" @@ -185,10 +185,12 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) { return nullptr; } + bool code_cache_only_for_profile_data = !options->UseJitCompilation(); jit->code_cache_.reset(JitCodeCache::Create( options->GetCodeCacheInitialCapacity(), options->GetCodeCacheMaxCapacity(), jit->generate_debug_info_, + code_cache_only_for_profile_data, error_msg)); if (jit->GetCodeCache() == nullptr) { return nullptr; diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 6d27cfe5db..4b8b8919d1 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -252,6 +252,13 @@ class JitOptions { void SetSaveProfilingInfo(bool save_profiling_info) { profile_saver_options_.SetEnabled(save_profiling_info); } + void SetWaitForJitNotificationsToSaveProfile(bool value) { + profile_saver_options_.SetWaitForJitNotificationsToSave(value); + } + void SetProfileAOTCode(bool value) { + profile_saver_options_.SetProfileAOTCode(value); + } + void SetJitAtFirstUse() { use_jit_compilation_ = true; compile_threshold_ = 0; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 1c8c26cf5d..d8aa00c45e 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -50,7 +50,6 @@ namespace art { namespace jit { -static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC; static constexpr int kProtData = PROT_READ | PROT_WRITE; static constexpr int kProtCode = PROT_READ | PROT_EXEC; @@ -161,6 +160,7 @@ class JitCodeCache::JniStubData { JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, + bool used_only_for_profile_data, std::string* error_msg) { ScopedTrace trace(__PRETTY_FUNCTION__); CHECK_GE(max_capacity, initial_capacity); @@ -184,6 +184,15 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, return nullptr; } + // Decide how we should map the code and data sections. + // If we use the code cache just for profiling we do not need to map the code section as + // executable. + // NOTE 1: this is yet another workaround to bypass strict SElinux policies in order to be able + // to profile system server. + // NOTE 2: We could just not create the code section at all but we will need to + // special case too many cases. + int memmap_flags_prot_code = used_only_for_profile_data ? (kProtCode & ~PROT_EXEC) : kProtCode; + std::string error_str; // Map name specific for android_os_Debug.cpp accounting. // Map in low 4gb to simplify accessing root tables for x86_64. @@ -216,8 +225,11 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, DCHECK_EQ(code_size + data_size, max_capacity); uint8_t* divider = data_map->Begin() + data_size; - MemMap* code_map = - data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem); + MemMap* code_map = data_map->RemapAtEnd( + divider, + "jit-code-cache", + memmap_flags_prot_code | PROT_WRITE, + &error_str, use_ashmem); if (code_map == nullptr) { std::ostringstream oss; oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity; @@ -229,7 +241,13 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, code_size = initial_capacity - data_size; DCHECK_EQ(code_size + data_size, initial_capacity); return new JitCodeCache( - code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code); + code_map, + data_map.release(), + code_size, + data_size, + max_capacity, + garbage_collect_code, + memmap_flags_prot_code); } JitCodeCache::JitCodeCache(MemMap* code_map, @@ -237,7 +255,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, size_t initial_code_capacity, size_t initial_data_capacity, size_t max_capacity, - bool garbage_collect_code) + bool garbage_collect_code, + int memmap_flags_prot_code) : lock_("Jit code cache", kJitCodeCacheLock), lock_cond_("Jit code cache condition variable", lock_), collection_in_progress_(false), @@ -258,7 +277,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, histogram_code_memory_use_("Memory used for compiled code", 16), histogram_profiling_info_memory_use_("Memory used for profiling info", 16), is_weak_access_enabled_(true), - inline_cache_cond_("Jit inline cache condition variable", lock_) { + inline_cache_cond_("Jit inline cache condition variable", lock_), + memmap_flags_prot_code_(memmap_flags_prot_code) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -274,7 +294,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, "mprotect jit code cache", code_map_->Begin(), code_map_->Size(), - kProtCode); + memmap_flags_prot_code_); CheckedCall(mprotect, "mprotect jit data cache", data_map_->Begin(), @@ -327,19 +347,30 @@ const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(MemMap* code_map) + explicit ScopedCodeCacheWrite(const JitCodeCache* const code_cache) : ScopedTrace("ScopedCodeCacheWrite"), - code_map_(code_map) { + code_cache_(code_cache) { ScopedTrace trace("mprotect all"); - CheckedCall(mprotect, "make code writable", code_map_->Begin(), code_map_->Size(), kProtAll); + CheckedCall( + mprotect, + "make code writable", + code_cache_->code_map_->Begin(), + code_cache_->code_map_->Size(), + code_cache_->memmap_flags_prot_code_ | PROT_WRITE); } + ~ScopedCodeCacheWrite() { ScopedTrace trace("mprotect code"); - CheckedCall(mprotect, "make code protected", code_map_->Begin(), code_map_->Size(), kProtCode); + CheckedCall( + mprotect, + "make code protected", + code_cache_->code_map_->Begin(), + code_cache_->code_map_->Size(), + code_cache_->memmap_flags_prot_code_); } private: - MemMap* const code_map_; + const JitCodeCache* const code_cache_; DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite); }; @@ -532,7 +563,7 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { } } -void JitCodeCache::FreeCode(const void* code_ptr) { +void JitCodeCache::FreeCodeAndData(const void* code_ptr) { uintptr_t allocation = FromCodeToAllocation(code_ptr); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. @@ -557,9 +588,9 @@ void JitCodeCache::FreeAllMethodHeaders( // so it's possible for the same method_header to start representing // different compile code. MutexLock mu(Thread::Current(), lock_); - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); for (const OatQuickMethodHeader* method_header : method_headers) { - FreeCode(method_header->GetCode()); + FreeCodeAndData(method_header->GetCode()); } } @@ -576,7 +607,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // with the classlinker_classes_lock_ held, and suspending ourselves could // lead to a deadlock. { - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { it->second.RemoveMethodsIn(alloc); if (it->second.GetMethods().empty()) { @@ -715,7 +746,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); { - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); memory = AllocateCode(total_size); if (memory == nullptr) { return nullptr; @@ -878,14 +909,14 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { } bool in_cache = false; - ScopedCodeCacheWrite ccw(code_map_.get()); + ScopedCodeCacheWrite ccw(this); if (UNLIKELY(method->IsNative())) { auto it = jni_stubs_map_.find(JniStubKey(method)); if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { in_cache = true; if (it->second.GetMethods().empty()) { if (release_memory) { - FreeCode(it->second.GetCode()); + FreeCodeAndData(it->second.GetCode()); } jni_stubs_map_.erase(it); } else { @@ -897,7 +928,7 @@ bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { if (it->second == method) { in_cache = true; if (release_memory) { - FreeCode(it->first); + FreeCodeAndData(it->first); } it = method_code_map_.erase(it); } else { @@ -1105,7 +1136,7 @@ void JitCodeCache::SetFootprintLimit(size_t new_footprint) { DCHECK_EQ(per_space_footprint * 2, new_footprint); mspace_set_footprint_limit(data_mspace_, per_space_footprint); { - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); mspace_set_footprint_limit(code_mspace_, per_space_footprint); } } @@ -1273,7 +1304,7 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { std::unordered_set<OatQuickMethodHeader*> method_headers; { MutexLock mu(self, lock_); - ScopedCodeCacheWrite scc(code_map_.get()); + ScopedCodeCacheWrite scc(this); // Iterate over all compiled code and remove entries that are not marked. for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { JniStubData* data = &it->second; diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index f1c99fb85a..958e8e8aa2 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -68,6 +68,7 @@ template<class T> class ObjectArray; namespace jit { class JitInstrumentationCache; +class ScopedCodeCacheWrite; // Alignment in bits that will suit all architectures. static constexpr int kJitCodeAlignment = 16; @@ -88,6 +89,7 @@ class JitCodeCache { static JitCodeCache* Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, + bool used_only_for_profile_data, std::string* error_msg); ~JitCodeCache(); @@ -270,7 +272,8 @@ class JitCodeCache { size_t initial_code_capacity, size_t initial_data_capacity, size_t max_capacity, - bool garbage_collect_code); + bool garbage_collect_code, + int memmap_flags_prot_code); // Internal version of 'CommitCode' that will not retry if the // allocation fails. Return null if the allocation fails. @@ -314,8 +317,8 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES(Locks::mutator_lock_); - // Free in the mspace allocations for `code_ptr`. - void FreeCode(const void* code_ptr) REQUIRES(lock_); + // Free code and data allocations for `code_ptr`. + void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_); // Number of bytes allocated in the code cache. size_t CodeCacheSizeLocked() REQUIRES(lock_); @@ -354,10 +357,10 @@ class JitCodeCache { REQUIRES(lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); - void FreeData(uint8_t* data) REQUIRES(lock_); + void FreeCode(uint8_t* code) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); bool IsWeakAccessEnabled(Thread* self) const; void WaitUntilInlineCacheAccessible(Thread* self) @@ -442,7 +445,12 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + // Mapping flags for the code section. + const int memmap_flags_prot_code_; + friend class art::JitJniStubTestHelper; + friend class ScopedCodeCacheWrite; + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 618fde8f00..6ccda8b0bb 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -131,6 +131,11 @@ void ProfileSaver::Run() { } FetchAndCacheResolvedClassesAndMethods(/*startup*/ true); + + // When we save without waiting for JIT notifications we use a simple + // exponential back off policy bounded by max_wait_without_jit. + uint32_t max_wait_without_jit = options_.GetMinSavePeriodMs() * 16; + uint64_t cur_wait_without_jit = options_.GetMinSavePeriodMs(); // Loop for the profiled methods. while (!ShuttingDown(self)) { uint64_t sleep_start = NanoTime(); @@ -138,7 +143,14 @@ void ProfileSaver::Run() { uint64_t sleep_time = 0; { MutexLock mu(self, wait_lock_); - period_condition_.Wait(self); + if (options_.GetWaitForJitNotificationsToSave()) { + period_condition_.Wait(self); + } else { + period_condition_.TimedWait(self, cur_wait_without_jit, 0); + if (cur_wait_without_jit < max_wait_without_jit) { + cur_wait_without_jit *= 2; + } + } sleep_time = NanoTime() - sleep_start; } // Check if the thread was woken up for shutdown. @@ -516,10 +528,24 @@ 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, ProfileCompilationInfo::MethodHotness::kFlagPostStartup); + // Try to add the method data. Note this may fail is the profile loaded from disk contains + // outdated data (e.g. the previous profiled dex files might have been updated). + // If this happens we clear the profile data and for the save to ensure the file is cleared. + if (!info.AddMethods(profile_methods, + ProfileCompilationInfo::MethodHotness::kFlagPostStartup)) { + LOG(WARNING) << "Could not add methods to the existing profiler. " + << "Clearing the profile data."; + info.ClearData(); + force_save = true; + } + auto profile_cache_it = profile_cache_.find(filename); if (profile_cache_it != profile_cache_.end()) { - info.MergeWith(*(profile_cache_it->second)); + if (!info.MergeWith(*(profile_cache_it->second))) { + LOG(WARNING) << "Could not merge the profile. Clearing the profile data."; + info.ClearData(); + force_save = true; + } } int64_t delta_number_of_methods = @@ -597,7 +623,13 @@ void* ProfileSaver::RunProfileSaverThread(void* arg) { return nullptr; } -static bool ShouldProfileLocation(const std::string& location) { +static bool ShouldProfileLocation(const std::string& location, bool profile_aot_code) { + if (profile_aot_code) { + // If we have to profile all the code, irrespective of its compilation state, return true + // right away. + return true; + } + OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager(); const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location); if (oat_file == nullptr) { @@ -629,7 +661,7 @@ void ProfileSaver::Start(const ProfileSaverOptions& options, std::vector<std::string> code_paths_to_profile; for (const std::string& location : code_paths) { - if (ShouldProfileLocation(location)) { + if (ShouldProfileLocation(location, options.GetProfileAOTCode())) { code_paths_to_profile.push_back(location); } } diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h index d1e14e2766..18f7899af1 100644 --- a/runtime/jit/profile_saver_options.h +++ b/runtime/jit/profile_saver_options.h @@ -41,7 +41,9 @@ struct ProfileSaverOptions { min_notification_before_wake_(kMinNotificationBeforeWake), max_notification_before_wake_(kMaxNotificationBeforeWake), profile_path_(""), - profile_boot_class_path_(false) {} + profile_boot_class_path_(false), + profile_aot_code_(false), + wait_for_jit_notifications_to_save_(true) {} ProfileSaverOptions( bool enabled, @@ -53,7 +55,9 @@ struct ProfileSaverOptions { uint32_t min_notification_before_wake, uint32_t max_notification_before_wake, const std::string& profile_path, - bool profile_boot_class_path) + bool profile_boot_class_path, + bool profile_aot_code = false, + bool wait_for_jit_notifications_to_save = true) : enabled_(enabled), min_save_period_ms_(min_save_period_ms), save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms), @@ -63,7 +67,9 @@ struct ProfileSaverOptions { min_notification_before_wake_(min_notification_before_wake), max_notification_before_wake_(max_notification_before_wake), profile_path_(profile_path), - profile_boot_class_path_(profile_boot_class_path) {} + profile_boot_class_path_(profile_boot_class_path), + profile_aot_code_(profile_aot_code), + wait_for_jit_notifications_to_save_(wait_for_jit_notifications_to_save) {} bool IsEnabled() const { return enabled_; @@ -103,6 +109,18 @@ struct ProfileSaverOptions { bool GetProfileBootClassPath() const { return profile_boot_class_path_; } + bool GetProfileAOTCode() const { + return profile_aot_code_; + } + void SetProfileAOTCode(bool value) { + profile_aot_code_ = value; + } + bool GetWaitForJitNotificationsToSave() const { + return wait_for_jit_notifications_to_save_; + } + void SetWaitForJitNotificationsToSave(bool value) { + wait_for_jit_notifications_to_save_ = value; + } friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) { os << "enabled_" << pso.enabled_ @@ -113,7 +131,9 @@ struct ProfileSaverOptions { << ", min_classes_to_save_" << pso.min_classes_to_save_ << ", min_notification_before_wake_" << pso.min_notification_before_wake_ << ", max_notification_before_wake_" << pso.max_notification_before_wake_ - << ", profile_boot_class_path_" << pso.profile_boot_class_path_; + << ", profile_boot_class_path_" << pso.profile_boot_class_path_ + << ", profile_aot_code_" << pso.profile_aot_code_ + << ", wait_for_jit_notifications_to_save_" << pso.wait_for_jit_notifications_to_save_; return os; } @@ -129,6 +149,8 @@ struct ProfileSaverOptions { uint32_t max_notification_before_wake_; std::string profile_path_; bool profile_boot_class_path_; + bool profile_aot_code_; + bool wait_for_jit_notifications_to_save_; }; } // namespace art diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 9126bea7d0..2cb569c61a 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -26,12 +26,12 @@ namespace art { ProfilingInfo::ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries) - : number_of_inline_caches_(entries.size()), - method_(method), - is_method_being_compiled_(false), - is_osr_method_being_compiled_(false), + : method_(method), + saved_entry_point_(nullptr), + number_of_inline_caches_(entries.size()), current_inline_uses_(0), - saved_entry_point_(nullptr) { + is_method_being_compiled_(false), + is_osr_method_being_compiled_(false) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 788fa1f92b..a3dae8330a 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -132,27 +132,27 @@ class ProfilingInfo { private: ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries); - // Number of instructions we are profiling in the ArtMethod. - const uint32_t number_of_inline_caches_; - // Method this profiling info is for. // Not 'const' as JVMTI introduces obsolete methods that we implement by creating new ArtMethods. // See JitCodeCache::MoveObsoleteMethod. ArtMethod* method_; - // Whether the ArtMethod is currently being compiled. This flag - // is implicitly guarded by the JIT code cache lock. - // TODO: Make the JIT code cache lock global. - bool is_method_being_compiled_; - bool is_osr_method_being_compiled_; + // Entry point of the corresponding ArtMethod, while the JIT code cache + // is poking for the liveness of compiled code. + const void* saved_entry_point_; + + // Number of instructions we are profiling in the ArtMethod. + const uint32_t number_of_inline_caches_; // When the compiler inlines the method associated to this ProfilingInfo, // it updates this counter so that the GC does not try to clear the inline caches. uint16_t current_inline_uses_; - // Entry point of the corresponding ArtMethod, while the JIT code cache - // is poking for the liveness of compiled code. - const void* saved_entry_point_; + // Whether the ArtMethod is currently being compiled. This flag + // is implicitly guarded by the JIT code cache lock. + // TODO: Make the JIT code cache lock global. + bool is_method_being_compiled_; + bool is_osr_method_being_compiled_; // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; diff --git a/runtime/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index 8fe68bd318..8fe68bd318 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc diff --git a/runtime/java_vm_ext.h b/runtime/jni/java_vm_ext.h index ac20afecd4..408d3542ed 100644 --- a/runtime/java_vm_ext.h +++ b/runtime/jni/java_vm_ext.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JAVA_VM_EXT_H_ -#define ART_RUNTIME_JAVA_VM_EXT_H_ +#ifndef ART_RUNTIME_JNI_JAVA_VM_EXT_H_ +#define ART_RUNTIME_JNI_JAVA_VM_EXT_H_ #include "jni.h" @@ -262,4 +262,4 @@ class JavaVMExt : public JavaVM { } // namespace art -#endif // ART_RUNTIME_JAVA_VM_EXT_H_ +#endif // ART_RUNTIME_JNI_JAVA_VM_EXT_H_ diff --git a/runtime/java_vm_ext_test.cc b/runtime/jni/java_vm_ext_test.cc index a15ec56274..74e4a30905 100644 --- a/runtime/java_vm_ext_test.cc +++ b/runtime/jni/java_vm_ext_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "jni_internal.h" +#include "jni/jni_internal.h" #include <pthread.h> diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni/jni_env_ext-inl.h index 14f708b18d..7609a9e01a 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni/jni_env_ext-inl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_ -#define ART_RUNTIME_JNI_ENV_EXT_INL_H_ +#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ +#define ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ #include "jni_env_ext.h" @@ -51,4 +51,4 @@ inline T JNIEnvExt::AddLocalReference(ObjPtr<mirror::Object> obj) { } // namespace art -#endif // ART_RUNTIME_JNI_ENV_EXT_INL_H_ +#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_ diff --git a/runtime/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc index efe43ee0e9..efe43ee0e9 100644 --- a/runtime/jni_env_ext.cc +++ b/runtime/jni/jni_env_ext.cc diff --git a/runtime/jni_env_ext.h b/runtime/jni/jni_env_ext.h index 291ac48e86..3a007adcf0 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni/jni_env_ext.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_ENV_EXT_H_ -#define ART_RUNTIME_JNI_ENV_EXT_H_ +#ifndef ART_RUNTIME_JNI_JNI_ENV_EXT_H_ +#define ART_RUNTIME_JNI_JNI_ENV_EXT_H_ #include <jni.h> @@ -229,4 +229,4 @@ class ScopedJniEnvLocalRefState { } // namespace art -#endif // ART_RUNTIME_JNI_ENV_EXT_H_ +#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_H_ diff --git a/runtime/jni_internal.cc b/runtime/jni/jni_internal.cc index 9dbcded867..cd66a60376 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni/jni_internal.cc @@ -80,15 +80,15 @@ namespace art { // things not rendering correctly. E.g. b/16858794 static constexpr bool kWarnJniAbort = false; -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1)); +static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1)); } template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + member, self, IsCallerTrusted, hiddenapi::kJNI); if (action != hiddenapi::kAllow) { hiddenapi::NotifyHiddenApiListener(member); } diff --git a/runtime/jni_internal.h b/runtime/jni/jni_internal.h index 2c90b3ba78..d0426617eb 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni/jni_internal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JNI_INTERNAL_H_ -#define ART_RUNTIME_JNI_INTERNAL_H_ +#ifndef ART_RUNTIME_JNI_JNI_INTERNAL_H_ +#define ART_RUNTIME_JNI_JNI_INTERNAL_H_ #include <jni.h> #include <iosfwd> @@ -59,4 +59,4 @@ static inline ArtMethod* DecodeArtMethod(jmethodID method_id) { std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs); -#endif // ART_RUNTIME_JNI_INTERNAL_H_ +#endif // ART_RUNTIME_JNI_JNI_INTERNAL_H_ diff --git a/runtime/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc index 5d74181cef..5d74181cef 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni/jni_internal_test.cc diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc deleted file mode 100644 index 4c45e3839b..0000000000 --- a/runtime/jobject_comparator.cc +++ /dev/null @@ -1,56 +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. - */ - -#include "jobject_comparator.h" - -#include "mirror/array-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "scoped_thread_state_change-inl.h" - -namespace art { - -bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const { - // Ensure null references and cleared jweaks appear at the end. - if (jobj1 == nullptr) { - return true; - } else if (jobj2 == nullptr) { - return false; - } - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object>(jobj1))); - Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object>(jobj2))); - if (obj1 == nullptr) { - return true; - } else if (obj2 == nullptr) { - return false; - } - // Sort by class... - if (obj1->GetClass() != obj2->GetClass()) { - return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode(); - } - // ...then by size... - const size_t count1 = obj1->SizeOf(); - const size_t count2 = obj2->SizeOf(); - if (count1 != count2) { - return count1 < count2; - } - // ...and finally by identity hash code. - return obj1->IdentityHashCode() < obj2->IdentityHashCode(); -} - -} // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 51d1376a3c..98e25eb320 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -210,6 +210,15 @@ class MANAGED Class FINAL : public Object { return (GetAccessFlags() & kAccClassIsFinalizable) != 0; } + ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0; + } + + ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t flags = GetAccessFlags(); + SetAccessFlags(flags | kAccSkipHiddenApiChecks); + } + ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()); uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_)); diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc index 039bbf2932..aeecf75c1f 100644 --- a/runtime/mirror/method_handles_lookup.cc +++ b/runtime/mirror/method_handles_lookup.cc @@ -20,7 +20,7 @@ #include "dex/modifiers.h" #include "gc_root-inl.h" #include "handle_scope.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/method_handle_impl.h" #include "object-inl.h" #include "well_known_classes.h" diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index a79c0a26d0..b309f596fd 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -22,7 +22,7 @@ #include "class_linker.h" #include "gc_root-inl.h" #include "intrinsics_enum.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "jvalue-inl.h" #include "method_handles.h" #include "method_type.h" diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 8320d9c7ba..cdba6b204f 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -35,7 +35,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "jit/debugger_interface.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "mirror/string.h" @@ -816,6 +816,28 @@ static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) return static_cast<jlong>(file_size); } +static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(env); + + // Currently only allow this for debuggable apps. + if (!runtime->IsJavaDebuggable()) { + ThrowSecurityException("Can't exempt class, process is not debuggable."); + return; + } + + std::vector<const DexFile*> dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) { + Thread::Current()->AssertPendingException(); + return; + } + + for (const DexFile* dex_file : dex_files) { + const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile(); + } +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"), NATIVE_METHOD(DexFile, @@ -854,7 +876,8 @@ static JNINativeMethod gMethods[] = { "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"), NATIVE_METHOD(DexFile, getDexFileOptimizationStatus, - "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;") + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V") }; void register_dalvik_system_DexFile(JNIEnv* env) { diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 3692a308d8..f1e267becc 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -35,8 +35,8 @@ #include "gc/space/zygote_space.h" #include "handle_scope-inl.h" #include "hprof/hprof.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/object_array-inl.h" #include "native_util.h" @@ -588,6 +588,25 @@ static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobjec Runtime::Current()->AttachAgent(env, filename, classloader); } +static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(env); + + if (!runtime->IsJavaDebuggable()) { + ThrowSecurityException("Can't exempt class, process is not debuggable."); + return; + } + + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller))); + if (h_caller.IsNull()) { + ThrowNullPointerException("argument is null"); + return; + } + + h_caller->SetSkipHiddenApiChecks(); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), @@ -623,6 +642,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"), + NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"), }; void register_dalvik_system_VMDebug(JNIEnv* env) { diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index a5ade6f30f..7f7b524227 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -41,8 +41,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "gc/space/image_space.h" #include "gc/task_processor.h" #include "intern_table.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object-inl.h" @@ -93,6 +93,10 @@ static void VMRuntime_setHiddenApiExemptions(JNIEnv* env, Runtime::Current()->SetHiddenApiExemptions(exemptions_vec); } +static void VMRuntime_setHiddenApiAccessLogSamplingRate(JNIEnv*, jclass, jint rate) { + Runtime::Current()->SetHiddenApiEventLogSampleRate(rate); +} + static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); @@ -678,6 +682,12 @@ static void VMRuntime_setSystemDaemonThreadPriority(JNIEnv* env ATTRIBUTE_UNUSED #endif } +static void VMRuntime_setDedupeHiddenApiWarnings(JNIEnv* env ATTRIBUTE_UNUSED, + jclass klass ATTRIBUTE_UNUSED, + jboolean dedupe) { + Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe); +} + static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), @@ -688,6 +698,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"), NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"), NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"), + NATIVE_METHOD(VMRuntime, setHiddenApiAccessLogSamplingRate, "(I)V"), NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"), FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"), FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"), @@ -718,6 +729,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"), NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"), + NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"), }; void register_dalvik_system_VMRuntime(JNIEnv* env) { diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index ed0eb97da1..39192274ad 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "gc/task_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index cf0a72a477..38c65f5deb 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -28,9 +28,9 @@ #include "base/runtime_debug.h" #include "debugger.h" #include "hidden_api.h" -#include "java_vm_ext.h" #include "jit/jit.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_utf_chars.h" @@ -177,6 +177,7 @@ enum { DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11, HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13), + PROFILE_SYSTEM_SERVER = 1 << 14, // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value // corresponding to hiddenapi::EnforcementPolicy @@ -308,6 +309,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, (runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT); runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK; + bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER; + runtime_flags &= ~PROFILE_SYSTEM_SERVER; + if (runtime_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags); } @@ -363,6 +367,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, << "Child zygote processes should be forked with EnforcementPolicy::kDisable"; Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy); Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings); + if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks && + Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) { + // Hidden API checks are enabled, and we are sampling access for the event log. Initialize the + // random seed, to ensure the sampling is actually random. We do this post-fork, as doing it + // pre-fork would result in the same sequence for every forked process. + std::srand(static_cast<uint32_t>(NanoTime())); + } // Clear the hidden API warning flag, in case it was set. Runtime::Current()->SetPendingHiddenApiWarning(false); @@ -385,7 +396,11 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env, env, is_system_server, action, isa_string.c_str()); } else { Runtime::Current()->InitNonZygoteOrPostFork( - env, is_system_server, Runtime::NativeBridgeAction::kUnload, nullptr); + env, + is_system_server, + Runtime::NativeBridgeAction::kUnload, + /*isa*/ nullptr, + profile_system_server); } } diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index bfd7f69cef..68024cd1c2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -28,7 +28,7 @@ #include "dex/dex_file_annotations.h" #include "dex/utf.h" #include "hidden_api.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/field-inl.h" @@ -52,7 +52,7 @@ namespace art { // Returns true if the first caller outside of the Class class or java.lang.invoke package // is in a platform DEX file. -static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { +static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke. // This is very expensive. Save this till the last. struct FirstExternalCallerVisitor : public StackVisitor { @@ -99,7 +99,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l FirstExternalCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && - hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); + hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass()); } // Returns true if the first non-ClassClass caller up the stack is not allowed to @@ -107,7 +107,7 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); - return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self); + return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self); } // Returns true if the first non-ClassClass caller up the stack should not be @@ -116,7 +116,7 @@ template<typename T> ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { hiddenapi::Action action = hiddenapi::GetMemberAction( - member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + member, self, IsCallerTrusted, hiddenapi::kReflection); if (action != hiddenapi::kAllow) { hiddenapi::NotifyHiddenApiListener(member); } @@ -128,19 +128,20 @@ ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) // the criteria. Some reflection calls only return public members // (public_only == true), some members should be hidden from non-boot class path // callers (enforce_hidden_api == true). +template<typename T> ALWAYS_INLINE static bool IsDiscoverable(bool public_only, bool enforce_hidden_api, - uint32_t access_flags) { - if (public_only && ((access_flags & kAccPublic) == 0)) { - return false; - } - - if (enforce_hidden_api && - hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) { + T* member) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) { return false; } - return true; + return hiddenapi::GetMemberAction(member, + nullptr, + [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; }, + hiddenapi::kNone) + != hiddenapi::kDeny; } ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass( @@ -269,12 +270,12 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields( bool enforce_hidden_api = ShouldEnforceHiddenApi(self); // Lets go subtract all the non discoverable fields. for (ArtField& field : ifields) { - if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) { --array_size; } } for (ArtField& field : sfields) { - if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) { --array_size; } } @@ -285,7 +286,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields( return nullptr; } for (ArtField& field : ifields) { - if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (IsDiscoverable(public_only, enforce_hidden_api, &field)) { auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve); @@ -300,7 +301,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields( } } for (ArtField& field : sfields) { - if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) { + if (IsDiscoverable(public_only, enforce_hidden_api, &field)) { auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self, &field, force_resolve); @@ -521,7 +522,7 @@ static ALWAYS_INLINE inline bool MethodMatchesConstructor( DCHECK(m != nullptr); return m->IsConstructor() && !m->IsStatic() && - IsDiscoverable(public_only, enforce_hidden_api, m->GetAccessFlags()); + IsDiscoverable(public_only, enforce_hidden_api, m); } static jobjectArray Class_getDeclaredConstructorsInternal( @@ -591,7 +592,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT uint32_t modifiers = m.GetAccessFlags(); // Add non-constructor declared methods. if ((modifiers & kAccConstructor) == 0 && - IsDiscoverable(public_only, enforce_hidden_api, modifiers)) { + IsDiscoverable(public_only, enforce_hidden_api, &m)) { ++num_methods; } } @@ -605,7 +606,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) { uint32_t modifiers = m.GetAccessFlags(); if ((modifiers & kAccConstructor) == 0 && - IsDiscoverable(public_only, enforce_hidden_api, modifiers)) { + IsDiscoverable(public_only, enforce_hidden_api, &m)) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); auto* method = diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc index d52bf0490b..208ccf6a82 100644 --- a/runtime/native/java_lang_Object.cc +++ b/runtime/native/java_lang_Object.cc @@ -18,7 +18,7 @@ #include "nativehelper/jni_macros.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc index b5aea7ca7c..8976058b53 100644 --- a/runtime/native/java_lang_String.cc +++ b/runtime/native/java_lang_String.cc @@ -19,7 +19,7 @@ #include "nativehelper/jni_macros.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/object-inl.h" #include "mirror/string-inl.h" diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 136a02f8f6..07e875efcb 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -17,7 +17,7 @@ #include "java_lang_StringFactory.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/string.h" #include "native_util.h" diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc index 390f026588..2c4184c285 100644 --- a/runtime/native/java_lang_System.cc +++ b/runtime/native/java_lang_System.cc @@ -20,7 +20,7 @@ #include "common_throws.h" #include "gc/accounting/card_table-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class.h" diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index 9a52f7002b..9edb0c21dd 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -17,7 +17,7 @@ #include "java_lang_Thread.h" #include "common_throws.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object.h" #include "monitor.h" #include "native_util.h" diff --git a/runtime/native/java_lang_Throwable.cc b/runtime/native/java_lang_Throwable.cc index 03b7f9dfba..b5ef7d807b 100644 --- a/runtime/native/java_lang_Throwable.cc +++ b/runtime/native/java_lang_Throwable.cc @@ -18,7 +18,7 @@ #include "nativehelper/jni_macros.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" #include "thread.h" diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc index 44585fc453..0630737d29 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -20,7 +20,7 @@ #include "class_linker.h" #include "dex/descriptors_names.h" #include "dex/dex_file_loader.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc index 2e3b4d41ef..1f2bf09f0e 100644 --- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -20,7 +20,7 @@ #include "art_method.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/field.h" #include "mirror/method.h" #include "mirror/method_handle_impl.h" diff --git a/runtime/native/java_lang_ref_FinalizerReference.cc b/runtime/native/java_lang_ref_FinalizerReference.cc index 72af5f7ea7..c89188c99c 100644 --- a/runtime/native/java_lang_ref_FinalizerReference.cc +++ b/runtime/native/java_lang_ref_FinalizerReference.cc @@ -20,7 +20,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc index 524a18ca20..fc018d15c4 100644 --- a/runtime/native/java_lang_ref_Reference.cc +++ b/runtime/native/java_lang_ref_Reference.cc @@ -20,7 +20,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/object-inl.h" #include "mirror/reference-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index d28f74158e..8bcda10f2a 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -22,7 +22,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index 86124388bc..a5d6c9704d 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -23,7 +23,7 @@ #include "class_linker-inl.h" #include "class_linker.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index b129c66759..9a2d3020c0 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "dex/dex_file_annotations.h" #include "handle.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/method.h" #include "mirror/object-inl.h" diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 13275d92e4..e0afbee845 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -26,7 +26,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "native_util.h" diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc index 4355c06acd..2503b3cb44 100644 --- a/runtime/native/java_lang_reflect_Method.cc +++ b/runtime/native/java_lang_reflect_Method.cc @@ -23,7 +23,7 @@ #include "class_linker-inl.h" #include "class_linker.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index b80b20cd8d..263a56796f 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -24,7 +24,7 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index 691ed28b0b..f723ed223d 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -19,7 +19,7 @@ #include "nativehelper/jni_macros.h" #include "class_linker.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object_array.h" #include "mirror/string.h" diff --git a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc index c0032975ce..fa288edcb8 100644 --- a/runtime/native/java_util_concurrent_atomic_AtomicLong.cc +++ b/runtime/native/java_util_concurrent_atomic_AtomicLong.cc @@ -21,7 +21,7 @@ #include "arch/instruction_set.h" #include "base/atomic.h" #include "base/quasi_atomic.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" namespace art { diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc index f3aba2575b..24298049ee 100644 --- a/runtime/native/libcore_util_CharsetUtils.cc +++ b/runtime/native/libcore_util_CharsetUtils.cc @@ -18,7 +18,7 @@ #include <string.h> -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/string-inl.h" #include "mirror/string.h" #include "native_util.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index 8f8fd71727..419aed8578 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -20,7 +20,7 @@ #include "base/array_ref.h" #include "debugger.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_primitive_array.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index fbee7b31a3..028675d448 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -22,7 +22,7 @@ #include "base/mutex.h" #include "debugger.h" #include "gc/heap.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index fb00ae3967..d41a19556e 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -27,7 +27,7 @@ #include "base/quasi_atomic.h" #include "common_throws.h" #include "gc/accounting/card_table-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index 7d72805dc6..def48e8be9 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -25,7 +25,7 @@ #include "base/logging.h" // For VLOG. #include "base/macros.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" #include "sigchain.h" diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc index 8484e2cde7..f42a2d6755 100644 --- a/runtime/non_debuggable_classes.cc +++ b/runtime/non_debuggable_classes.cc @@ -16,7 +16,7 @@ #include "non_debuggable_classes.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "nativehelper/scoped_local_ref.h" #include "obj_ptr-inl.h" diff --git a/runtime/oat.h b/runtime/oat.h index 0318606f87..6c683f1541 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Use rMR as temp in Baker RB introspection marking. - static constexpr uint8_t kOatVersion[] = { '1', '4', '1', '\0' }; + // Last oat version changed reason: compiler support const-method-handle + static constexpr uint8_t kOatVersion[] = { '1', '4', '3', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 9c8b6512a7..241102ea83 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -1217,7 +1217,9 @@ bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* return false; } - bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()); + + bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) == + ClassLoaderContext::VerificationResult::kVerifies; if (!result) { VLOG(oat) << "ClassLoaderContext check failed. Context was " << file->GetClassLoaderContext() diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index f6fb9ded87..59a1045ba2 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -38,7 +38,7 @@ #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "oat_file.h" @@ -276,9 +276,19 @@ static void AddNext(/*inout*/DexFileAndClassPair& original, } } -static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, - std::vector<const DexFile*>& dex_files_unloaded, - std::string* error_msg /*out*/) { +static bool CheckClassCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + std::string* error_msg /*out*/) { + std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); + + // Vector that holds the newly opened dex files live, this is done to prevent leaks. + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + + ScopedTrace st("Collision check"); + // Add dex files from the oat file to check. + std::vector<const DexFile*> dex_files_unloaded; + AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); + // Generate type index information for each dex file. std::vector<TypeIndexInfo> loaded_types; for (const DexFile* dex_file : dex_files_loaded) { @@ -355,9 +365,10 @@ static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, // against the following top element. If the descriptor is the same, it is now checked whether // the two elements agree on whether their dex file was from an already-loaded oat-file or the // new oat file. Any disagreement indicates a collision. -bool OatFileManager::HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - std::string* error_msg /*out*/) const { +OatFileManager::CheckCollisionResult OatFileManager::CheckCollision( + const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const { DCHECK(oat_file != nullptr); DCHECK(error_msg != nullptr); @@ -367,28 +378,59 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, // Note that this has correctness implications as we cannot guarantee that the class resolution // used during compilation is OK (b/37777332). if (context == nullptr) { - LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; - return false; + LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; + return CheckCollisionResult::kSkippedUnsupportedClassLoader; } - // If the pat file loading context matches the context used during compilation then we accept + // If the oat file loading context matches the context used during compilation then we accept // the oat file without addition checks - if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { - return false; + ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch( + oat_file->GetClassLoaderContext(), + /*verify_names*/ true, + /*verify_checksums*/ true); + switch (result) { + case ClassLoaderContext::VerificationResult::kForcedToSkipChecks: + return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary; + case ClassLoaderContext::VerificationResult::kMismatch: + // Mismatched context, do the actual collision check. + break; + case ClassLoaderContext::VerificationResult::kVerifies: + return CheckCollisionResult::kNoCollisions; } // The class loader context does not match. Perform a full duplicate classes check. + return CheckClassCollision(oat_file, context, error_msg) + ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions; +} - std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); - - // Vector that holds the newly opened dex files live, this is done to prevent leaks. - std::vector<std::unique_ptr<const DexFile>> opened_dex_files; +bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const { + // Take the file only if it has no collisions, or we must take it because of preopting. + // Also accept oat files for shared libraries and unsupported class loaders. + return result != CheckCollisionResult::kPerformedHasCollisions; +} - ScopedTrace st("Collision check"); - // Add dex files from the oat file to check. - std::vector<const DexFile*> dex_files_unloaded; - AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); - return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); +bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + // If we verified the class loader context (skipping due to the special marker doesn't + // count), then also avoid the collision check. + bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions; + // If we skipped the collision check, we need to reverify to be sure its OK to load the + // image. + if (!load_image && + check_collision_result == + CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) { + // We can load the app image only if there are no collisions. If we know the + // class loader but didn't do the full collision check in HasCollisions(), + // do it now. b/77342775 + load_image = !CheckClassCollision(source_oat_file, context, error_msg); + } + return load_image; + } + return false; } std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( @@ -473,16 +515,17 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( << reinterpret_cast<uintptr_t>(oat_file.get()) << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; + CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions; if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { // Prevent oat files from being loaded if no class_loader or dex_elements are provided. // This can happen when the deprecated DexFile.<init>(String) is called directly, and it // could load oat files without checking the classpath, which would be incorrect. // Take the file only if it has no collisions, or we must take it because of preopting. - bool accept_oat_file = - !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); + check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg); + bool accept_oat_file = AcceptOatFile(check_collision_result); if (!accept_oat_file) { // Failed the collision check. Print warning. - if (Runtime::Current()->IsDexFileFallbackEnabled()) { + if (runtime->IsDexFileFallbackEnabled()) { if (!oat_file_assistant.HasOriginalDexFiles()) { // We need to fallback but don't have original dex files. We have to // fallback to opening the existing oat file. This is potentially @@ -529,10 +572,11 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr<gc::space::ImageSpace> image_space; - if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + if (ShouldLoadAppImage(check_collision_result, + source_oat_file, + context.get(), + &error_msg)) { image_space = oat_file_assistant.OpenImageSpace(source_oat_file); - } else { - image_space = nullptr; } if (image_space != nullptr) { ScopedObjectAccess soa(self); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 038474e31f..80456e9b75 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -108,23 +108,39 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(); private: + enum class CheckCollisionResult { + kSkippedUnsupportedClassLoader, + kSkippedClassLoaderContextSharedLibrary, + kNoCollisions, + kPerformedHasCollisions, + }; + // Check that the class loader context of the given oat file matches the given context. // This will perform a check that all class loaders in the chain have the same type and // classpath. // If the context is null (which means the initial class loader was null or unsupported) - // this returns false. + // this returns kSkippedUnsupportedClassLoader. // If the context does not validate the method will check for duplicate class definitions of // the given oat file against the oat files (either from the class loaders if possible or all // non-boot oat files otherwise). - // Return true if there are any class definition collisions in the oat_file. - bool HasCollisions(const OatFile* oat_file, - const ClassLoaderContext* context, - /*out*/ std::string* error_msg) const + // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file. + CheckCollisionResult CheckCollision(const OatFile* oat_file, + const ClassLoaderContext* context, + /*out*/ std::string* error_msg) const REQUIRES(!Locks::oat_file_manager_lock_); const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const REQUIRES(Locks::oat_file_manager_lock_); + // Return true if we should accept the oat file. + bool AcceptOatFile(CheckCollisionResult result) const; + + // Return true if we should attempt to load the app image. + bool ShouldLoadAppImage(CheckCollisionResult check_collision_result, + const OatFile* source_oat_file, + ClassLoaderContext* context, + std::string* error_msg); + std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_); bool have_non_pic_oat_file_; diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5518eb2c49..3aa481af8c 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -252,12 +252,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xstackdumplockprofthreshold:_") .WithType<unsigned int>() .IntoKey(M::StackDumpLockProfThreshold) - .Define("-Xusetombstonedtraces") - .WithValue(true) - .IntoKey(M::UseTombstonedTraces) - .Define("-Xstacktracefile:_") - .WithType<std::string>() - .IntoKey(M::StackTraceFile) .Define("-Xmethod-trace") .IntoKey(M::MethodTrace) .Define("-Xmethod-trace-file:_") @@ -699,7 +693,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, "The following Dalvik options are supported:\n"); UsageMessage(stream, " -Xzygote\n"); UsageMessage(stream, " -Xjnitrace:substring (eg NativeClass or nativeMethod)\n"); - UsageMessage(stream, " -Xstacktracefile:<filename>\n"); UsageMessage(stream, " -Xgc:[no]preverify\n"); UsageMessage(stream, " -Xgc:[no]postverify\n"); UsageMessage(stream, " -XX:HeapGrowthLimit=N\n"); diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 3ad3a4b6a9..4e0bf890db 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -18,97 +18,16 @@ #include <vector> #include "art_field-inl.h" -#include "art_method-inl.h" #include "base/enums.h" -#include "class_linker-inl.h" #include "common_compiler_test.h" -#include "mirror/class-inl.h" #include "mirror/field-inl.h" -#include "mirror/method.h" +#include "proxy_test.h" #include "scoped_thread_state_change-inl.h" namespace art { +namespace proxy_test { -class ProxyTest : public CommonCompilerTest { - public: - // Generate a proxy class with the given name and interfaces. This is a simplification from what - // libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and - // we do not declare exceptions. - mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, jobject jclass_loader, - const char* className, - const std::vector<mirror::Class*>& interfaces) - REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* javaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); - CHECK(javaLangObject != nullptr); - - jclass javaLangClass = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass()); - - // Builds the interfaces array. - jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, - nullptr); - soa.Self()->AssertNoPendingException(); - for (size_t i = 0; i < interfaces.size(); ++i) { - soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i, - soa.AddLocalReference<jclass>(interfaces[i])); - } - - // Builds the method array. - jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString. - for (mirror::Class* interface : interfaces) { - methods_count += interface->NumVirtualMethods(); - } - jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( - methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr); - soa.Self()->AssertNoPendingException(); - - jsize array_index = 0; - // Fill the method array - DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); - ArtMethod* method = javaLangObject->FindClassMethod( - "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); - CHECK(method != nullptr); - CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); - DCHECK(!Runtime::Current()->IsActiveTransaction()); - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( - mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); - method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); - CHECK(method != nullptr); - CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( - mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); - method = javaLangObject->FindClassMethod( - "toString", "()Ljava/lang/String;", kRuntimePointerSize); - CHECK(method != nullptr); - CHECK(!method->IsDirect()); - CHECK(method->GetDeclaringClass() == javaLangObject); - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( - mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); - // Now adds all interfaces virtual methods. - for (mirror::Class* interface : interfaces) { - for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) { - soa.Env()->SetObjectArrayElement( - proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( - mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m))); - } - } - CHECK_EQ(array_index, methods_count); - - // Builds an empty exception array. - jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); - soa.Self()->AssertNoPendingException(); - - mirror::Class* proxyClass = class_linker_->CreateProxyClass( - soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, - proxyClassMethods, proxyClassThrows); - soa.Self()->AssertNoPendingException(); - return proxyClass; - } -}; +class ProxyTest : public CommonRuntimeTest {}; // Creates a proxy class and check ClassHelper works correctly. TEST_F(ProxyTest, ProxyClassHelper) { @@ -129,7 +48,7 @@ TEST_F(ProxyTest, ProxyClassHelper) { interfaces.push_back(I.Get()); interfaces.push_back(J.Get()); Handle<mirror::Class> proxy_class(hs.NewHandle( - GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces))); + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces))); interfaces.clear(); // Don't least possibly stale objects in the array as good practice. ASSERT_TRUE(proxy_class != nullptr); ASSERT_TRUE(proxy_class->IsProxyClass()); @@ -164,7 +83,8 @@ TEST_F(ProxyTest, ProxyFieldHelper) { std::vector<mirror::Class*> interfaces; interfaces.push_back(I.Get()); interfaces.push_back(J.Get()); - proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)); + proxyClass = hs.NewHandle( + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1234", interfaces)); } ASSERT_TRUE(proxyClass != nullptr); @@ -212,8 +132,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { Handle<mirror::Class> proxyClass1; { std::vector<mirror::Class*> interfaces; - proxyClass0 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy0", interfaces)); - proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces)); + proxyClass0 = hs.NewHandle( + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy0", interfaces)); + proxyClass1 = hs.NewHandle( + GenerateProxyClass(soa, jclass_loader, class_linker_, "$Proxy1", interfaces)); } ASSERT_TRUE(proxyClass0 != nullptr); @@ -255,4 +177,5 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { EXPECT_EQ(field11->GetArtField(), &static_fields1->At(1)); } +} // namespace proxy_test } // namespace art diff --git a/runtime/proxy_test.h b/runtime/proxy_test.h new file mode 100644 index 0000000000..b559823257 --- /dev/null +++ b/runtime/proxy_test.h @@ -0,0 +1,114 @@ +/* + * 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_PROXY_TEST_H_ +#define ART_RUNTIME_PROXY_TEST_H_ + +#include <jni.h> +#include <vector> + +#include "art_method-inl.h" +#include "class_linker-inl.h" +#include "mirror/class-inl.h" +#include "mirror/method.h" + +namespace art { +namespace proxy_test { + +// Generate a proxy class with the given name and interfaces. This is a simplification from what +// libcore does to fit to our test needs. We do not check for duplicated interfaces or methods and +// we do not declare exceptions. +mirror::Class* GenerateProxyClass(ScopedObjectAccess& soa, + jobject jclass_loader, + ClassLinker* class_linker, + const char* className, + const std::vector<mirror::Class*>& interfaces) + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* javaLangObject = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); + CHECK(javaLangObject != nullptr); + + jclass javaLangClass = soa.AddLocalReference<jclass>(mirror::Class::GetJavaLangClass()); + + // Builds the interfaces array. + jobjectArray proxyClassInterfaces = soa.Env()->NewObjectArray(interfaces.size(), javaLangClass, + nullptr); + soa.Self()->AssertNoPendingException(); + for (size_t i = 0; i < interfaces.size(); ++i) { + soa.Env()->SetObjectArrayElement(proxyClassInterfaces, i, + soa.AddLocalReference<jclass>(interfaces[i])); + } + + // Builds the method array. + jsize methods_count = 3; // Object.equals, Object.hashCode and Object.toString. + for (mirror::Class* interface : interfaces) { + methods_count += interface->NumVirtualMethods(); + } + jobjectArray proxyClassMethods = soa.Env()->NewObjectArray( + methods_count, soa.AddLocalReference<jclass>(mirror::Method::StaticClass()), nullptr); + soa.Self()->AssertNoPendingException(); + + jsize array_index = 0; + // Fill the method array + DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); + ArtMethod* method = javaLangObject->FindClassMethod( + "equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize); + CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); + DCHECK(!Runtime::Current()->IsActiveTransaction()); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); + method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize); + CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); + method = javaLangObject->FindClassMethod( + "toString", "()Ljava/lang/String;", kRuntimePointerSize); + CHECK(method != nullptr); + CHECK(!method->IsDirect()); + CHECK(method->GetDeclaringClass() == javaLangObject); + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method))); + // Now adds all interfaces virtual methods. + for (mirror::Class* interface : interfaces) { + for (auto& m : interface->GetDeclaredVirtualMethods(kRuntimePointerSize)) { + soa.Env()->SetObjectArrayElement( + proxyClassMethods, array_index++, soa.AddLocalReference<jobject>( + mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), &m))); + } + } + CHECK_EQ(array_index, methods_count); + + // Builds an empty exception array. + jobjectArray proxyClassThrows = soa.Env()->NewObjectArray(0, javaLangClass, nullptr); + soa.Self()->AssertNoPendingException(); + + mirror::Class* proxyClass = class_linker->CreateProxyClass( + soa, soa.Env()->NewStringUTF(className), proxyClassInterfaces, jclass_loader, + proxyClassMethods, proxyClassThrows); + soa.Self()->AssertNoPendingException(); + return proxyClass; +} + +} // namespace proxy_test +} // namespace art + +#endif // ART_RUNTIME_PROXY_TEST_H_ diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 068bc285e5..dfa4b3daab 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -23,8 +23,8 @@ #include "common_throws.h" #include "dex/dex_file-inl.h" #include "indirect_reference_table-inl.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc index 7b36c7368e..d2d720f722 100644 --- a/runtime/reflection_test.cc +++ b/runtime/reflection_test.cc @@ -23,8 +23,8 @@ #include "base/enums.h" #include "common_compiler_test.h" #include "dex/descriptors_names.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "nativehelper/scoped_local_ref.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d12a976be8..b8775b874f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -93,11 +93,11 @@ #include "instrumentation.h" #include "intern_table.h" #include "interpreter/interpreter.h" -#include "java_vm_ext.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profile_saver.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "linear_alloc.h" #include "memory_representation.h" #include "mirror/array.h" @@ -232,7 +232,6 @@ Runtime::Runtime() intern_table_(nullptr), class_linker_(nullptr), signal_catcher_(nullptr), - use_tombstoned_traces_(false), java_vm_(nullptr), fault_message_lock_("Fault message lock"), fault_message_(""), @@ -274,6 +273,7 @@ Runtime::Runtime() pending_hidden_api_warning_(false), dedupe_hidden_api_warnings_(true), always_set_hidden_api_warning_flag_(false), + hidden_api_access_event_log_rate_(0), dump_native_stack_on_sig_quit_(true), pruned_dalvik_cache_(false), // Initially assume we perceive jank in case the process state is never updated. @@ -861,7 +861,11 @@ void Runtime::EndThreadBirth() REQUIRES(Locks::runtime_shutdown_lock_) { } void Runtime::InitNonZygoteOrPostFork( - JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) { + JNIEnv* env, + bool is_system_server, + NativeBridgeAction action, + const char* isa, + bool profile_system_server) { is_zygote_ = false; if (is_native_bridge_loaded_) { @@ -884,8 +888,15 @@ void Runtime::InitNonZygoteOrPostFork( heap_->ResetGcPerformanceInfo(); // We may want to collect profiling samples for system server, but we never want to JIT there. - if ((!is_system_server || !jit_options_->UseJitCompilation()) && - !safe_mode_ && + if (is_system_server) { + jit_options_->SetUseJitCompilation(false); + jit_options_->SetSaveProfilingInfo(profile_system_server); + if (profile_system_server) { + jit_options_->SetWaitForJitNotificationsToSaveProfile(false); + VLOG(profiler) << "Enabling system server profiles"; + } + } + if (!safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) && jit_ == nullptr) { // Note that when running ART standalone (not zygote, nor zygote fork), @@ -904,7 +915,7 @@ void Runtime::InitNonZygoteOrPostFork( void Runtime::StartSignalCatcher() { if (!is_zygote_) { - signal_catcher_ = new SignalCatcher(stack_trace_file_, use_tombstoned_traces_); + signal_catcher_ = new SignalCatcher(); } } @@ -1152,12 +1163,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { abort_ = runtime_options.GetOrDefault(Opt::HookAbort); default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize); - use_tombstoned_traces_ = runtime_options.GetOrDefault(Opt::UseTombstonedTraces); -#if !defined(ART_TARGET_ANDROID) - CHECK(!use_tombstoned_traces_) - << "-Xusetombstonedtraces is only supported in an Android environment"; -#endif - stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile); compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler); compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions); diff --git a/runtime/runtime.h b/runtime/runtime.h index 1b7663cbdf..953acbb948 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -451,7 +451,11 @@ class Runtime { void PreZygoteFork(); void InitNonZygoteOrPostFork( - JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa); + JNIEnv* env, + bool is_system_server, + NativeBridgeAction action, + const char* isa, + bool profile_system_server = false); const instrumentation::Instrumentation* GetInstrumentation() const { return &instrumentation_; @@ -569,6 +573,14 @@ class Runtime { return always_set_hidden_api_warning_flag_; } + void SetHiddenApiEventLogSampleRate(uint32_t rate) { + hidden_api_access_event_log_rate_ = rate; + } + + uint32_t GetHiddenApiEventLogSampleRate() const { + return hidden_api_access_event_log_rate_; + } + bool IsDexFileFallbackEnabled() const { return allow_dex_file_fallback_; } @@ -871,14 +883,6 @@ class Runtime { SignalCatcher* signal_catcher_; - // If true, the runtime will connect to tombstoned via a socket to - // request an open file descriptor to write its traces to. - bool use_tombstoned_traces_; - - // Location to which traces must be written on SIGQUIT. Only used if - // tombstoned_traces_ == false. - std::string stack_trace_file_; - std::unique_ptr<JavaVMExt> java_vm_; std::unique_ptr<jit::Jit> jit_; @@ -1014,7 +1018,8 @@ class Runtime { // Whether access checks on hidden API should be performed. hiddenapi::EnforcementPolicy hidden_api_policy_; - // List of signature prefixes of methods that have been removed from the blacklist + // List of signature prefixes of methods that have been removed from the blacklist, and treated + // as if whitelisted. std::vector<std::string> hidden_api_exemptions_; // Whether the application has used an API which is not restricted but we @@ -1030,6 +1035,10 @@ class Runtime { // when there is a warning. This is only used for testing. bool always_set_hidden_api_warning_flag_; + // How often to log hidden API access to the event log. An integer between 0 (never) + // and 0x10000 (always). + uint32_t hidden_api_access_event_log_rate_; + // Whether threads should dump their native stack on SIGQUIT. bool dump_native_stack_on_sig_quit_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 4121ad69ed..427385d914 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -103,8 +103,6 @@ RUNTIME_OPTIONS_KEY (Unit, ForceNativeBridge) RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose) RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold) RUNTIME_OPTIONS_KEY (unsigned int, StackDumpLockProfThreshold) -RUNTIME_OPTIONS_KEY (bool, UseTombstonedTraces, false) -RUNTIME_OPTIONS_KEY (std::string, StackTraceFile) RUNTIME_OPTIONS_KEY (Unit, MethodTrace) RUNTIME_OPTIONS_KEY (std::string, MethodTraceFile, "/data/misc/trace/method-trace-file.bin") RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB) diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index f95593209b..3089c24c59 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -22,7 +22,7 @@ #include <android-base/logging.h> #include "base/casts.h" -#include "jni_env_ext-inl.h" +#include "jni/jni_env_ext-inl.h" #include "obj_ptr-inl.h" #include "runtime.h" #include "thread-inl.h" diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc index 6a86cc6411..edbce05325 100644 --- a/runtime/scoped_thread_state_change.cc +++ b/runtime/scoped_thread_state_change.cc @@ -19,7 +19,7 @@ #include <type_traits> #include "base/casts.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "obj_ptr-inl.h" #include "runtime-inl.h" diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index d590ad5cc6..f4a27b8397 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -73,19 +73,10 @@ static void DumpCmdLine(std::ostream& os) { #endif } -SignalCatcher::SignalCatcher(const std::string& stack_trace_file, - bool use_tombstoned_stack_trace_fd) - : stack_trace_file_(stack_trace_file), - use_tombstoned_stack_trace_fd_(use_tombstoned_stack_trace_fd), - lock_("SignalCatcher lock"), +SignalCatcher::SignalCatcher() + : lock_("SignalCatcher lock"), cond_("SignalCatcher::cond_", lock_), thread_(nullptr) { -#if !defined(ART_TARGET_ANDROID) - // We're not running on Android, so we can't communicate with tombstoned - // to ask for an open file. - CHECK(!use_tombstoned_stack_trace_fd_); -#endif - SetHaltFlag(false); // Create a raw pthread; its start routine will attach to the runtime. @@ -116,37 +107,11 @@ bool SignalCatcher::ShouldHalt() { return halt_; } -bool SignalCatcher::OpenStackTraceFile(android::base::unique_fd* tombstone_fd, - android::base::unique_fd* output_fd) { - if (use_tombstoned_stack_trace_fd_) { -#if defined(ART_TARGET_ANDROID) - return tombstoned_connect(getpid(), tombstone_fd, output_fd, kDebuggerdJavaBacktrace); -#else - UNUSED(tombstone_fd); - UNUSED(output_fd); -#endif - } - - // The runtime is not configured to dump traces to a file, will LOG(INFO) - // instead. - if (stack_trace_file_.empty()) { - return false; - } - - int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666); - if (fd == -1) { - PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'"; - return false; - } - - output_fd->reset(fd); - return true; -} - void SignalCatcher::Output(const std::string& s) { +#if defined(ART_TARGET_ANDROID) android::base::unique_fd tombstone_fd; android::base::unique_fd output_fd; - if (!OpenStackTraceFile(&tombstone_fd, &output_fd)) { + if (!tombstoned_connect(getpid(), &tombstone_fd, &output_fd, kDebuggerdJavaBacktrace)) { LOG(INFO) << s; return; } @@ -161,19 +126,16 @@ void SignalCatcher::Output(const std::string& s) { file->Erase(); } - const std::string output_path_msg = (use_tombstoned_stack_trace_fd_) ? - "[tombstoned]" : stack_trace_file_; - if (success) { - LOG(INFO) << "Wrote stack traces to '" << output_path_msg << "'"; + LOG(INFO) << "Wrote stack traces to tombstoned"; } else { - PLOG(ERROR) << "Failed to write stack traces to '" << output_path_msg << "'"; + PLOG(ERROR) << "Failed to write stack traces to tombstoned"; } - -#if defined(ART_TARGET_ANDROID) - if (use_tombstoned_stack_trace_fd_ && !tombstoned_notify_completion(tombstone_fd)) { + if (!tombstoned_notify_completion(tombstone_fd)) { PLOG(WARNING) << "Unable to notify tombstoned of dump completion"; } +#else + LOG(INFO) << s; #endif } diff --git a/runtime/signal_catcher.h b/runtime/signal_catcher.h index 8a2a7289de..46eae7e50e 100644 --- a/runtime/signal_catcher.h +++ b/runtime/signal_catcher.h @@ -33,17 +33,7 @@ class Thread; */ class SignalCatcher { public: - // If |use_tombstoned_stack_trace_fd| is |true|, traces will be - // written to a file descriptor provided by tombstoned. The process - // will communicate with tombstoned via a unix domain socket. This - // mode of stack trace dumping is only supported in an Android - // environment. - // - // If false, all traces will be dumped to |stack_trace_file| if it's - // non-empty. If |stack_trace_file| is empty, all traces will be written - // to the log buffer. - SignalCatcher(const std::string& stack_trace_file, - const bool use_tombstoned_stack_trace_fd); + SignalCatcher(); ~SignalCatcher(); void HandleSigQuit() REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, @@ -54,19 +44,12 @@ class SignalCatcher { // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. static void* Run(void* arg) NO_THREAD_SAFETY_ANALYSIS; - // NOTE: We're using android::base::unique_fd here for easier - // interoperability with tombstoned client APIs. - bool OpenStackTraceFile(android::base::unique_fd* tombstone_fd, - android::base::unique_fd* output_fd); void HandleSigUsr1(); void Output(const std::string& s); void SetHaltFlag(bool new_value) REQUIRES(!lock_); bool ShouldHalt() REQUIRES(!lock_); int WaitForSignal(Thread* self, SignalSet& signals) REQUIRES(!lock_); - std::string stack_trace_file_; - const bool use_tombstoned_stack_trace_fd_; - mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable cond_ GUARDED_BY(lock_); bool halt_ GUARDED_BY(lock_); diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index e34f32e0bf..91c27af407 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -23,7 +23,7 @@ #include "base/casts.h" #include "base/mutex-inl.h" #include "base/time_utils.h" -#include "jni_env_ext.h" +#include "jni/jni_env_ext.h" #include "managed_stack-inl.h" #include "obj_ptr.h" #include "thread-current-inl.h" diff --git a/runtime/thread.cc b/runtime/thread.cc index f6ac64f7bd..eada24d257 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -65,8 +65,8 @@ #include "interpreter/interpreter.h" #include "interpreter/shadow_frame.h" #include "java_frame_root_info.h" -#include "java_vm_ext.h" -#include "jni_internal.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object_array-inl.h" diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 44af867d60..b2be549996 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -40,7 +40,7 @@ #include "gc/heap.h" #include "gc/reference_processor.h" #include "gc_root.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "lock_word.h" #include "monitor.h" #include "native_stack_dump.h" diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 15c514e593..608f0ee13c 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,7 +21,7 @@ #include "nativeloader/native_loader.h" #include "base/strlcpy.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "runtime.h" #include "thread-current-inl.h" #include "scoped_thread_state_change-inl.h" diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 72292c33f8..3518d2facd 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2287,14 +2287,10 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::CONST_METHOD_HANDLE: work_line_->SetRegisterType<LockOp::kClear>( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodHandle()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::CONST_METHOD_TYPE: work_line_->SetRegisterType<LockOp::kClear>( this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType()); - // TODO: add compiler support for const-method-{handle,type} (b/66890674) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); break; case Instruction::MONITOR_ENTER: work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_); @@ -3801,16 +3797,8 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( must_fail = true; // Try to find the method also with the other type for better error reporting below // but do not store such bogus lookup result in the DexCache or VerifierDeps. - if (klass->IsInterface()) { - // NB This is normally not really allowed but we want to get any static or private object - // methods for error message purposes. This will never be returned. - // TODO We might want to change the verifier to not require this. - res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size); - } else { - // If there was an interface method with the same signature, - // we would have found it also in the "copied" methods. - DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr); - } + res_method = class_linker->FindIncompatibleMethod( + klass, dex_cache_.Get(), class_loader_.Get(), dex_method_idx); } if (res_method == nullptr) { diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index f5d112c30b..b79334ac7f 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -25,7 +25,7 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "hidden_api.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index f01b82553d..d9ade931d2 100644 --- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -19,8 +19,8 @@ #include "base/casts.h" #include "base/macros.h" -#include "java_vm_ext.h" -#include "jni_env_ext.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_env_ext.h" #include "thread-current-inl.h" namespace art { diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check new file mode 100755 index 0000000000..26a97a48ae --- /dev/null +++ b/test/172-app-image-twice/check @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. + +# Remove all lines not containing "passed". +grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc new file mode 100644 index 0000000000..6c3de20f2d --- /dev/null +++ b/test/172-app-image-twice/debug_print_class.cc @@ -0,0 +1,33 @@ +/* + * 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 "debug_print.h" +#include "dex/dex_file.h" +#include "mirror/class-inl.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls); + LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation() + << "/" << static_cast<const void*>(&klass->GetDexFile()) + << " " << DescribeSpace(klass); +} + +} // namespace art diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/172-app-image-twice/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt new file mode 100644 index 0000000000..028046e872 --- /dev/null +++ b/test/172-app-image-twice/info.txt @@ -0,0 +1 @@ +Regression test for loading the same app image twice. diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile new file mode 100644 index 0000000000..70cb2efbb5 --- /dev/null +++ b/test/172-app-image-twice/profile @@ -0,0 +1 @@ +LTestClass; diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run new file mode 100644 index 0000000000..aa2819075f --- /dev/null +++ b/test/172-app-image-twice/run @@ -0,0 +1,28 @@ +#!/bin/bash +# +# 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. + +# Build an app image with TestClass (specified by profile) and class loader +# context that skips the duplicate class checks. + +# Target and host use a different shell, and we need to special case the +# passing of the class loader context marker. +if [[ "$@" = *" --host "* ]]; then + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option --class-loader-context=\& +else + ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \ + -Xcompiler-option '--class-loader-context=\&' +fi diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java new file mode 100644 index 0000000000..a1c151a6bc --- /dev/null +++ b/test/172-app-image-twice/src/Main.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import java.lang.reflect.Method; + +public class Main { + private static String TEST_NAME = "172-app-image-twice"; + + public static void main(String args[]) throws Exception { + System.loadLibrary(args[0]); + + Class<?> tc1 = Class.forName("TestClass"); + + String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar"; + Class<?> bdcl = Class.forName("dalvik.system.BaseDexClassLoader"); + Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class); + addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath); + + Class<?> tc2 = Class.forName("TestClass"); + + // Add extra logging to simulate libcore logging, this logging should not be compared + // against. + System.out.println("Extra logging"); + + if (tc1 != tc2) { + System.out.println("Class mismatch!"); + debugPrintClass(tc1); + debugPrintClass(tc2); + } else { + System.out.println("passed"); + } + } + + public static native void debugPrintClass(Class<?> cls); +} diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java new file mode 100644 index 0000000000..5381718f6e --- /dev/null +++ b/test/172-app-image-twice/src/TestClass.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class TestClass { +} diff --git a/test/1947-breakpoint-redefine-deopt/check_deopt.cc b/test/1947-breakpoint-redefine-deopt/check_deopt.cc index b40b201c9c..667d8be684 100644 --- a/test/1947-breakpoint-redefine-deopt/check_deopt.cc +++ b/test/1947-breakpoint-redefine-deopt/check_deopt.cc @@ -16,7 +16,7 @@ #include "jni.h" #include "art_method-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "instrumentation.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java index a808e946a9..782748c345 100644 --- a/test/674-hiddenapi/src-art/Main.java +++ b/test/674-hiddenapi/src-art/Main.java @@ -16,6 +16,7 @@ import dalvik.system.InMemoryDexClassLoader; import dalvik.system.PathClassLoader; +import dalvik.system.VMRuntime; import java.io.File; import java.io.InputStream; import java.lang.reflect.Constructor; @@ -34,26 +35,41 @@ public class Main { // Enable hidden API checks in case they are disabled by default. init(); + // TODO there are sequential depencies between these test cases, and bugs + // in the production code may lead to subsequent tests to erroneously pass, + // or test the wrong thing. We rely on not deduping hidden API warnings + // here for the same reasons), meaning the code under test and production + // code are running in different configurations. Each test should be run in + // a fresh process to ensure that they are working correcting and not + // accidentally interfering with eachother. + // Run test with both parent and child dex files loaded with class loaders. // The expectation is that hidden members in parent should be visible to // the child. - doTest(false, false); + doTest(false, false, false); doUnloading(); // Now append parent dex file to boot class path and run again. This time - // the child dex file should not be able to access private APIs of the parent. + // the child dex file should not be able to access private APIs of the + // parent. appendToBootClassLoader(DEX_PARENT_BOOT); - doTest(true, false); + doTest(true, false, false); + doUnloading(); + + // Now run the same test again, but with the blacklist exmemptions list set + // to "L" which matches everything. + doTest(true, false, true); doUnloading(); // And finally append to child to boot class path as well. With both in the // boot class path, access should be granted. appendToBootClassLoader(DEX_CHILD); - doTest(true, true); + doTest(true, true, false); doUnloading(); } - private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception { + private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) + throws Exception { // Load parent dex if it is not in boot class path. ClassLoader parentLoader = null; if (parentInBoot) { @@ -78,12 +94,18 @@ public class Main { // be loaded once, but for some reason even classes from a class loader // cannot register their native methods against symbols in a shared library // loaded by their parent class loader. - String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot); + String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis); + + if (whitelistAllApis) { + VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"}); + } // Invoke ChildClass.runTest Class.forName("ChildClass", true, childLoader) - .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE) - .invoke(null, nativeLibCopy, parentInBoot, childInBoot); + .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE) + .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis); + + VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]); } // Routine which tries to figure out the absolute path of our native library. @@ -122,20 +144,21 @@ public class Main { return buffer; } - // Copy native library to a new file with a unique name so it does not conflict - // with other loaded instance of the same binary file. - private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot) - throws Exception { + // Copy native library to a new file with a unique name so it does not + // conflict with other loaded instance of the same binary file. + private static String createNativeLibCopy( + boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception { String tempFileName = System.mapLibraryName( - "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0")); + "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") + + (whitelistAllApis ? "1" : "0")); File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName); Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath()); return tempFile.getAbsolutePath(); } private static void doUnloading() { - // Do multiple GCs to prevent rare flakiness if some other thread is keeping the - // classloader live. + // Do multiple GCs to prevent rare flakiness if some other thread is + // keeping the classloader live. for (int i = 0; i < 5; ++i) { Runtime.getRuntime().gc(); } diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index ea66f1651e..0349e8fe46 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -70,7 +70,7 @@ public class ChildClass { private static final boolean booleanValues[] = new boolean[] { false, true }; public static void runTest(String libFileName, boolean expectedParentInBoot, - boolean expectedChildInBoot) throws Exception { + boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception { System.load(libFileName); // Check expectations about loading into boot class path. @@ -84,13 +84,14 @@ public class ChildClass { throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") + "in boot class path"); } + ChildClass.everythingWhitelisted = everythingWhitelisted; boolean isSameBoot = (isParentInBoot == isChildInBoot); // Run meaningful combinations of access flags. for (Hiddenness hiddenness : Hiddenness.values()) { final Behaviour expected; - if (isSameBoot || hiddenness == Hiddenness.Whitelist) { + if (isSameBoot || hiddenness == Hiddenness.Whitelist || everythingWhitelisted) { expected = Behaviour.Granted; } else if (hiddenness == Hiddenness.Blacklist) { expected = Behaviour.Denied; @@ -121,6 +122,7 @@ public class ChildClass { checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected); checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected); checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected); + checkLinking("LinkMethodInterface" + suffix, /*takesParameter*/ false, expected); } // Check whether Class.newInstance succeeds. @@ -509,14 +511,16 @@ public class ChildClass { String fn, boolean canAccess) { throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() + "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + + "everythingWhitelisted = " + everythingWhitelisted); } private static void throwAccessException(Class<?> klass, String name, boolean isField, String fn) { throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") + klass.getName() + "." + name + " using " + fn + ". " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + + "everythingWhitelisted = " + everythingWhitelisted); } private static void throwWarningException(Class<?> klass, String name, boolean isField, @@ -524,7 +528,8 @@ public class ChildClass { throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") + klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") + "set the warning flag. " + - "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot); + "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " + + "everythingWhitelisted = " + everythingWhitelisted); } private static void throwModifiersException(Class<?> klass, String name, boolean isField) { @@ -534,6 +539,7 @@ public class ChildClass { private static boolean isParentInBoot; private static boolean isChildInBoot; + private static boolean everythingWhitelisted; private static native boolean hasPendingWarning(); private static native void clearWarning(); diff --git a/test/674-hiddenapi/src-ex/Linking.java b/test/674-hiddenapi/src-ex/Linking.java index a89b92b2b9..0fa0b1912f 100644 --- a/test/674-hiddenapi/src-ex/Linking.java +++ b/test/674-hiddenapi/src-ex/Linking.java @@ -174,6 +174,32 @@ class LinkMethodBlacklist { } } +// INVOKE INSTANCE INTERFACE METHOD + +class LinkMethodInterfaceWhitelist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicWhitelist(); + } +} + +class LinkMethodInterfaceLightGreylist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicLightGreylist(); + } +} + +class LinkMethodInterfaceDarkGreylist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicDarkGreylist(); + } +} + +class LinkMethodInterfaceBlacklist { + public static int access() { + return DummyClass.getInterfaceInstance().methodPublicBlacklist(); + } +} + // INVOKE STATIC METHOD class LinkMethodStaticWhitelist { @@ -199,3 +225,29 @@ class LinkMethodStaticBlacklist { return ParentClass.methodPublicStaticBlacklist(); } } + +// INVOKE INTERFACE STATIC METHOD + +class LinkMethodInterfaceStaticWhitelist { + public static int access() { + return ParentInterface.methodPublicStaticWhitelist(); + } +} + +class LinkMethodInterfaceStaticLightGreylist { + public static int access() { + return ParentInterface.methodPublicStaticLightGreylist(); + } +} + +class LinkMethodInterfaceStaticDarkGreylist { + public static int access() { + return ParentInterface.methodPublicStaticDarkGreylist(); + } +} + +class LinkMethodInterfaceStaticBlacklist { + public static int access() { + return ParentInterface.methodPublicStaticBlacklist(); + } +} diff --git a/test/674-hiddenapi/src/DummyClass.java b/test/674-hiddenapi/src/DummyClass.java new file mode 100644 index 0000000000..51281a2666 --- /dev/null +++ b/test/674-hiddenapi/src/DummyClass.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class DummyClass implements ParentInterface { + public int methodPublicWhitelist() { return 1; } + public int methodPublicLightGreylist() { return 2; } + public int methodPublicDarkGreylist() { return 3; } + public int methodPublicBlacklist() { return 4; } + + public static ParentInterface getInterfaceInstance() { + return new DummyClass(); + } +} diff --git a/test/674-hiddenapi/src/ParentInterface.java b/test/674-hiddenapi/src/ParentInterface.java index e36fe0e6b2..f79ac9d661 100644 --- a/test/674-hiddenapi/src/ParentInterface.java +++ b/test/674-hiddenapi/src/ParentInterface.java @@ -23,9 +23,9 @@ public interface ParentInterface { // INSTANCE METHOD int methodPublicWhitelist(); - int methodPublicBlacklist(); int methodPublicLightGreylist(); int methodPublicDarkGreylist(); + int methodPublicBlacklist(); // STATIC METHOD static int methodPublicStaticWhitelist() { return 21; } diff --git a/test/708-jit-cache-churn/jit.cc b/test/708-jit-cache-churn/jit.cc index 1284a8703d..1b80eb3c0c 100644 --- a/test/708-jit-cache-churn/jit.cc +++ b/test/708-jit-cache-churn/jit.cc @@ -19,7 +19,7 @@ #include "art_method.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc index cab0abf58b..7121d108a4 100644 --- a/test/900-hello-plugin/load_unload.cc +++ b/test/900-hello-plugin/load_unload.cc @@ -21,7 +21,7 @@ #include <android-base/macros.h> #include "art_method-inl.h" -#include "java_vm_ext.h" +#include "jni/java_vm_ext.h" #include "runtime.h" namespace art { diff --git a/test/913-heaps/expected_d8.diff b/test/913-heaps/expected_d8.diff index 3ea3c0d2b0..1ad0cbdd3b 100644 --- a/test/913-heaps/expected_d8.diff +++ b/test/913-heaps/expected_d8.diff @@ -10,8 +10,8 @@ 51c50,51 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 102,103c102 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] @@ -24,8 +24,8 @@ 117c116,117 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 162c162 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] --- @@ -37,8 +37,8 @@ 179c179,180 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 201,202c202 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] @@ -51,8 +51,8 @@ 248c248,249 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] 292d292 < root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] 347c347 @@ -66,5 +66,5 @@ 368c368,369 < root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] --- -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] -> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt index bc943e368e..bbaaedb0af 100644 --- a/test/979-const-method-handle/expected.txt +++ b/test/979-const-method-handle/expected.txt @@ -1,6 +1,9 @@ (int,Integer,System)String +repeatConstMethodType0((int,Integer,System)String) +repeatConstMethodType1((LocalClass)void) Hello World! And Hello Zog Hello World! And Hello Zorba name is HoverFly 2.718281828459045 +repeatConstMethodHandle() Attempting to set Math.E raised IAE diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java index 663814f232..427ca7a306 100644 --- a/test/979-const-method-handle/src/Main.java +++ b/test/979-const-method-handle/src/Main.java @@ -20,78 +20,146 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; class Main { + /** + * Number of iterations run to attempt to trigger JIT compilation. These tests run on ART and + * the RI so they iterate rather than using the ART only native method ensureJitCompiled(). + */ + private static final int ITERATIONS_FOR_JIT = 12000; + + /** A static field updated by method handle getters and setters. */ private static String name = "default"; private static void unreachable() { throw new Error("Unreachable"); } + private static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError("Assertion failure: " + expected + " != " + actual); + } + } + + private static class LocalClass { + public LocalClass() {} + + private int field; + } + @ConstantMethodType( - returnType = String.class, - parameterTypes = {int.class, Integer.class, System.class} - ) + returnType = String.class, + parameterTypes = {int.class, Integer.class, System.class}) private static MethodType methodType0() { unreachable(); return null; } + @ConstantMethodType( + returnType = void.class, + parameterTypes = {LocalClass.class}) + private static MethodType methodType1() { + unreachable(); + return null; + } + + private static void repeatConstMethodType0(MethodType expected) { + System.out.print("repeatConstMethodType0("); + System.out.print(expected); + System.out.println(")"); + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + MethodType actual = methodType0(); + assertEquals(expected, actual); + } + } + + private static void repeatConstMethodType1(MethodType expected) { + System.out.print("repeatConstMethodType1("); + System.out.print(expected); + System.out.println(")"); + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + MethodType actual = methodType1(); + assertEquals(expected, actual); + } + } + static void helloWorld(String who) { System.out.print("Hello World! And Hello "); System.out.println(who); } @ConstantMethodHandle( - kind = ConstantMethodHandle.INVOKE_STATIC, - owner = "Main", - fieldOrMethodName = "helloWorld", - descriptor = "(Ljava/lang/String;)V" - ) + kind = ConstantMethodHandle.INVOKE_STATIC, + owner = "Main", + fieldOrMethodName = "helloWorld", + descriptor = "(Ljava/lang/String;)V") private static MethodHandle printHelloHandle() { unreachable(); return null; } @ConstantMethodHandle( - kind = ConstantMethodHandle.STATIC_PUT, - owner = "Main", - fieldOrMethodName = "name", - descriptor = "Ljava/lang/String;" - ) + kind = ConstantMethodHandle.STATIC_PUT, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;") private static MethodHandle setNameHandle() { unreachable(); return null; } @ConstantMethodHandle( - kind = ConstantMethodHandle.STATIC_GET, - owner = "java/lang/Math", - fieldOrMethodName = "E", - descriptor = "D" - ) + kind = ConstantMethodHandle.STATIC_GET, + owner = "Main", + fieldOrMethodName = "name", + descriptor = "Ljava/lang/String;") + private static MethodHandle getNameHandle() { + unreachable(); + return null; + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.STATIC_GET, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D") private static MethodHandle getMathE() { unreachable(); return null; } @ConstantMethodHandle( - kind = ConstantMethodHandle.STATIC_PUT, - owner = "java/lang/Math", - fieldOrMethodName = "E", - descriptor = "D" - ) + kind = ConstantMethodHandle.STATIC_PUT, + owner = "java/lang/Math", + fieldOrMethodName = "E", + descriptor = "D") private static MethodHandle putMathE() { unreachable(); return null; } + private static void repeatConstMethodHandle() throws Throwable { + System.out.println("repeatConstMethodHandle()"); + String[] values = {"A", "B", "C"}; + for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { + String value = values[i % values.length]; + setNameHandle().invoke(value); + String actual = (String) getNameHandle().invokeExact(); + assertEquals(value, actual); + assertEquals(value, name); + } + } + public static void main(String[] args) throws Throwable { System.out.println(methodType0()); + repeatConstMethodType0( + MethodType.methodType(String.class, int.class, Integer.class, System.class)); + repeatConstMethodType1(MethodType.methodType(void.class, LocalClass.class)); printHelloHandle().invokeExact("Zog"); printHelloHandle().invokeExact("Zorba"); setNameHandle().invokeExact("HoverFly"); System.out.print("name is "); System.out.println(name); System.out.println(getMathE().invoke()); + repeatConstMethodHandle(); try { putMathE().invokeExact(Math.PI); unreachable(); diff --git a/test/Android.bp b/test/Android.bp index 0c6b449877..76189f62a9 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -423,6 +423,7 @@ cc_defaults { "154-gc-loop/heap_interface.cc", "167-visit-locks/visit_locks.cc", "169-threadgroup-jni/jni_daemon_thread.cc", + "172-app-image-twice/debug_print_class.cc", "1945-proxy-method-arguments/get_args.cc", "203-multi-checkpoint/multi_checkpoint.cc", "305-other-fault-handler/fault_handler.cc", diff --git a/runtime/jobject_comparator.h b/test/HiddenApiSignatures/Interface.java index 698d6678d6..f141d09bf6 100644 --- a/runtime/jobject_comparator.h +++ b/test/HiddenApiSignatures/Interface.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * 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. @@ -14,17 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_JOBJECT_COMPARATOR_H_ -#define ART_RUNTIME_JOBJECT_COMPARATOR_H_ +package mypackage.packagea; -#include <jni.h> - -namespace art { - -struct JobjectComparator { - bool operator()(jobject jobj1, jobject jobj2) const; -}; - -} // namespace art - -#endif // ART_RUNTIME_JOBJECT_COMPARATOR_H_ +public interface Interface { + public void method(); +} diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc index fd6273769b..192274e5ae 100644 --- a/test/common/stack_inspect.cc +++ b/test/common/stack_inspect.cc @@ -20,7 +20,7 @@ #include "base/mutex.h" #include "dex/dex_file-inl.h" -#include "jni_internal.h" +#include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "nth_caller_visitor.h" #include "oat_file.h" diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index be1296b990..fad801192a 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -13,6 +13,7 @@ ARCHITECTURES_32="(arm|x86|mips|none)" ARCHITECTURES_64="(arm64|x86_64|mips64|none)" ARCHITECTURES_PATTERN="${ARCHITECTURES_32}" BOOT_IMAGE="" +CHROOT= COMPILE_FLAGS="" DALVIKVM="dalvikvm32" DEBUGGER="n" @@ -80,11 +81,6 @@ DEX2OAT_TIMEOUT="300" # 5 mins # The *hard* timeout where we really start trying to kill the dex2oat. DEX2OAT_RT_TIMEOUT="360" # 6 mins -# if "y", set -Xstacktracedir and inform the test of its location. When -# this is set, stack trace dumps (from signal 3) will be written to a file -# under this directory instead of stdout. -SET_STACK_TRACE_DUMP_DIR="n" - # if "y", run 'sync' before dalvikvm to make sure all files from # build step (e.g. dex2oat) were finished writing. SYNC_BEFORE_RUN="n" @@ -304,6 +300,10 @@ while true; do elif [ "x$1" = "x--no-optimize" ]; then OPTIMIZE="n" shift + elif [ "x$1" = "x--chroot" ]; then + shift + CHROOT="$1" + shift elif [ "x$1" = "x--android-root" ]; then shift ANDROID_ROOT="$1" @@ -364,9 +364,6 @@ while true; do elif [ "x$1" = "x--random-profile" ]; then RANDOM_PROFILE="y" shift - elif [ "x$1" = "x--set-stack-trace-dump-dir" ]; then - SET_STACK_TRACE_DUMP_DIR="y" - shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -375,7 +372,8 @@ while true; do fi done -mkdir_locations="" +# The DEX_LOCATION with the chroot prefix, if any. +CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION" if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" @@ -383,14 +381,6 @@ if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}" done - - if [ "$SET_STACK_TRACE_DUMP_DIR" = "y" ]; then - # Note that DEX_LOCATION is used as a proxy for tmpdir throughout this - # file (it will be under the test specific folder). - mkdir_locations="${mkdir_locations} $DEX_LOCATION/stack_traces" - FLAGS="${FLAGS} -Xstacktracedir:$DEX_LOCATION/stack_traces" - ARGS="${ARGS} --stack-trace-dir $DEX_LOCATION/stack_traces" - fi fi if [ "x$1" = "x" ] ; then @@ -684,7 +674,7 @@ profman_cmdline="true" dex2oat_cmdline="true" vdex_cmdline="true" dm_cmdline="true" -mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/dalvik-cache/$ISA" +mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" sync_cmdline="true" @@ -835,28 +825,28 @@ if [ "$HOST" = "n" ]; then adb root > /dev/null adb wait-for-device if [ "$QUIET" = "n" ]; then - adb shell rm -rf $DEX_LOCATION - adb shell mkdir -p $DEX_LOCATION - adb push $TEST_NAME.jar $DEX_LOCATION - adb push $TEST_NAME-ex.jar $DEX_LOCATION + adb shell rm -rf $CHROOT_DEX_LOCATION + adb shell mkdir -p $CHROOT_DEX_LOCATION + adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION + adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then - adb push profile $DEX_LOCATION + adb push profile $CHROOT_DEX_LOCATION fi # Copy resource folder if [ -d res ]; then - adb push res $DEX_LOCATION + adb push res $CHROOT_DEX_LOCATION fi else - adb shell rm -r $DEX_LOCATION >/dev/null 2>&1 - adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1 - adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1 - adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1 + adb shell rm -rf $CHROOT_DEX_LOCATION >/dev/null 2>&1 + adb shell mkdir -p $CHROOT_DEX_LOCATION >/dev/null 2>&1 + adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1 + adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1 if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then - adb push profile $DEX_LOCATION >/dev/null 2>&1 + adb push profile $CHROOT_DEX_LOCATION >/dev/null 2>&1 fi # Copy resource folder if [ -d res ]; then - adb push res $DEX_LOCATION >/dev/null 2>&1 + adb push res $CHROOT_DEX_LOCATION >/dev/null 2>&1 fi fi @@ -865,7 +855,7 @@ if [ "$HOST" = "n" ]; then # Current default installation is dalvikvm 64bits and dex2oat 32bits, # so we can only use LD_LIBRARY_PATH when testing on a local # installation. - LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH + LD_LIBRARY_PATH="$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH" fi # System libraries needed by libarttestd.so @@ -907,14 +897,18 @@ if [ "$HOST" = "n" ]; then fi if [ "$QUIET" = "n" ]; then - adb push $cmdfile $DEX_LOCATION/cmdline.sh + adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh else - adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1 + adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh >/dev/null 2>&1 fi exit_status=0 if [ "$DRY_RUN" != "y" ]; then - adb shell sh $DEX_LOCATION/cmdline.sh + if [ -n "$CHROOT" ]; then + adb shell chroot "$CHROOT" sh $DEX_LOCATION/cmdline.sh + else + adb shell sh $DEX_LOCATION/cmdline.sh + fi exit_status=$? fi diff --git a/test/knownfailures.json b/test/knownfailures.json index e109bf5a8f..f473a99a27 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -412,10 +412,11 @@ ".*methodhandle.*", ".*method-handle.*", ".*varhandle.*", - ".*var-handle.*" + ".*var-handle.*", + "716-jli-jit-samples" ], "description": [ - "Tests that use invoke-polymorphic/invoke-custom which is not yet supported by", + "Tests for bytecodes introduced after DEX version 037 that are unsupported by", "dexter/slicer." ], "bug": "b/37272822", @@ -733,6 +734,7 @@ "164-resolution-trampoline-dex-cache", "167-visit-locks", "168-vmstack-annotated", + "172-app-image-twice", "201-built-in-except-detail-messages", "203-multi-checkpoint", "304-method-tracing", diff --git a/test/run-test b/test/run-test index 5f85b0875b..be0a88d1f9 100755 --- a/test/run-test +++ b/test/run-test @@ -121,6 +121,8 @@ if [ -z "$HIDDENAPI" ]; then export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi" fi +chroot= + info="info.txt" build="build" run="run" @@ -380,6 +382,16 @@ while true; do break fi shift + elif [ "x$1" = "x--chroot" ]; then + shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --chroot" 1>&2 + usage="yes" + break + fi + chroot="$1" + run_args="${run_args} --chroot $1" + shift elif [ "x$1" = "x--android-root" ]; then shift if [ "x$1" = "x" ]; then @@ -449,6 +461,9 @@ while true; do fi done +# The DEX_LOCATION with the chroot prefix, if any. +chroot_dex_location="$chroot$DEX_LOCATION" + run_args="${run_args} ${image_args}" # Allocate file descriptor real_stderr and redirect it to the shell's error # output (fd 2). @@ -476,7 +491,7 @@ function err_echo() { # tmp_dir may be relative, resolve. # # Cannot use realpath, as it does not exist on Mac. -# Cannot us a simple "cd", as the path might not be created yet. +# Cannot use a simple "cd", as the path might not be created yet. # Cannot use readlink -m, as it does not exist on Mac. # Fallback to nuclear option: noncanonical_tmp_dir=$tmp_dir @@ -550,7 +565,13 @@ if [ "$target_mode" = "no" ]; then if [ "$runtime" = "jvm" ]; then if [ "$prebuild_mode" = "yes" ]; then err_echo "--prebuild with --jvm is unsupported" - exit 1; + exit 1 + fi + else + # ART/Dalvik host mode. + if [ -n "$chroot" ]; then + err_echo "--chroot with --host is unsupported" + exit 1 fi fi fi @@ -628,6 +649,12 @@ if [ "$bisection_search" = "yes" -a "$have_patchoat" = "no" ]; then usage="yes" fi +# TODO: Chroot-based bisection search is not supported yet (see below); implement it. +if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then + err_echo "--chroot with --bisection-search is unsupported" + exit 1 +fi + if [ "$usage" = "no" ]; then if [ "x$1" = "x" -o "x$1" = "x-" ]; then test_dir=`basename "$oldwd"` @@ -732,6 +759,7 @@ if [ "$usage" = "yes" ]; then echo " Run with jvmti method redefinition stress testing" echo " --always-clean Delete the test files even if the test fails." echo " --never-clean Keep the test files even if the test succeeds." + echo " --chroot [newroot] Run with root directory set to newroot." echo " --android-root [path] The path on target for the android root. (/system by default)." echo " --dex2oat-swap Use a dex2oat swap file." echo " --instruction-set-features [string]" @@ -866,7 +894,7 @@ if [ "$dev_mode" = "yes" ]; then if [ "$run_exit" = "0" ]; then if [ "$run_checker" = "yes" ]; then if [ "$target_mode" = "yes" ]; then - adb pull $cfg_output_dir/$cfg_output &> /dev/null + adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null fi "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1 checker_exit="$?" @@ -888,7 +916,7 @@ elif [ "$update_mode" = "yes" ]; then "./${run}" $run_args "$@" >"$output" 2>&1 if [ "$run_checker" = "yes" ]; then if [ "$target_mode" = "yes" ]; then - adb pull $cfg_output_dir/$cfg_output &> /dev/null + adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null fi "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1 fi @@ -926,7 +954,7 @@ else good_run="no" elif [ "$run_checker" = "yes" ]; then if [ "$target_mode" = "yes" ]; then - adb pull $cfg_output_dir/$cfg_output &> /dev/null + adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null fi "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1 checker_exit="$?" @@ -986,6 +1014,7 @@ fi ) 2>&${real_stderr} 1>&2 # Attempt bisection only if the test failed. +# TODO: Implement support for chroot-based bisection search. if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then # Bisecting works by skipping different optimization passes which breaks checker assertions. if [ "$run_checker" == "yes" ]; then @@ -997,17 +1026,18 @@ if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then maybe_device_mode="" raw_cmd="" if [ "$target_mode" = "yes" ]; then - # Produce cmdline.sh in $DEX_LOCATION. "$@" is passed as a runtime option + # Produce cmdline.sh in $chroot_dex_location. "$@" is passed as a runtime option # so that cmdline.sh forwards its arguments to dalvikvm. invoke-with is set # to exec in order to preserve pid when calling dalvikvm. This is required # for bisection search to correctly retrieve logs from device. "./${run}" $run_args --runtime-option '"$@"' --invoke-with exec --dry-run "$@" &> /dev/null - adb shell chmod u+x "$DEX_LOCATION/cmdline.sh" + adb shell chmod u+x "$chroot_dex_location/cmdline.sh" maybe_device_mode="--device" raw_cmd="$DEX_LOCATION/cmdline.sh" else raw_cmd="$cwd/${run} --external-log-tags $run_args $@" fi + # TODO: Pass a `--chroot` option to the bisection_search.py script and use it there. $ANDROID_BUILD_TOP/art/tools/bisection_search/bisection_search.py \ $maybe_device_mode \ --raw-cmd="$raw_cmd" \ @@ -1023,7 +1053,7 @@ if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; cd "$oldwd" rm -rf "$tmp_dir" if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then - adb shell rm -rf $DEX_LOCATION + adb shell rm -rf $chroot_dex_location fi if [ "$good" = "yes" ]; then exit 0 @@ -1040,7 +1070,7 @@ fi else echo "${TEST_NAME} files left in ${tmp_dir} on host" if [ "$target_mode" == "yes" ]; then - echo "and in ${DEX_LOCATION} on target" + echo "and in ${chroot_dex_location} on target" fi fi diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 7564f5a6b4..0c1c308218 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -91,6 +91,8 @@ HOST_2ND_ARCH_PREFIX = _get_build_var('HOST_2ND_ARCH_PREFIX') HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES = _env.get( HOST_2ND_ARCH_PREFIX + 'DEX2OAT_HOST_INSTRUCTION_SET_FEATURES') +ART_TEST_CHROOT = _env.get('ART_TEST_CHROOT') + ART_TEST_ANDROID_ROOT = _env.get('ART_TEST_ANDROID_ROOT') ART_TEST_WITH_STRACE = _getEnvBoolean('ART_TEST_DEBUG_GC', False) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 88b509d3b7..254ffc9db1 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -320,6 +320,9 @@ def run_tests(tests): if env.ART_TEST_BISECTION: options_all += ' --bisection-search' + if env.ART_TEST_CHROOT: + options_all += ' --chroot ' + env.ART_TEST_CHROOT + if env.ART_TEST_ANDROID_ROOT: options_all += ' --android-root ' + env.ART_TEST_ANDROID_ROOT diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 31bddd5213..10eb9360af 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -83,6 +83,10 @@ elif [[ $mode == "target" ]]; then make_command+=" debuggerd su" make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ " make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt" + if [[ -n "$ART_TEST_CHROOT" ]]; then + # These targets are needed for the chroot environment. + make_command+=" crash_dump event-log-tags" + fi mode_suffix="-target" fi diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh new file mode 100755 index 0000000000..53072ae7c5 --- /dev/null +++ b/tools/cleanup-buildbot-device.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +green='\033[0;32m' +nc='\033[0m' + +# Setup as root, as device cleanup requires it. +adb root +adb wait-for-device + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Check that ART_TEST_CHROOT is correctly defined. + if [[ "x$ART_TEST_CHROOT" != x/* ]]; then + echo "$ART_TEST_CHROOT is not an absolute path" + exit 1 + fi + + echo -e "${green}Clean up /system in chroot${nc}" + # Remove all files under /system except the potential property_contexts file. + # + # The current ART Buildbot set-up runs the "setup device" step + # (performed by script tools/setup-buildbot-device.sh) before the + # "device cleanup" step (implemented by this script). As + # property_contexts file aliases are created during the former step, + # we need this exception to prevent the property_contexts file under + # /system in the chroot from being removed by the latter step. + # + # TODO: Reorder ART Buildbot steps so that "device cleanup" happens + # before "setup device" and remove this special case. + # + # TODO: Also consider adding a "tear down device" step on the ART + # Buildbot (at the very end of a build) undoing (some of) the work + # done in the "device setup" step. + adb shell find "$ART_TEST_CHROOT/system" \ + ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ + ! -type d \ + -exec rm -f \{\} + + + echo -e "${green}Clean up some subdirs in /data in chroot${nc}" + adb shell rm -rf \ + "$ART_TEST_CHROOT/data/local/tmp/*" \ + "$ART_TEST_CHROOT/data/art-test" \ + "$ART_TEST_CHROOT/data/nativetest" \ + "$ART_TEST_CHROOT/data/nativetest64" \ + "$ART_TEST_CHROOT/data/run-test" \ + "$ART_TEST_CHROOT/data/dalvik-cache/*" \ + "$ART_TEST_CHROOT/data/misc/trace/*" +else + adb shell rm -rf \ + /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*' +fi diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 56d412bfa0..d376cad9da 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -68,6 +68,8 @@ test="org.apache.harmony.jpda.tests.share.AllTests" mode="target" # Use JIT compiling by default. use_jit=true +# Don't use chroot by default. +use_chroot=false variant_cmdline_parameter="--variant=X32" dump_command="/bin/true" # Timeout of JDWP test in ms. @@ -110,6 +112,15 @@ while true; do # We don't care about jit with the RI use_jit=false shift + elif [[ "$1" == "--chroot" ]]; then + use_chroot=true + # Adjust settings for chroot environment. + art="/system/bin/art" + art_debugee="sh /system/bin/art" + vm_command="--vm-command=$art" + device_dir="--device-dir=/tmp" + # Shift the "--chroot" flag and its argument. + shift 2 elif [[ $1 == --test-timeout-ms ]]; then # Remove the --test-timeout-ms from the arguments. args=${args/$1} @@ -191,6 +202,12 @@ while true; do fi done +if $use_chroot && [[ $mode == "host" ]]; then + # Chroot-based testing is not supported on host. + echo "Cannot use --chroot with --mode=host" + exit 1 +fi + if [[ $has_gdb = "yes" ]]; then if [[ $explicit_debug = "no" ]]; then debug="yes" diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 26b5c0a09b..3537c1b861 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -104,10 +104,14 @@ vogar_args=$@ gcstress=false debug=false +# Don't use device mode by default. +device_mode=false +# Don't use chroot by default. +use_chroot=false + while true; do if [[ "$1" == "--mode=device" ]]; then - vogar_args="$vogar_args --device-dir=/data/local/tmp" - vogar_args="$vogar_args --vm-command=$android_root/bin/art" + device_mode=true vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art" shift elif [[ "$1" == "--mode=host" ]]; then @@ -131,6 +135,10 @@ while true; do elif [[ "$1" == "-Xgc:gcstress" ]]; then gcstress=true shift + elif [[ "$1" == "--chroot" ]]; then + use_chroot=true + # Shift the "--chroot" flag and its argument. + shift 2 elif [[ "$1" == "" ]]; then break else @@ -138,6 +146,23 @@ while true; do fi done +if $device_mode; then + if $use_chroot; then + vogar_args="$vogar_args --device-dir=/tmp" + vogar_args="$vogar_args --vm-command=/system/bin/art" + else + vogar_args="$vogar_args --device-dir=/data/local/tmp" + vogar_args="$vogar_args --vm-command=$android_root/bin/art" + fi +else + # Host mode. + if $use_chroot; then + # Chroot-based testing is not supported on host. + echo "Cannot use --chroot with --mode=host" + exit 1 + fi +fi + # Increase the timeout, as vogar cannot set individual test # timeout when being asked to run packages, and some tests go above # the default timeout. diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 5ce7f5244e..f71d973925 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -17,8 +17,7 @@ green='\033[0;32m' nc='\033[0m' -# Setup as root, as the next buildbot step (device cleanup) requires it. -# This is also required to set the date, if needed. +# Setup as root, as some actions performed here (e.g. setting the date) requires it. adb root adb wait-for-device @@ -100,3 +99,58 @@ else processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') for i in $processes; do adb shell kill -9 $i; done fi + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Prepare the chroot dir. + echo -e "${green}Prepare the chroot dir in $ART_TEST_CHROOT${nc}" + + # Check that ART_TEST_CHROOT is correctly defined. + [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; } + + # Create chroot. + adb shell mkdir -p "$ART_TEST_CHROOT" + + # Provide property_contexts file(s) in chroot. + # This is required to have Android system properties work from the chroot. + # Notes: + # - In Android N, only '/property_contexts' is expected. + # - In Android O, property_context files are expected under /system and /vendor. + # (See bionic/libc/bionic/system_properties.cpp for more information.) + property_context_files="/property_contexts \ + /system/etc/selinux/plat_property_contexts \ + /vendor/etc/selinux/nonplat_property_context \ + /plat_property_contexts \ + /nonplat_property_contexts" + for f in $property_context_files; do + adb shell test -f "$f" \ + "&&" mkdir -p "$ART_TEST_CHROOT$(dirname $f)" \ + "&&" cp -f "$f" "$ART_TEST_CHROOT$f" + done + + # Create directories required for ART testing in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/tmp" + adb shell mkdir -p "$ART_TEST_CHROOT/data/dalvik-cache" + adb shell mkdir -p "$ART_TEST_CHROOT/data/local/tmp" + + # Populate /etc in chroot with required files. + adb shell mkdir -p "$ART_TEST_CHROOT/system/etc" + adb shell "cd $ART_TEST_CHROOT && ln -s system/etc etc" + + # Provide /proc in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/proc" + adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \ + || adb shell mount -t proc proc "$ART_TEST_CHROOT/proc" + + # Provide /sys in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/sys" + adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \ + || adb shell mount -t sysfs sysfs "$ART_TEST_CHROOT/sys" + # Provide /sys/kernel/debug in chroot. + adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \ + || adb shell mount -t debugfs debugfs "$ART_TEST_CHROOT/sys/kernel/debug" + + # Provide /dev in chroot. + adb shell mkdir -p "$ART_TEST_CHROOT/dev" + adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \ + || adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev" +fi diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc index 736abb7f17..154c60f6ac 100644 --- a/tools/veridex/flow_analysis.cc +++ b/tools/veridex/flow_analysis.cc @@ -112,7 +112,12 @@ void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cl RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls); } -const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) { +void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls) { + current_registers_[dex_register] = + RegisterValue(RegisterSource::kConstant, value, DexFileReference(nullptr, 0), cls); +} + +const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) const { return current_registers_[dex_register]; } @@ -131,6 +136,49 @@ RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) { return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls); } +int VeriFlowAnalysis::GetBranchFlags(const Instruction& instruction) const { + switch (instruction.Opcode()) { + #define IF_XX(cond, op) \ + case Instruction::IF_##cond: { \ + RegisterValue lhs = GetRegister(instruction.VRegA()); \ + RegisterValue rhs = GetRegister(instruction.VRegB()); \ + if (lhs.IsConstant() && rhs.IsConstant()) { \ + if (lhs.GetConstant() op rhs.GetConstant()) { \ + return Instruction::kBranch; \ + } else { \ + return Instruction::kContinue; \ + } \ + } \ + break; \ + } \ + case Instruction::IF_##cond##Z: { \ + RegisterValue val = GetRegister(instruction.VRegA()); \ + if (val.IsConstant()) { \ + if (val.GetConstant() op 0) { \ + return Instruction::kBranch; \ + } else { \ + return Instruction::kContinue; \ + } \ + } \ + break; \ + } + + IF_XX(EQ, ==); + IF_XX(NE, !=); + IF_XX(LT, <); + IF_XX(LE, <=); + IF_XX(GT, >); + IF_XX(GE, >=); + + #undef IF_XX + + default: + break; + } + + return Instruction::FlagsOf(instruction.Opcode()); +} + void VeriFlowAnalysis::AnalyzeCode() { std::vector<uint32_t> work_list; work_list.push_back(0); @@ -149,16 +197,17 @@ void VeriFlowAnalysis::AnalyzeCode() { ProcessDexInstruction(inst); SetVisited(dex_pc); - int opcode_flags = Instruction::FlagsOf(inst.Opcode()); - if ((opcode_flags & Instruction::kContinue) != 0) { - if ((opcode_flags & Instruction::kBranch) != 0) { + int branch_flags = GetBranchFlags(inst); + + if ((branch_flags & Instruction::kContinue) != 0) { + if ((branch_flags & Instruction::kBranch) != 0) { uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset(); if (MergeRegisterValues(branch_dex_pc)) { work_list.push_back(branch_dex_pc); } } dex_pc += inst.SizeInCodeUnits(); - } else if ((opcode_flags & Instruction::kBranch) != 0) { + } else if ((branch_flags & Instruction::kBranch) != 0) { dex_pc += inst.GetTargetOffset(); DCHECK(IsBranchTarget(dex_pc)); } else { @@ -178,12 +227,30 @@ void VeriFlowAnalysis::AnalyzeCode() { void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { switch (instruction.Opcode()) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: + case Instruction::CONST_4: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_11n(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + case Instruction::CONST_16: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_21s(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + + case Instruction::CONST: { + int32_t register_index = instruction.VRegA(); + int32_t value = instruction.VRegB_31i(); + UpdateRegister(register_index, value, VeriClass::integer_); + break; + } + case Instruction::CONST_HIGH16: { int32_t register_index = instruction.VRegA(); - UpdateRegister(register_index, VeriClass::integer_); + int32_t value = instruction.VRegB_21h(); + UpdateRegister(register_index, value, VeriClass::integer_); break; } @@ -243,43 +310,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::INVOKE_STATIC: case Instruction::INVOKE_SUPER: case Instruction::INVOKE_VIRTUAL: { - VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c()); - uint32_t args[5]; - instruction.GetVarArgs(args); - if (method == VeriClass::forName_) { - RegisterValue value = GetRegister(args[0]); - last_result_ = RegisterValue( - value.GetSource(), value.GetDexFileReference(), VeriClass::class_); - } else if (IsGetField(method)) { - RegisterValue cls = GetRegister(args[0]); - RegisterValue name = GetRegister(args[1]); - field_uses_.push_back(std::make_pair(cls, name)); - last_result_ = GetReturnType(instruction.VRegB_35c()); - } else if (IsGetMethod(method)) { - RegisterValue cls = GetRegister(args[0]); - RegisterValue name = GetRegister(args[1]); - method_uses_.push_back(std::make_pair(cls, name)); - last_result_ = GetReturnType(instruction.VRegB_35c()); - } else if (method == VeriClass::getClass_) { - RegisterValue obj = GetRegister(args[0]); - const VeriClass* cls = obj.GetType(); - if (cls != nullptr && cls->GetClassDef() != nullptr) { - const DexFile::ClassDef* def = cls->GetClassDef(); - last_result_ = RegisterValue( - RegisterSource::kClass, - DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_), - VeriClass::class_); - } else { - last_result_ = RegisterValue( - obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); - } - } else if (method == VeriClass::loadClass_) { - RegisterValue value = GetRegister(args[1]); - last_result_ = RegisterValue( - value.GetSource(), value.GetDexFileReference(), VeriClass::class_); - } else { - last_result_ = GetReturnType(instruction.VRegB_35c()); - } + last_result_ = AnalyzeInvoke(instruction, /* is_range */ false); break; } @@ -288,7 +319,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::INVOKE_STATIC_RANGE: case Instruction::INVOKE_SUPER_RANGE: case Instruction::INVOKE_VIRTUAL_RANGE: { - last_result_ = GetReturnType(instruction.VRegB_3rc()); + last_result_ = AnalyzeInvoke(instruction, /* is_range */ true); break; } @@ -304,6 +335,8 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::RETURN: { break; } + + // If operations will be handled when looking at the control flow. #define IF_XX(cond) \ case Instruction::IF_##cond: break; \ case Instruction::IF_##cond##Z: break @@ -315,6 +348,8 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { IF_XX(GT); IF_XX(GE); + #undef IF_XX + case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { @@ -520,6 +555,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { + AnalyzeFieldSet(instruction); break; } @@ -530,7 +566,13 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { - UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c())); + uint32_t dest_reg = instruction.VRegA_21c(); + uint16_t field_index = instruction.VRegB_21c(); + if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) { + UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_); + } else { + UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c())); + } break; } @@ -541,6 +583,7 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { + AnalyzeFieldSet(instruction); break; } @@ -613,7 +656,112 @@ void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) { void VeriFlowAnalysis::Run() { FindBranches(); + uint32_t number_of_registers = code_item_accessor_.RegistersSize(); + uint32_t number_of_parameters = code_item_accessor_.InsSize(); + std::vector<RegisterValue>& initial_values = *dex_registers_[0].get(); + for (uint32_t i = 0; i < number_of_parameters; ++i) { + initial_values[number_of_registers - number_of_parameters + i] = RegisterValue( + RegisterSource::kParameter, + i, + DexFileReference(&resolver_->GetDexFile(), method_id_), + nullptr); + } AnalyzeCode(); } +static uint32_t GetParameterAt(const Instruction& instruction, + bool is_range, + uint32_t* args, + uint32_t index) { + return is_range ? instruction.VRegC() + index : args[index]; +} + +RegisterValue FlowAnalysisCollector::AnalyzeInvoke(const Instruction& instruction, bool is_range) { + uint32_t id = is_range ? instruction.VRegB_3rc() : instruction.VRegB_35c(); + VeriMethod method = resolver_->GetMethod(id); + uint32_t args[5]; + if (!is_range) { + instruction.GetVarArgs(args); + } + + if (method == VeriClass::forName_) { + // Class.forName. Fetch the first parameter. + RegisterValue value = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + return RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); + } else if (IsGetField(method)) { + // Class.getField or Class.getDeclaredField. Fetch the first parameter for the class, and the + // second parameter for the field name. + RegisterValue cls = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + RegisterValue name = GetRegister(GetParameterAt(instruction, is_range, args, 1)); + uses_.push_back(ReflectAccessInfo(cls, name, /* is_method */ false)); + return GetReturnType(id); + } else if (IsGetMethod(method)) { + // Class.getMethod or Class.getDeclaredMethod. Fetch the first parameter for the class, and the + // second parameter for the field name. + RegisterValue cls = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + RegisterValue name = GetRegister(GetParameterAt(instruction, is_range, args, 1)); + uses_.push_back(ReflectAccessInfo(cls, name, /* is_method */ true)); + return GetReturnType(id); + } else if (method == VeriClass::getClass_) { + // Get the type of the first parameter. + RegisterValue obj = GetRegister(GetParameterAt(instruction, is_range, args, 0)); + const VeriClass* cls = obj.GetType(); + if (cls != nullptr && cls->GetClassDef() != nullptr) { + const DexFile::ClassDef* def = cls->GetClassDef(); + return RegisterValue( + RegisterSource::kClass, + DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_), + VeriClass::class_); + } else { + return RegisterValue( + obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_); + } + } else if (method == VeriClass::loadClass_) { + // ClassLoader.loadClass. Fetch the first parameter. + RegisterValue value = GetRegister(GetParameterAt(instruction, is_range, args, 1)); + return RegisterValue( + value.GetSource(), value.GetDexFileReference(), VeriClass::class_); + } else { + // Return a RegisterValue referencing the method whose type is the return type + // of the method. + return GetReturnType(id); + } +} + +void FlowAnalysisCollector::AnalyzeFieldSet(const Instruction& instruction ATTRIBUTE_UNUSED) { + // There are no fields that escape reflection uses. +} + +RegisterValue FlowAnalysisSubstitutor::AnalyzeInvoke(const Instruction& instruction, + bool is_range) { + uint32_t id = is_range ? instruction.VRegB_3rc() : instruction.VRegB_35c(); + MethodReference method(&resolver_->GetDexFile(), id); + // TODO: doesn't work for multidex + // TODO: doesn't work for overriding (but maybe should be done at a higher level); + if (accesses_.find(method) == accesses_.end()) { + return GetReturnType(id); + } + uint32_t args[5]; + if (!is_range) { + instruction.GetVarArgs(args); + } + for (const ReflectAccessInfo& info : accesses_.at(method)) { + if (info.cls.IsParameter() || info.name.IsParameter()) { + RegisterValue cls = info.cls.IsParameter() + ? GetRegister(GetParameterAt(instruction, is_range, args, info.cls.GetParameterIndex())) + : info.cls; + RegisterValue name = info.name.IsParameter() + ? GetRegister(GetParameterAt(instruction, is_range, args, info.name.GetParameterIndex())) + : info.name; + uses_.push_back(ReflectAccessInfo(cls, name, info.is_method)); + } + } + return GetReturnType(id); +} + +void FlowAnalysisSubstitutor::AnalyzeFieldSet(const Instruction& instruction ATTRIBUTE_UNUSED) { + // TODO: analyze field sets. +} + } // namespace art diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h index 80ae5fc9df..fc093600c3 100644 --- a/tools/veridex/flow_analysis.h +++ b/tools/veridex/flow_analysis.h @@ -21,13 +21,11 @@ #include "dex/dex_file_reference.h" #include "dex/method_reference.h" #include "hidden_api.h" +#include "resolver.h" #include "veridex.h" namespace art { -class VeridexClass; -class VeridexResolver; - /** * The source where a dex register comes from. */ @@ -37,6 +35,7 @@ enum class RegisterSource { kMethod, kClass, kString, + kConstant, kNone }; @@ -45,13 +44,34 @@ enum class RegisterSource { */ class RegisterValue { public: - RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {} + RegisterValue() : source_(RegisterSource::kNone), + value_(0), + reference_(nullptr, 0), + type_(nullptr) {} RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type) - : source_(source), reference_(reference), type_(type) {} + : source_(source), value_(0), reference_(reference), type_(type) {} + + RegisterValue(RegisterSource source, + uint32_t value, + DexFileReference reference, + const VeriClass* type) + : source_(source), value_(value), reference_(reference), type_(type) {} RegisterSource GetSource() const { return source_; } DexFileReference GetDexFileReference() const { return reference_; } const VeriClass* GetType() const { return type_; } + uint32_t GetParameterIndex() const { + CHECK(IsParameter()); + return value_; + } + uint32_t GetConstant() const { + CHECK(IsConstant()); + return value_; + } + bool IsParameter() const { return source_ == RegisterSource::kParameter; } + bool IsClass() const { return source_ == RegisterSource::kClass; } + bool IsString() const { return source_ == RegisterSource::kString; } + bool IsConstant() const { return source_ == RegisterSource::kConstant; } std::string ToString() const { switch (source_) { @@ -68,6 +88,8 @@ class RegisterValue { } case RegisterSource::kClass: return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index)); + case RegisterSource::kParameter: + return std::string("Parameter of ") + reference_.dex_file->PrettyMethod(reference_.index); default: return "<unknown>"; } @@ -75,6 +97,7 @@ class RegisterValue { private: RegisterSource source_; + uint32_t value_; DexFileReference reference_; const VeriClass* type_; }; @@ -85,22 +108,18 @@ struct InstructionInfo { class VeriFlowAnalysis { public: - VeriFlowAnalysis(VeridexResolver* resolver, - const CodeItemDataAccessor& code_item_accessor) + VeriFlowAnalysis(VeridexResolver* resolver, const ClassDataItemIterator& it) : resolver_(resolver), - code_item_accessor_(code_item_accessor), - dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()), - instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {} + method_id_(it.GetMemberIndex()), + code_item_accessor_(resolver->GetDexFile(), it.GetMethodCodeItem()), + dex_registers_(code_item_accessor_.InsnsSizeInCodeUnits()), + instruction_infos_(code_item_accessor_.InsnsSizeInCodeUnits()) {} void Run(); - const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const { - return field_uses_; - } - - const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const { - return method_uses_; - } + virtual RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) = 0; + virtual void AnalyzeFieldSet(const Instruction& instruction) = 0; + virtual ~VeriFlowAnalysis() {} private: // Find all branches in the code. @@ -124,14 +143,22 @@ class VeriFlowAnalysis { uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id); void UpdateRegister(uint32_t dex_register, const RegisterValue& value); void UpdateRegister(uint32_t dex_register, const VeriClass* cls); - const RegisterValue& GetRegister(uint32_t dex_register); + void UpdateRegister(uint32_t dex_register, int32_t value, const VeriClass* cls); void ProcessDexInstruction(const Instruction& inst); void SetVisited(uint32_t dex_pc); - RegisterValue GetReturnType(uint32_t method_index); RegisterValue GetFieldType(uint32_t field_index); + int GetBranchFlags(const Instruction& instruction) const; + + protected: + const RegisterValue& GetRegister(uint32_t dex_register) const; + RegisterValue GetReturnType(uint32_t method_index); + VeridexResolver* resolver_; - const CodeItemDataAccessor& code_item_accessor_; + + private: + const uint32_t method_id_; + CodeItemDataAccessor code_item_accessor_; // Vector of register values for all branch targets. std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_; @@ -144,12 +171,59 @@ class VeriFlowAnalysis { // The value of invoke instructions, to be fetched when visiting move-result. RegisterValue last_result_; +}; + +struct ReflectAccessInfo { + RegisterValue cls; + RegisterValue name; + bool is_method; + + ReflectAccessInfo(RegisterValue c, RegisterValue n, bool m) : cls(c), name(n), is_method(m) {} - // List of reflection field uses found. - std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_; + bool IsConcrete() const { + // We capture RegisterSource::kString for the class, for example in Class.forName. + return (cls.IsClass() || cls.IsString()) && name.IsString(); + } +}; + +// Collects all reflection uses. +class FlowAnalysisCollector : public VeriFlowAnalysis { + public: + FlowAnalysisCollector(VeridexResolver* resolver, const ClassDataItemIterator& it) + : VeriFlowAnalysis(resolver, it) {} + + const std::vector<ReflectAccessInfo>& GetUses() const { + return uses_; + } + + RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) OVERRIDE; + void AnalyzeFieldSet(const Instruction& instruction) OVERRIDE; + + private: + // List of reflection uses found, concrete and abstract. + std::vector<ReflectAccessInfo> uses_; +}; + +// Substitutes reflection uses by new ones. +class FlowAnalysisSubstitutor : public VeriFlowAnalysis { + public: + FlowAnalysisSubstitutor(VeridexResolver* resolver, + const ClassDataItemIterator& it, + const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses) + : VeriFlowAnalysis(resolver, it), accesses_(accesses) {} - // List of reflection method uses found. - std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_; + const std::vector<ReflectAccessInfo>& GetUses() const { + return uses_; + } + + RegisterValue AnalyzeInvoke(const Instruction& instruction, bool is_range) OVERRIDE; + void AnalyzeFieldSet(const Instruction& instruction) OVERRIDE; + + private: + // List of reflection uses found, concrete and abstract. + std::vector<ReflectAccessInfo> uses_; + // The abstract uses we are trying to subsititute. + const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses_; }; } // namespace art diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc index 4ae5769014..89754c2cdf 100644 --- a/tools/veridex/precise_hidden_api_finder.cc +++ b/tools/veridex/precise_hidden_api_finder.cc @@ -29,7 +29,9 @@ namespace art { -void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) { +void PreciseHiddenApiFinder::RunInternal( + const std::vector<std::unique_ptr<VeridexResolver>>& resolvers, + const std::function<void(VeridexResolver*, const ClassDataItemIterator&)>& action) { for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) { const DexFile& dex_file = resolver->GetDexFile(); size_t class_def_count = dex_file.NumClassDefs(); @@ -47,43 +49,67 @@ void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolv if (code_item == nullptr) { continue; } - CodeItemDataAccessor code_item_accessor(dex_file, code_item); - VeriFlowAnalysis ana(resolver.get(), code_item_accessor); - ana.Run(); - if (!ana.GetFieldUses().empty()) { - field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses(); - } - if (!ana.GetMethodUses().empty()) { - method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses(); - } + action(resolver.get(), it); } } } } +void PreciseHiddenApiFinder::AddUsesAt(const std::vector<ReflectAccessInfo>& accesses, + MethodReference ref) { + for (const ReflectAccessInfo& info : accesses) { + if (info.IsConcrete()) { + concrete_uses_[ref].push_back(info); + } else { + abstract_uses_[ref].push_back(info); + } + } +} + +void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) { + // Collect reflection uses. + RunInternal(resolvers, [this] (VeridexResolver* resolver, const ClassDataItemIterator& it) { + FlowAnalysisCollector collector(resolver, it); + collector.Run(); + AddUsesAt(collector.GetUses(), MethodReference(&resolver->GetDexFile(), it.GetMemberIndex())); + }); + + // For non-final reflection uses, do a limited fixed point calculation over the code to try + // substituting them with final reflection uses. + // We limit the number of times we iterate over the code as one run can be long. + static const int kMaximumIterations = 10; + uint32_t i = 0; + while (!abstract_uses_.empty() && (i++ < kMaximumIterations)) { + // Fetch and clear the worklist. + std::map<MethodReference, std::vector<ReflectAccessInfo>> current_uses + = std::move(abstract_uses_); + RunInternal(resolvers, + [this, current_uses] (VeridexResolver* resolver, const ClassDataItemIterator& it) { + FlowAnalysisSubstitutor substitutor(resolver, it, current_uses); + substitutor.Run(); + AddUsesAt(substitutor.GetUses(), + MethodReference(&resolver->GetDexFile(), it.GetMemberIndex())); + }); + } +} + void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) { static const char* kPrefix = " "; - std::map<std::string, std::vector<MethodReference>> uses; - for (auto kinds : { field_uses_, method_uses_ }) { - for (auto it : kinds) { - MethodReference ref = it.first; - for (const std::pair<RegisterValue, RegisterValue>& info : it.second) { - if ((info.first.GetSource() == RegisterSource::kClass || - info.first.GetSource() == RegisterSource::kString) && - info.second.GetSource() == RegisterSource::kString) { - std::string cls(info.first.ToString()); - std::string name(info.second.ToString()); - std::string full_name = cls + "->" + name; - HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); - if (api_list != HiddenApiAccessFlags::kWhitelist) { - uses[full_name].push_back(ref); - } - } + std::map<std::string, std::vector<MethodReference>> named_uses; + for (auto it : concrete_uses_) { + MethodReference ref = it.first; + for (const ReflectAccessInfo& info : it.second) { + std::string cls(info.cls.ToString()); + std::string name(info.name.ToString()); + std::string full_name = cls + "->" + name; + HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); + if (api_list != HiddenApiAccessFlags::kWhitelist) { + named_uses[full_name].push_back(ref); } } } - for (auto it : uses) { + for (auto it : named_uses) { ++stats->reflection_count; const std::string& full_name = it.first; HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name); diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h index 22744a6f1f..1c4d0ae84e 100644 --- a/tools/veridex/precise_hidden_api_finder.h +++ b/tools/veridex/precise_hidden_api_finder.h @@ -45,9 +45,18 @@ class PreciseHiddenApiFinder { void Dump(std::ostream& os, HiddenApiStats* stats); private: + // Run over all methods of all dex files, and call `action` on each. + void RunInternal( + const std::vector<std::unique_ptr<VeridexResolver>>& resolvers, + const std::function<void(VeridexResolver*, const ClassDataItemIterator&)>& action); + + // Add uses found in method `ref`. + void AddUsesAt(const std::vector<ReflectAccessInfo>& accesses, MethodReference ref); + const HiddenApi& hidden_api_; - std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_; - std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_; + + std::map<MethodReference, std::vector<ReflectAccessInfo>> concrete_uses_; + std::map<MethodReference, std::vector<ReflectAccessInfo>> abstract_uses_; }; } // namespace art diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc index dc7ea94032..bcd4815a38 100644 --- a/tools/veridex/veridex.cc +++ b/tools/veridex/veridex.cc @@ -25,6 +25,7 @@ #include "precise_hidden_api_finder.h" #include "resolver.h" +#include <cstdlib> #include <sstream> namespace art { @@ -62,6 +63,7 @@ VeriMethod VeriClass::getMethod_ = nullptr; VeriMethod VeriClass::getDeclaredMethod_ = nullptr; VeriMethod VeriClass::getClass_ = nullptr; VeriMethod VeriClass::loadClass_ = nullptr; +VeriField VeriClass::sdkInt_ = nullptr; struct VeridexOptions { const char* dex_file = nullptr; @@ -70,6 +72,7 @@ struct VeridexOptions { const char* light_greylist = nullptr; const char* dark_greylist = nullptr; bool precise = true; + int target_sdk_version = 28; /* P */ }; static const char* Substr(const char* str, int index) { @@ -91,6 +94,7 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { static const char* kDarkGreylistOption = "--dark-greylist="; static const char* kLightGreylistOption = "--light-greylist="; static const char* kImprecise = "--imprecise"; + static const char* kTargetSdkVersion = "--target-sdk-version="; for (int i = 0; i < argc; ++i) { if (StartsWith(argv[i], kDexFileOption)) { @@ -105,6 +109,8 @@ static void ParseArgs(VeridexOptions* options, int argc, char** argv) { options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption)); } else if (strcmp(argv[i], kImprecise) == 0) { options->precise = false; + } else if (StartsWith(argv[i], kTargetSdkVersion)) { + options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion))); } } } @@ -124,6 +130,7 @@ class Veridex { static int Run(int argc, char** argv) { VeridexOptions options; ParseArgs(&options, argc, argv); + gTargetSdkVersion = options.target_sdk_version; std::vector<std::string> boot_content; std::vector<std::string> app_content; @@ -200,6 +207,11 @@ class Veridex { VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn( *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + VeriClass* version = type_map["Landroid/os/Build$VERSION;"]; + if (version != nullptr) { + VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I"); + } + std::vector<std::unique_ptr<VeridexResolver>> app_resolvers; Resolve(app_dex_files, resolver_map, type_map, &app_resolvers); diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h index 9c0a158174..31ddbf439e 100644 --- a/tools/veridex/veridex.h +++ b/tools/veridex/veridex.h @@ -24,6 +24,8 @@ namespace art { +static int gTargetSdkVersion = 1000; // Will be initialized after parsing options. + /** * Abstraction for fields defined in dex files. Currently, that's a pointer into their * `encoded_field` description. @@ -86,6 +88,8 @@ class VeriClass { static VeriMethod getClass_; static VeriMethod loadClass_; + static VeriField sdkInt_; + private: Primitive::Type kind_; uint8_t dimensions_; |