Merge "Handle multidex in LocationIsOnSystemFramework."
diff --git a/Android.mk b/Android.mk
index 64b9400..d6472be 100644
--- a/Android.mk
+++ b/Android.mk
@@ -110,22 +110,33 @@
# 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/build/Android.gtest.mk b/build/Android.gtest.mk
index b46f677..3daaf01 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -472,17 +472,32 @@
$$(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 @@
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 @@
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 @@
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/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 231017f..1e44311 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -736,6 +736,26 @@
}
}
+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 f0c4ee0..7e84a44 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -564,6 +564,11 @@
Location runtime_return_location);
void GenerateLoadClassRuntimeCall(HLoadClass* cls);
+ 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 d4cfab8..0601d2d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5144,6 +5144,16 @@
}
}
+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 58ce9aa..33304c6 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -7527,6 +7527,16 @@
}
}
+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 25e2edd..3a3fcff 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -8226,6 +8226,16 @@
}
}
+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 5b07b55..d6fc9a1 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -6262,6 +6262,16 @@
}
}
+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 82d1fda..d18a750 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6539,6 +6539,16 @@
}
}
+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 322b0cf..450c857 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5908,6 +5908,16 @@
}
}
+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 54d4644..87ce1f0 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -386,6 +386,13 @@
<< load_class->NeedsAccessCheck() << std::noboolalpha;
}
+ 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 9647dd5..61730a8 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1896,6 +1896,13 @@
}
}
+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 +2934,13 @@
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 f788292..3fde54c 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -45,6 +45,7 @@
namespace mirror {
class Class;
+class MethodType;
} // namespace mirror
class HInstructionBuilder : public ValueObject {
@@ -239,6 +240,9 @@
bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Builds a `HLoadMethodType` loading the given `proto_index`.
+ 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/nodes.h b/compiler/optimizing/nodes.h
index ae1e606..54882ff 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,7 @@
M(LessThanOrEqual, Condition) \
M(LoadClass, Instruction) \
M(LoadException, Instruction) \
+ M(LoadMethodType, Instruction) \
M(LoadString, Instruction) \
M(LongConstant, Constant) \
M(Max, Instruction) \
@@ -6498,6 +6500,50 @@
special_input->AddUseAt(this, 0);
}
+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_;
+
+ uint16_t proto_idx_;
+ const DexFile& dex_file_;
+};
+
/**
* Performs an initialization check on its Class object input.
*/
@@ -7747,11 +7793,13 @@
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) \
@@ -7766,7 +7814,7 @@
FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
#undef INSTRUCTION_TYPE_CHECK
-#undef INSTRUCTION_TYPE_CHECK_CASE
+#undef INSTRUCTION_TYPE_CHECK_RESULT
#define INSTRUCTION_TYPE_CAST(type, super) \
inline const H##type* HInstruction::As##type() const { \
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index c47c69a..b15a0ea 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -59,6 +59,12 @@
return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_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 +95,7 @@
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;
void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
+ void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE;
void VisitLoadString(HLoadString* instr) OVERRIDE;
void VisitLoadException(HLoadException* instr) OVERRIDE;
void VisitNewArray(HNewArray* instr) OVERRIDE;
@@ -668,6 +675,11 @@
instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
}
+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 400852f..da2193d 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -75,6 +75,7 @@
ReferenceTypeInfo::TypeHandle GetObjectClassHandle();
ReferenceTypeInfo::TypeHandle GetClassClassHandle();
+ ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle();
ReferenceTypeInfo::TypeHandle GetStringClassHandle();
ReferenceTypeInfo::TypeHandle GetThrowableClassHandle();
@@ -83,6 +84,7 @@
ReferenceTypeInfo::TypeHandle object_class_handle_;
ReferenceTypeInfo::TypeHandle class_class_handle_;
+ ReferenceTypeInfo::TypeHandle method_type_class_handle_;
ReferenceTypeInfo::TypeHandle string_class_handle_;
ReferenceTypeInfo::TypeHandle throwable_class_handle_;
};
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 674dc9a..ff3e1ba 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -153,7 +153,7 @@
" 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 c2c8 ldr.w ip, [r9, #712] ; 0x2c8\n",
" 228: 47e0 blx ip\n",
nullptr
};
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 63518be..6b65aca 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1257,10 +1257,10 @@
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/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 1699153..7bd9f0f 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -497,7 +497,7 @@
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(163 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 2425a58..be82fff 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -58,6 +58,7 @@
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/runtime/Android.bp b/runtime/Android.bp
index 116453b..64e6796 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -422,6 +422,7 @@
srcs: [
"arch/instruction_set.h",
"base/mutex.h",
+ "class_loader_context.h",
"class_status.h",
"debugger.h",
"gc_root.h",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a930cc4..1a1f4ed 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1015,7 +1015,10 @@
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,7 @@
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_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/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 9ff5ebe..9919e98 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1533,7 +1533,10 @@
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,7 @@
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_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 9418caf..58b9d48 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -203,6 +203,8 @@
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->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 d8fe480..104a4ca 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_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,12 @@
.endm
/*
+ * 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 8d2a7bd..1e94e07 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_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,12 @@
.endm
/*
+ * 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/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index df43aef..0ae6914 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -923,7 +923,10 @@
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 @@
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,7 @@
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_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 4f941e1..8fef802 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 @@
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,7 @@
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_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/asm_support.h b/runtime/asm_support.h
index 2f7d6ab..705e1ff 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -73,7 +73,7 @@
// 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 + 163) * __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/class_loader_context.cc b/runtime/class_loader_context.cc
index 9817414..2bd5411 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -672,9 +672,10 @@
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 @@
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 @@
<< 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 @@
<< ". 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 @@
<< ". expected=" << expected_info.classpath[k]
<< ", found=" << info.classpath[k]
<< " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
- return false;
+ return VerificationResult::kMismatch;
}
// Compare the checksums.
@@ -781,11 +788,11 @@
<< ". 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 1c83007..a4268aa 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 @@
// 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 @@
// 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 @@
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 4689ae4..5e3f48c 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -608,6 +608,17 @@
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 @@
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 @@
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 @@
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 @@
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/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 246c703..df184bc 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -260,4 +260,19 @@
return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first;
}
+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 eb32153..203ff3d 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -34,6 +34,7 @@
namespace mirror {
class Array;
class Class;
+class MethodType;
class Object;
class String;
} // namespace mirror
@@ -151,6 +152,10 @@
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/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 2d0932a..d934a53 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -37,6 +37,7 @@
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_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 8c90800..a4572f6 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -37,6 +37,7 @@
qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
qpoints->pInitializeType = art_quick_initialize_type;
+ 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 cfb427f..09cbfff 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -183,6 +183,16 @@
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 48a56f2..39dcd39 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -38,6 +38,7 @@
V(InitializeStaticStorage, void*, uint32_t) \
V(InitializeTypeAndVerifyAccess, void*, uint32_t) \
V(InitializeType, 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 1fdf439..b0689f6 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -183,7 +183,8 @@
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, 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/hidden_api.h b/runtime/hidden_api.h
index 65c6406..8e21fd3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -143,31 +143,45 @@
// 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 {
- const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
- return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
}
+
+ if (!caller_dex_cache.IsNull()) {
+ const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+ 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);
@@ -188,8 +202,8 @@
// 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;
}
@@ -197,10 +211,9 @@
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
@@ -212,10 +225,11 @@
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/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 249a8b0..d8aa00c 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -563,7 +563,7 @@
}
}
-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.
@@ -590,7 +590,7 @@
MutexLock mu(Thread::Current(), lock_);
ScopedCodeCacheWrite scc(this);
for (const OatQuickMethodHeader* method_header : method_headers) {
- FreeCode(method_header->GetCode());
+ FreeCodeAndData(method_header->GetCode());
}
}
@@ -916,7 +916,7 @@
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 {
@@ -928,7 +928,7 @@
if (it->second == method) {
in_cache = true;
if (release_memory) {
- FreeCode(it->first);
+ FreeCodeAndData(it->first);
}
it = method_code_map_.erase(it);
} else {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index b10f57e..958e8e8 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -317,8 +317,8 @@
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_);
@@ -357,10 +357,10 @@
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)
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 9dbcded..cd66a60 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -80,15 +80,15 @@
// 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/mirror/class.h b/runtime/mirror/class.h
index 51d1376..98e25eb 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -210,6 +210,15 @@
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/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 5c2ca24..cdba6b2 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -816,6 +816,28 @@
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 @@
"(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 6aaafc2..f1e267b 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -588,6 +588,25 @@
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 @@
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/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 2625c0a..68024cd 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -52,7 +52,7 @@
// 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 @@
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 @@
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 @@
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);
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 0318606..9a58ded 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
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-type
+ static constexpr uint8_t kOatVersion[] = { '1', '4', '2', '\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 9c8b651..241102e 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -1217,7 +1217,9 @@
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 16e6cf3..59a1045 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -276,9 +276,19 @@
}
}
-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 @@
// 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 @@
// 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();
+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;
+}
- // 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);
- 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 @@
<< 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 @@
// 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 038474e..80456e9 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -108,23 +108,39 @@
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/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index bc29aac..5cd6091 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2293,8 +2293,6 @@
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_);
diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check
new file mode 100755
index 0000000..26a97a4
--- /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 0000000..6c3de20
--- /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 0000000..b0aad4d
--- /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 0000000..028046e
--- /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 0000000..70cb2ef
--- /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 0000000..aa28190
--- /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 0000000..a1c151a
--- /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 0000000..5381718
--- /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/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt
index bc943e3..2d169b9 100644
--- a/test/979-const-method-handle/expected.txt
+++ b/test/979-const-method-handle/expected.txt
@@ -1,4 +1,6 @@
(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
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 663814f..79be913 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -26,59 +26,100 @@
throw new Error("Unreachable");
}
+ 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 < 12000; ++i) {
+ MethodType actual = methodType0();
+ if (!actual.equals(expected)) {
+ System.out.print("Expected: ");
+ System.out.println(expected);
+ System.out.print("Actual: ");
+ System.out.println(actual);
+ unreachable();
+ }
+ }
+ }
+
+ private static void repeatConstMethodType1(MethodType expected) {
+ System.out.print("repeatConstMethodType1(");
+ System.out.print(expected);
+ System.out.println(")");
+ for (int i = 0; i < 12000; ++i) {
+ MethodType actual = methodType1();
+ if (!actual.equals(expected)) {
+ System.out.print("Expected: ");
+ System.out.println(expected);
+ System.out.print("Actual: ");
+ System.out.println(actual);
+ unreachable();
+ }
+ }
+ }
+
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 = "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;
@@ -86,6 +127,9 @@
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");
diff --git a/test/Android.bp b/test/Android.bp
index 0c6b449..76189f6 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -423,6 +423,7 @@
"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/test/etc/run-test-jar b/test/etc/run-test-jar
index 81e77be..fad8011 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -13,6 +13,7 @@
ARCHITECTURES_64="(arm64|x86_64|mips64|none)"
ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
BOOT_IMAGE=""
+CHROOT=
COMPILE_FLAGS=""
DALVIKVM="dalvikvm32"
DEBUGGER="n"
@@ -299,6 +300,10 @@
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"
@@ -367,6 +372,9 @@
fi
done
+# The DEX_LOCATION with the chroot prefix, if any.
+CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION"
+
if [ "$USE_JVM" = "n" ]; then
FLAGS="${FLAGS} ${ANDROID_FLAGS}"
for feature in ${EXPERIMENTAL}; do
@@ -817,28 +825,28 @@
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
@@ -847,7 +855,7 @@
# 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
@@ -889,14 +897,18 @@
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 f313758..f473a99 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -734,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 5f85b08..be0a88d 100755
--- a/test/run-test
+++ b/test/run-test
@@ -121,6 +121,8 @@
export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi"
fi
+chroot=
+
info="info.txt"
build="build"
run="run"
@@ -380,6 +382,16 @@
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 @@
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 @@
# 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 [ "$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 @@
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 @@
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 [ "$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 @@
"./${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 @@
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 @@
) 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 @@
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 @@
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 @@
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 7564f5a..0c1c308 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -91,6 +91,8 @@
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 88b509d..254ffc9 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -320,6 +320,9 @@
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 31bddd5..10eb936 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -83,6 +83,10 @@
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 0000000..53072ae
--- /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 56d412b..d376cad 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -68,6 +68,8 @@
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 @@
# 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 @@
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 26b5c0a..3537c1b 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -104,10 +104,14 @@
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 @@
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 @@
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 5ce7f52..f71d973 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 @@
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