Merge "ART: Compile libctstiagent with the NDK"
diff --git a/Android.mk b/Android.mk
index 2489308..558986e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,18 +25,6 @@
include $(art_path)/build/Android.common_path.mk
include $(art_path)/build/Android.oat.mk
-# Following the example of build's dont_bother for clean targets.
-art_dont_bother := false
-ifneq (,$(filter clean-oat%,$(MAKECMDGOALS)))
- art_dont_bother := true
-endif
-
-# Don't bother with tests unless there is a test-art*, build-art*, or related target.
-art_test_bother := false
-ifneq (,$(filter tests test-art% valgrind-test-art% build-art% checkbuild,$(MAKECMDGOALS)))
- art_test_bother := true
-endif
-
.PHONY: clean-oat
clean-oat: clean-oat-host clean-oat-target
@@ -66,8 +54,6 @@
endif
adb shell rm -rf data/run-test/test-*/dalvik-cache/*
-ifneq ($(art_dont_bother),true)
-
########################################################################
# cpplint rules to style check art source files
@@ -104,8 +90,6 @@
########################################################################
# test rules
-ifeq ($(art_test_bother),true)
-
# All the dependencies that must be built ahead of sync-ing them onto the target device.
TEST_ART_TARGET_SYNC_DEPS :=
@@ -349,7 +333,6 @@
valgrind-test-art-target64: valgrind-test-art-target-gtest64
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-endif # art_test_bother
#######################
# Fake packages for ART
@@ -606,11 +589,7 @@
########################################################################
-endif # !art_dont_bother
-
# Clear locally used variables.
-art_dont_bother :=
-art_test_bother :=
TEST_ART_TARGET_SYNC_DEPS :=
# Helper target that depends on boot image creation.
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index df5bb37..59a080f 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -156,14 +156,25 @@
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
if (mini_debug_info) {
- std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa,
- features,
- min_address,
- max_address - min_address,
- /* dex_section_address */ 0,
- /* dex_section_size */ 0,
- debug_info);
- builder->WriteSection(".gnu_debugdata", &mdi);
+ if (method_infos.size() > 1) {
+ std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa,
+ features,
+ min_address,
+ max_address - min_address,
+ /* dex_section_address */ 0,
+ /* dex_section_size */ 0,
+ debug_info);
+ builder->WriteSection(".gnu_debugdata", &mdi);
+ } else {
+ // The compression is great help for multiple methods but it is not worth it for a
+ // single method due to the overheads so skip the compression here for performance.
+ builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
+ WriteDebugSymbols(builder.get(), true /* mini-debug-info */, debug_info);
+ WriteCFISection(builder.get(),
+ debug_info.compiled_methods,
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_paches */);
+ }
} else {
builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
WriteDebugInfo(builder.get(),
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 4b19547..1310e8d 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -37,7 +37,8 @@
// exist, but it will still work well without them.
// However, these extra symbols take space, so let's just generate
// one symbol which marks the whole .text section as code.
-constexpr bool kGenerateSingleArmMappingSymbol = true;
+// Note that ARM's Streamline requires it to match function symbol.
+constexpr bool kGenerateArmMappingSymbol = true;
// Magic name for .symtab symbols which enumerate dex files used
// by this ELF file (currently mmapped inside the .dex section).
@@ -48,6 +49,7 @@
bool mini_debug_info,
const DebugInfo& debug_info) {
uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max();
+ const auto* text = builder->GetText();
auto* strtab = builder->GetStrTab();
auto* symtab = builder->GetSymTab();
@@ -62,10 +64,19 @@
if (info.deduped) {
deduped_addresses.insert(info.code_address);
}
+ if (kGenerateArmMappingSymbol && info.isa == InstructionSet::kThumb2) {
+ uint64_t address = info.code_address;
+ address += info.is_code_address_text_relative ? text->GetAddress() : 0;
+ mapping_symbol_address = std::min(mapping_symbol_address, address);
+ }
}
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
+ // Generate ARM mapping symbols. ELF local symbols must be added first.
+ if (mapping_symbol_address != std::numeric_limits<uint64_t>::max()) {
+ symtab->Add(strtab->Write("$t"), text, mapping_symbol_address, 0, STB_LOCAL, STT_NOTYPE);
+ }
// Add symbols for compiled methods.
for (const MethodDebugInfo& info : debug_info.compiled_methods) {
if (info.deduped) {
@@ -83,23 +94,11 @@
name_offset = strtab->Write(name);
}
- const auto* text = builder->GetText();
uint64_t address = info.code_address;
address += info.is_code_address_text_relative ? text->GetAddress() : 0;
// Add in code delta, e.g., thumb bit 0 for Thumb2 code.
address += CompiledMethod::CodeDelta(info.isa);
symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC);
-
- // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
- // instructions, so that disassembler tools can correctly disassemble.
- // Note that even if we generate just a single mapping symbol, ARM's Streamline
- // requires it to match function symbol. Just address 0 does not work.
- if (info.isa == InstructionSet::kThumb2) {
- if (address < mapping_symbol_address || !kGenerateSingleArmMappingSymbol) {
- symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE);
- mapping_symbol_address = address;
- }
- }
}
// Add symbols for dex files.
if (!debug_info.dex_files.empty() && builder->GetDex()->Exists()) {
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index 3145497..9fb4b18 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -201,7 +201,7 @@
return section_index_ != 0;
}
- private:
+ protected:
// Add this section to the list of generated ELF sections (if not there already).
// It also ensures the alignment is sufficient to generate valid program headers,
// since that depends on the previous section. It returns the required alignment.
@@ -345,7 +345,7 @@
type,
flags,
strtab,
- /* info */ 0,
+ /* info */ 1,
sizeof(Elf_Off),
sizeof(Elf_Sym)) {
syms_.push_back(Elf_Sym()); // The symbol table always has to start with NULL symbol.
@@ -386,6 +386,11 @@
sym.st_shndx = section_index;
sym.st_info = (binding << 4) + (type & 0xf);
syms_.push_back(sym);
+
+ // The sh_info file must be set to index one-past the last local symbol.
+ if (binding == STB_LOCAL) {
+ this->header_.sh_info = syms_.size();
+ }
}
Elf_Word GetCacheSize() { return syms_.size() * sizeof(Elf_Sym); }
@@ -717,8 +722,10 @@
Elf_Word oatdata = dynstr_.Add("oatdata");
dynsym_.Add(oatdata, &rodata_, rodata_.GetAddress(), rodata_size, STB_GLOBAL, STT_OBJECT);
if (text_size != 0u) {
+ // The runtime does not care about the size of this symbol (it uses the "lastword" symbol).
+ // We use size 0 (meaning "unknown size" in ELF) to prevent overlap with the debug symbols.
Elf_Word oatexec = dynstr_.Add("oatexec");
- dynsym_.Add(oatexec, &text_, text_.GetAddress(), text_size, STB_GLOBAL, STT_OBJECT);
+ dynsym_.Add(oatexec, &text_, text_.GetAddress(), /* size */ 0, STB_GLOBAL, STT_OBJECT);
Elf_Word oatlastword = dynstr_.Add("oatlastword");
Elf_Word oatlastword_address = text_.GetAddress() + text_size - 4;
dynsym_.Add(oatlastword, &text_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
@@ -756,7 +763,7 @@
}
if (dex_size != 0u) {
Elf_Word oatdex = dynstr_.Add("oatdex");
- dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), dex_size, STB_GLOBAL, STT_OBJECT);
+ dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), /* size */ 0, STB_GLOBAL, STT_OBJECT);
Elf_Word oatdexlastword = dynstr_.Add("oatdexlastword");
Elf_Word oatdexlastword_address = dex_.GetAddress() + dex_size - 4;
dynsym_.Add(oatdexlastword, &dex_, oatdexlastword_address, 4, STB_GLOBAL, STT_OBJECT);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 91e475d..f6ba19f 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1121,10 +1121,6 @@
user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node);
}
-HInstruction::InstructionKind HInstruction::GetKind() const {
- return GetKindInternal();
-}
-
HInstruction* HInstruction::GetNextDisregardingMoves() const {
HInstruction* next = GetNext();
while (next != nullptr && next->IsParallelMove()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f91d37b..0534685 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1518,7 +1518,6 @@
private: \
H##type& operator=(const H##type&) = delete; \
public: \
- InstructionKind GetKindInternal() const OVERRIDE { return k##type; } \
const char* DebugName() const OVERRIDE { return #type; } \
bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \
return other->Is##type(); \
@@ -1952,7 +1951,14 @@
class HInstruction : public ArenaObject<kArenaAllocInstruction> {
public:
- HInstruction(SideEffects side_effects, uint32_t dex_pc)
+#define DECLARE_KIND(type, super) k##type,
+ enum InstructionKind {
+ FOR_EACH_INSTRUCTION(DECLARE_KIND)
+ kLastInstructionKind
+ };
+#undef DECLARE_KIND
+
+ HInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
: previous_(nullptr),
next_(nullptr),
block_(nullptr),
@@ -1966,16 +1972,12 @@
lifetime_position_(kNoLifetime),
side_effects_(side_effects),
reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) {
+ SetPackedField<InstructionKindField>(kind);
SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact());
}
virtual ~HInstruction() {}
-#define DECLARE_KIND(type, super) k##type,
- enum InstructionKind {
- FOR_EACH_INSTRUCTION(DECLARE_KIND)
- };
-#undef DECLARE_KIND
HInstruction* GetNext() const { return next_; }
HInstruction* GetPrevious() const { return previous_; }
@@ -2279,8 +2281,7 @@
// is adopted and implemented by our C++ compiler(s). Fow now, we need to hide
// the virtual function because the __attribute__((__pure__)) doesn't really
// apply the strong requirement for virtual functions, preventing optimizations.
- InstructionKind GetKind() const PURE;
- virtual InstructionKind GetKindInternal() const = 0;
+ InstructionKind GetKind() const { return GetPackedField<InstructionKindField>(); }
virtual size_t ComputeHashCode() const {
size_t result = GetKind();
@@ -2332,9 +2333,16 @@
// its users. Used by liveness analysis to compute use positions accordingly.
static constexpr size_t kFlagEmittedAtUseSite = 0u;
static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1;
- static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1;
+ static constexpr size_t kFieldInstructionKind = kFlagReferenceTypeIsExact + 1;
+ static constexpr size_t kFieldInstructionKindSize =
+ MinimumBitsToStore(static_cast<size_t>(InstructionKind::kLastInstructionKind - 1));
+ static constexpr size_t kNumberOfGenericPackedBits =
+ kFieldInstructionKind + kFieldInstructionKindSize;
static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
+ static_assert(kNumberOfGenericPackedBits <= kMaxNumberOfPackedBits,
+ "Too many generic packed fields");
+
const HUserRecord<HInstruction*> InputRecordAt(size_t i) const {
return GetInputRecords()[i];
}
@@ -2391,9 +2399,13 @@
live_interval_(nullptr),
lifetime_position_(kNoLifetime),
side_effects_(other.side_effects_),
- reference_type_handle_(other.reference_type_handle_) {}
+ reference_type_handle_(other.reference_type_handle_) {
+ }
private:
+ using InstructionKindField =
+ BitField<InstructionKind, kFieldInstructionKind, kFieldInstructionKindSize>;
+
void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) {
auto before_use_node = uses_.before_begin();
for (auto use_node = uses_.begin(); use_node != fixup_end; ++use_node) {
@@ -2569,12 +2581,13 @@
void RemoveAllInputs();
protected:
- HVariableInputSizeInstruction(SideEffects side_effects,
+ HVariableInputSizeInstruction(InstructionKind inst_kind,
+ SideEffects side_effects,
uint32_t dex_pc,
ArenaAllocator* allocator,
size_t number_of_inputs,
ArenaAllocKind kind)
- : HInstruction(side_effects, dex_pc),
+ : HInstruction(inst_kind, side_effects, dex_pc),
inputs_(number_of_inputs, allocator->Adapter(kind)) {}
DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction);
@@ -2585,8 +2598,8 @@
template<size_t N>
class HTemplateInstruction: public HInstruction {
public:
- HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc)
- : HInstruction(side_effects, dex_pc), inputs_() {}
+ HTemplateInstruction<N>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
+ : HInstruction(kind, side_effects, dex_pc), inputs_() {}
virtual ~HTemplateInstruction() {}
using HInstruction::GetInputRecords; // Keep the const version visible.
@@ -2607,8 +2620,8 @@
template<>
class HTemplateInstruction<0>: public HInstruction {
public:
- explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc)
- : HInstruction(side_effects, dex_pc) {}
+ explicit HTemplateInstruction<0>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
+ : HInstruction(kind, side_effects, dex_pc) {}
virtual ~HTemplateInstruction() {}
@@ -2627,8 +2640,12 @@
template<intptr_t N>
class HExpression : public HTemplateInstruction<N> {
public:
- HExpression<N>(DataType::Type type, SideEffects side_effects, uint32_t dex_pc)
- : HTemplateInstruction<N>(side_effects, dex_pc) {
+ using HInstruction::InstructionKind;
+ HExpression<N>(InstructionKind kind,
+ DataType::Type type,
+ SideEffects side_effects,
+ uint32_t dex_pc)
+ : HTemplateInstruction<N>(kind, side_effects, dex_pc) {
this->template SetPackedField<TypeField>(type);
}
virtual ~HExpression() {}
@@ -2653,7 +2670,8 @@
class HReturnVoid FINAL : public HTemplateInstruction<0> {
public:
explicit HReturnVoid(uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+ : HTemplateInstruction(kReturnVoid, SideEffects::None(), dex_pc) {
+ }
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2668,7 +2686,7 @@
class HReturn FINAL : public HTemplateInstruction<1> {
public:
explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc) {
+ : HTemplateInstruction(kReturn, SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -2688,6 +2706,7 @@
DataType::Type type,
uint32_t dex_pc = kNoDexPc)
: HVariableInputSizeInstruction(
+ kPhi,
SideEffects::None(),
dex_pc,
allocator,
@@ -2788,7 +2807,9 @@
// exit block.
class HExit FINAL : public HTemplateInstruction<0> {
public:
- explicit HExit(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+ explicit HExit(uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(kExit, SideEffects::None(), dex_pc) {
+ }
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2801,7 +2822,9 @@
// Jumps from one block to another.
class HGoto FINAL : public HTemplateInstruction<0> {
public:
- explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+ explicit HGoto(uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(kGoto, SideEffects::None(), dex_pc) {
+ }
bool IsClonable() const OVERRIDE { return true; }
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2818,8 +2841,9 @@
class HConstant : public HExpression<0> {
public:
- explicit HConstant(DataType::Type type, uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc) {}
+ explicit HConstant(InstructionKind kind, DataType::Type type, uint32_t dex_pc = kNoDexPc)
+ : HExpression(kind, type, SideEffects::None(), dex_pc) {
+ }
bool CanBeMoved() const OVERRIDE { return true; }
@@ -2860,7 +2884,8 @@
private:
explicit HNullConstant(uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kReference, dex_pc) {}
+ : HConstant(kNullConstant, DataType::Type::kReference, dex_pc) {
+ }
friend class HGraph;
};
@@ -2899,9 +2924,12 @@
private:
explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kInt32, dex_pc), value_(value) {}
+ : HConstant(kIntConstant, DataType::Type::kInt32, dex_pc), value_(value) {
+ }
explicit HIntConstant(bool value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kInt32, dex_pc), value_(value ? 1 : 0) {}
+ : HConstant(kIntConstant, DataType::Type::kInt32, dex_pc),
+ value_(value ? 1 : 0) {
+ }
const int32_t value_;
@@ -2935,7 +2963,9 @@
private:
explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kInt64, dex_pc), value_(value) {}
+ : HConstant(kLongConstant, DataType::Type::kInt64, dex_pc),
+ value_(value) {
+ }
const int64_t value_;
@@ -2986,9 +3016,13 @@
private:
explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kFloat32, dex_pc), value_(value) {}
+ : HConstant(kFloatConstant, DataType::Type::kFloat32, dex_pc),
+ value_(value) {
+ }
explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kFloat32, dex_pc), value_(bit_cast<float, int32_t>(value)) {}
+ : HConstant(kFloatConstant, DataType::Type::kFloat32, dex_pc),
+ value_(bit_cast<float, int32_t>(value)) {
+ }
const float value_;
@@ -3039,9 +3073,13 @@
private:
explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kFloat64, dex_pc), value_(value) {}
+ : HConstant(kDoubleConstant, DataType::Type::kFloat64, dex_pc),
+ value_(value) {
+ }
explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
- : HConstant(DataType::Type::kFloat64, dex_pc), value_(bit_cast<double, int64_t>(value)) {}
+ : HConstant(kDoubleConstant, DataType::Type::kFloat64, dex_pc),
+ value_(bit_cast<double, int64_t>(value)) {
+ }
const double value_;
@@ -3055,7 +3093,7 @@
class HIf FINAL : public HTemplateInstruction<1> {
public:
explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc) {
+ : HTemplateInstruction(kIf, SideEffects::None(), dex_pc) {
SetRawInputAt(0, input);
}
@@ -3091,7 +3129,7 @@
};
explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc) {
+ : HTemplateInstruction(kTryBoundary, SideEffects::None(), dex_pc) {
SetPackedField<BoundaryKindField>(kind);
}
@@ -3150,6 +3188,7 @@
DeoptimizationKind kind,
uint32_t dex_pc)
: HVariableInputSizeInstruction(
+ kDeoptimize,
SideEffects::All(),
dex_pc,
allocator,
@@ -3173,6 +3212,7 @@
DeoptimizationKind kind,
uint32_t dex_pc)
: HVariableInputSizeInstruction(
+ kDeoptimize,
SideEffects::CanTriggerGC(),
dex_pc,
allocator,
@@ -3241,7 +3281,12 @@
// CHA guards are only optimized in a separate pass and it has no side effects
// with regard to other passes.
HShouldDeoptimizeFlag(ArenaAllocator* allocator, uint32_t dex_pc)
- : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, allocator, 0, kArenaAllocCHA) {
+ : HVariableInputSizeInstruction(kShouldDeoptimizeFlag,
+ SideEffects::None(),
+ dex_pc,
+ allocator,
+ 0,
+ kArenaAllocCHA) {
}
DataType::Type GetType() const OVERRIDE { return DataType::Type::kInt32; }
@@ -3264,7 +3309,8 @@
class HCurrentMethod FINAL : public HExpression<0> {
public:
explicit HCurrentMethod(DataType::Type type, uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc) {}
+ : HExpression(kCurrentMethod, type, SideEffects::None(), dex_pc) {
+ }
DECLARE_INSTRUCTION(CurrentMethod);
@@ -3286,7 +3332,7 @@
TableKind kind,
size_t index,
uint32_t dex_pc)
- : HExpression(type, SideEffects::None(), dex_pc),
+ : HExpression(kClassTableGet, type, SideEffects::None(), dex_pc),
index_(index) {
SetPackedField<TableKindField>(kind);
SetRawInputAt(0, cls);
@@ -3329,7 +3375,7 @@
uint32_t num_entries,
HInstruction* input,
uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc),
+ : HTemplateInstruction(kPackedSwitch, SideEffects::None(), dex_pc),
start_value_(start_value),
num_entries_(num_entries) {
SetRawInputAt(0, input);
@@ -3359,8 +3405,11 @@
class HUnaryOperation : public HExpression<1> {
public:
- HUnaryOperation(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HExpression(result_type, SideEffects::None(), dex_pc) {
+ HUnaryOperation(InstructionKind kind,
+ DataType::Type result_type,
+ HInstruction* input,
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(kind, result_type, SideEffects::None(), dex_pc) {
SetRawInputAt(0, input);
}
@@ -3394,12 +3443,13 @@
class HBinaryOperation : public HExpression<2> {
public:
- HBinaryOperation(DataType::Type result_type,
+ HBinaryOperation(InstructionKind kind,
+ DataType::Type result_type,
HInstruction* left,
HInstruction* right,
SideEffects side_effects = SideEffects::None(),
uint32_t dex_pc = kNoDexPc)
- : HExpression(result_type, side_effects, dex_pc) {
+ : HExpression(kind, result_type, side_effects, dex_pc) {
SetRawInputAt(0, left);
SetRawInputAt(1, right);
}
@@ -3498,8 +3548,16 @@
class HCondition : public HBinaryOperation {
public:
- HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(DataType::Type::kBool, first, second, SideEffects::None(), dex_pc) {
+ HCondition(InstructionKind kind,
+ HInstruction* first,
+ HInstruction* second,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(kind,
+ DataType::Type::kBool,
+ first,
+ second,
+ SideEffects::None(),
+ dex_pc) {
SetPackedField<ComparisonBiasField>(ComparisonBias::kNoBias);
}
@@ -3579,7 +3637,8 @@
class HEqual FINAL : public HCondition {
public:
HEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kEqual, first, second, dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -3623,8 +3682,10 @@
class HNotEqual FINAL : public HCondition {
public:
- HNotEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ HNotEqual(HInstruction* first, HInstruction* second,
+ uint32_t dex_pc = kNoDexPc)
+ : HCondition(kNotEqual, first, second, dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -3667,8 +3728,10 @@
class HLessThan FINAL : public HCondition {
public:
- HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ HLessThan(HInstruction* first, HInstruction* second,
+ uint32_t dex_pc = kNoDexPc)
+ : HCondition(kLessThan, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3705,8 +3768,10 @@
class HLessThanOrEqual FINAL : public HCondition {
public:
- HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ HLessThanOrEqual(HInstruction* first, HInstruction* second,
+ uint32_t dex_pc = kNoDexPc)
+ : HCondition(kLessThanOrEqual, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3744,7 +3809,8 @@
class HGreaterThan FINAL : public HCondition {
public:
HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kGreaterThan, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3782,7 +3848,8 @@
class HGreaterThanOrEqual FINAL : public HCondition {
public:
HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kGreaterThanOrEqual, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3820,7 +3887,8 @@
class HBelow FINAL : public HCondition {
public:
HBelow(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kBelow, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3861,7 +3929,8 @@
class HBelowOrEqual FINAL : public HCondition {
public:
HBelowOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kBelowOrEqual, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3902,7 +3971,8 @@
class HAbove FINAL : public HCondition {
public:
HAbove(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kAbove, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3943,7 +4013,8 @@
class HAboveOrEqual FINAL : public HCondition {
public:
HAboveOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HCondition(first, second, dex_pc) {}
+ : HCondition(kAboveOrEqual, first, second, dex_pc) {
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3993,7 +4064,8 @@
HInstruction* second,
ComparisonBias bias,
uint32_t dex_pc)
- : HBinaryOperation(DataType::Type::kInt32,
+ : HBinaryOperation(kCompare,
+ DataType::Type::kInt32,
first,
second,
SideEffectsForArchRuntimeCalls(comparison_type),
@@ -4079,7 +4151,10 @@
const DexFile& dex_file,
bool finalizable,
QuickEntrypointEnum entrypoint)
- : HExpression(DataType::Type::kReference, SideEffects::CanTriggerGC(), dex_pc),
+ : HExpression(kNewInstance,
+ DataType::Type::kReference,
+ SideEffects::CanTriggerGC(),
+ dex_pc),
type_index_(type_index),
dex_file_(dex_file),
entrypoint_(entrypoint) {
@@ -4235,7 +4310,8 @@
using InvokeTypeField = BitField<InvokeType, kFieldInvokeType, kFieldInvokeTypeSize>;
using ReturnTypeField = BitField<DataType::Type, kFieldReturnType, kFieldReturnTypeSize>;
- HInvoke(ArenaAllocator* allocator,
+ HInvoke(InstructionKind kind,
+ ArenaAllocator* allocator,
uint32_t number_of_arguments,
uint32_t number_of_other_inputs,
DataType::Type return_type,
@@ -4244,6 +4320,7 @@
ArtMethod* resolved_method,
InvokeType invoke_type)
: HVariableInputSizeInstruction(
+ kind,
SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
dex_pc,
allocator,
@@ -4278,7 +4355,8 @@
uint32_t dex_pc,
uint32_t dex_method_index,
InvokeType invoke_type)
- : HInvoke(allocator,
+ : HInvoke(kInvokeUnresolved,
+ allocator,
number_of_arguments,
0u /* number_of_other_inputs */,
return_type,
@@ -4303,14 +4381,16 @@
DataType::Type return_type,
uint32_t dex_pc,
uint32_t dex_method_index)
- : HInvoke(allocator,
+ : HInvoke(kInvokePolymorphic,
+ allocator,
number_of_arguments,
0u /* number_of_other_inputs */,
return_type,
dex_pc,
dex_method_index,
nullptr,
- kVirtual) {}
+ kVirtual) {
+ }
bool IsClonable() const OVERRIDE { return true; }
@@ -4387,7 +4467,8 @@
InvokeType invoke_type,
MethodReference target_method,
ClinitCheckRequirement clinit_check_requirement)
- : HInvoke(allocator,
+ : HInvoke(kInvokeStaticOrDirect,
+ allocator,
number_of_arguments,
// There is potentially one extra argument for the HCurrentMethod node, and
// potentially one other if the clinit check is explicit, and potentially
@@ -4582,7 +4663,8 @@
uint32_t dex_method_index,
ArtMethod* resolved_method,
uint32_t vtable_index)
- : HInvoke(allocator,
+ : HInvoke(kInvokeVirtual,
+ allocator,
number_of_arguments,
0u,
return_type,
@@ -4590,7 +4672,8 @@
dex_method_index,
resolved_method,
kVirtual),
- vtable_index_(vtable_index) {}
+ vtable_index_(vtable_index) {
+ }
bool IsClonable() const OVERRIDE { return true; }
@@ -4633,7 +4716,8 @@
uint32_t dex_method_index,
ArtMethod* resolved_method,
uint32_t imt_index)
- : HInvoke(allocator,
+ : HInvoke(kInvokeInterface,
+ allocator,
number_of_arguments,
0u,
return_type,
@@ -4641,7 +4725,8 @@
dex_method_index,
resolved_method,
kInterface),
- imt_index_(imt_index) {}
+ imt_index_(imt_index) {
+ }
bool IsClonable() const OVERRIDE { return true; }
@@ -4670,7 +4755,7 @@
class HNeg FINAL : public HUnaryOperation {
public:
HNeg(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HUnaryOperation(result_type, input, dex_pc) {
+ : HUnaryOperation(kNeg, result_type, input, dex_pc) {
DCHECK_EQ(result_type, DataType::Kind(input->GetType()));
}
@@ -4698,7 +4783,7 @@
class HNewArray FINAL : public HExpression<2> {
public:
HNewArray(HInstruction* cls, HInstruction* length, uint32_t dex_pc)
- : HExpression(DataType::Type::kReference, SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kNewArray, DataType::Type::kReference, SideEffects::CanTriggerGC(), dex_pc) {
SetRawInputAt(0, cls);
SetRawInputAt(1, length);
}
@@ -4734,7 +4819,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kAdd, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -4769,7 +4855,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kSub, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
template <typename T> static T Compute(T x, T y) { return x - y; }
@@ -4802,7 +4889,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kMul, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -4837,7 +4925,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kDiv, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
template <typename T>
T ComputeIntegral(T x, T y) const {
@@ -4884,7 +4973,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kRem, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
template <typename T>
T ComputeIntegral(T x, T y) const {
@@ -4930,7 +5020,7 @@
// `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException`
// constructor.
HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kDivZeroCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -4957,7 +5047,7 @@
HInstruction* value,
HInstruction* distance,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+ : HBinaryOperation(kShl, result_type, value, distance, SideEffects::None(), dex_pc) {
DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
}
@@ -5003,7 +5093,7 @@
HInstruction* value,
HInstruction* distance,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+ : HBinaryOperation(kShr, result_type, value, distance, SideEffects::None(), dex_pc) {
DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
}
@@ -5049,7 +5139,7 @@
HInstruction* value,
HInstruction* distance,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+ : HBinaryOperation(kUShr, result_type, value, distance, SideEffects::None(), dex_pc) {
DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
}
@@ -5097,7 +5187,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kAnd, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -5134,7 +5225,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kOr, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -5171,7 +5263,8 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+ : HBinaryOperation(kXor, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
bool IsCommutative() const OVERRIDE { return true; }
@@ -5205,7 +5298,7 @@
class HRor FINAL : public HBinaryOperation {
public:
HRor(DataType::Type result_type, HInstruction* value, HInstruction* distance)
- : HBinaryOperation(result_type, value, distance) {
+ : HBinaryOperation(kRor, result_type, value, distance) {
DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
}
@@ -5262,7 +5355,7 @@
uint8_t index,
DataType::Type parameter_type,
bool is_this = false)
- : HExpression(parameter_type, SideEffects::None(), kNoDexPc),
+ : HExpression(kParameterValue, parameter_type, SideEffects::None(), kNoDexPc),
dex_file_(dex_file),
type_index_(type_index),
index_(index) {
@@ -5301,7 +5394,8 @@
class HNot FINAL : public HUnaryOperation {
public:
HNot(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HUnaryOperation(result_type, input, dex_pc) {}
+ : HUnaryOperation(kNot, result_type, input, dex_pc) {
+ }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -5334,7 +5428,8 @@
class HBooleanNot FINAL : public HUnaryOperation {
public:
explicit HBooleanNot(HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HUnaryOperation(DataType::Type::kBool, input, dex_pc) {}
+ : HUnaryOperation(kBooleanNot, DataType::Type::kBool, input, dex_pc) {
+ }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -5372,7 +5467,7 @@
public:
// Instantiate a type conversion of `input` to `result_type`.
HTypeConversion(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HExpression(result_type, SideEffects::None(), dex_pc) {
+ : HExpression(kTypeConversion, result_type, SideEffects::None(), dex_pc) {
SetRawInputAt(0, input);
// Invariant: We should never generate a conversion to a Boolean value.
DCHECK_NE(DataType::Type::kBool, result_type);
@@ -5404,7 +5499,7 @@
// `HNullCheck` can trigger GC, as it may call the `NullPointerException`
// constructor.
HNullCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kNullCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -5474,7 +5569,10 @@
uint16_t declaring_class_def_index,
const DexFile& dex_file,
uint32_t dex_pc)
- : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
+ : HExpression(kInstanceFieldGet,
+ field_type,
+ SideEffects::FieldReadOfType(field_type, is_volatile),
+ dex_pc),
field_info_(field,
field_offset,
field_type,
@@ -5534,7 +5632,9 @@
uint16_t declaring_class_def_index,
const DexFile& dex_file,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
+ : HTemplateInstruction(kInstanceFieldSet,
+ SideEffects::FieldWriteOfType(field_type, is_volatile),
+ dex_pc),
field_info_(field,
field_offset,
field_type,
@@ -5586,7 +5686,8 @@
type,
SideEffects::ArrayReadOfType(type),
dex_pc,
- /* is_string_char_at */ false) {}
+ /* is_string_char_at */ false) {
+ }
HArrayGet(HInstruction* array,
HInstruction* index,
@@ -5594,7 +5695,7 @@
SideEffects side_effects,
uint32_t dex_pc,
bool is_string_char_at)
- : HExpression(type, side_effects, dex_pc) {
+ : HExpression(kArrayGet, type, side_effects, dex_pc) {
SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
@@ -5678,7 +5779,8 @@
expected_component_type,
// Make a best guess for side effects now, may be refined during SSA building.
ComputeSideEffects(GetComponentType(value->GetType(), expected_component_type)),
- dex_pc) {}
+ dex_pc) {
+ }
HArraySet(HInstruction* array,
HInstruction* index,
@@ -5686,7 +5788,7 @@
DataType::Type expected_component_type,
SideEffects side_effects,
uint32_t dex_pc)
- : HTemplateInstruction(side_effects, dex_pc) {
+ : HTemplateInstruction(kArraySet, side_effects, dex_pc) {
SetPackedField<ExpectedComponentTypeField>(expected_component_type);
SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == DataType::Type::kReference);
SetPackedFlag<kFlagValueCanBeNull>(true);
@@ -5786,7 +5888,7 @@
class HArrayLength FINAL : public HExpression<1> {
public:
HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false)
- : HExpression(DataType::Type::kInt32, SideEffects::None(), dex_pc) {
+ : HExpression(kArrayLength, DataType::Type::kInt32, SideEffects::None(), dex_pc) {
SetPackedFlag<kFlagIsStringLength>(is_string_length);
// Note that arrays do not change length, so the instruction does not
// depend on any write.
@@ -5829,7 +5931,7 @@
HInstruction* length,
uint32_t dex_pc,
bool is_string_char_at = false)
- : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kBoundsCheck, index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType()));
SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, index);
@@ -5862,7 +5964,9 @@
class HSuspendCheck FINAL : public HTemplateInstruction<0> {
public:
explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
+ : HTemplateInstruction(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc),
+ slow_path_(nullptr) {
+ }
bool IsClonable() const OVERRIDE { return true; }
@@ -5889,7 +5993,8 @@
class HNativeDebugInfo : public HTemplateInstruction<0> {
public:
explicit HNativeDebugInfo(uint32_t dex_pc)
- : HTemplateInstruction<0>(SideEffects::None(), dex_pc) {}
+ : HTemplateInstruction<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) {
+ }
bool NeedsEnvironment() const OVERRIDE {
return true;
@@ -5947,7 +6052,7 @@
bool is_referrers_class,
uint32_t dex_pc,
bool needs_access_check)
- : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
+ : HInstruction(kLoadClass, SideEffectsForArchRuntimeCalls(), dex_pc),
special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
dex_file_(dex_file),
@@ -6163,7 +6268,7 @@
dex::StringIndex string_index,
const DexFile& dex_file,
uint32_t dex_pc)
- : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
+ : HInstruction(kLoadString, SideEffectsForArchRuntimeCalls(), dex_pc),
special_input_(HUserRecord<HInstruction*>(current_method)),
string_index_(string_index),
dex_file_(dex_file) {
@@ -6300,6 +6405,7 @@
public:
HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
: HExpression(
+ kClinitCheck,
DataType::Type::kReference,
SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
dex_pc) {
@@ -6342,7 +6448,10 @@
uint16_t declaring_class_def_index,
const DexFile& dex_file,
uint32_t dex_pc)
- : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
+ : HExpression(kStaticFieldGet,
+ field_type,
+ SideEffects::FieldReadOfType(field_type, is_volatile),
+ dex_pc),
field_info_(field,
field_offset,
field_type,
@@ -6399,7 +6508,9 @@
uint16_t declaring_class_def_index,
const DexFile& dex_file,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
+ : HTemplateInstruction(kStaticFieldSet,
+ SideEffects::FieldWriteOfType(field_type, is_volatile),
+ dex_pc),
field_info_(field,
field_offset,
field_type,
@@ -6442,7 +6553,10 @@
DataType::Type field_type,
uint32_t field_index,
uint32_t dex_pc)
- : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+ : HExpression(kUnresolvedInstanceFieldGet,
+ field_type,
+ SideEffects::AllExceptGCDependency(),
+ dex_pc),
field_index_(field_index) {
SetRawInputAt(0, obj);
}
@@ -6470,7 +6584,9 @@
DataType::Type field_type,
uint32_t field_index,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+ : HTemplateInstruction(kUnresolvedInstanceFieldSet,
+ SideEffects::AllExceptGCDependency(),
+ dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType()));
@@ -6508,7 +6624,10 @@
HUnresolvedStaticFieldGet(DataType::Type field_type,
uint32_t field_index,
uint32_t dex_pc)
- : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+ : HExpression(kUnresolvedStaticFieldGet,
+ field_type,
+ SideEffects::AllExceptGCDependency(),
+ dex_pc),
field_index_(field_index) {
}
@@ -6534,7 +6653,9 @@
DataType::Type field_type,
uint32_t field_index,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+ : HTemplateInstruction(kUnresolvedStaticFieldSet,
+ SideEffects::AllExceptGCDependency(),
+ dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType()));
@@ -6570,7 +6691,8 @@
class HLoadException FINAL : public HExpression<0> {
public:
explicit HLoadException(uint32_t dex_pc = kNoDexPc)
- : HExpression(DataType::Type::kReference, SideEffects::None(), dex_pc) {}
+ : HExpression(kLoadException, DataType::Type::kReference, SideEffects::None(), dex_pc) {
+ }
bool CanBeNull() const OVERRIDE { return false; }
@@ -6585,7 +6707,8 @@
class HClearException FINAL : public HTemplateInstruction<0> {
public:
explicit HClearException(uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::AllWrites(), dex_pc) {}
+ : HTemplateInstruction(kClearException, SideEffects::AllWrites(), dex_pc) {
+ }
DECLARE_INSTRUCTION(ClearException);
@@ -6596,7 +6719,7 @@
class HThrow FINAL : public HTemplateInstruction<1> {
public:
HThrow(HInstruction* exception, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+ : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) {
SetRawInputAt(0, exception);
}
@@ -6637,7 +6760,8 @@
HLoadClass* target_class,
TypeCheckKind check_kind,
uint32_t dex_pc)
- : HExpression(DataType::Type::kBool,
+ : HExpression(kInstanceOf,
+ DataType::Type::kBool,
SideEffectsForArchRuntimeCalls(check_kind),
dex_pc) {
SetPackedField<TypeCheckKindField>(check_kind);
@@ -6696,7 +6820,7 @@
class HBoundType FINAL : public HExpression<1> {
public:
explicit HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HExpression(DataType::Type::kReference, SideEffects::None(), dex_pc),
+ : HExpression(kBoundType, DataType::Type::kReference, SideEffects::None(), dex_pc),
upper_bound_(ReferenceTypeInfo::CreateInvalid()) {
SetPackedFlag<kFlagUpperCanBeNull>(true);
SetPackedFlag<kFlagCanBeNull>(true);
@@ -6746,7 +6870,7 @@
HLoadClass* target_class,
TypeCheckKind check_kind,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+ : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) {
SetPackedField<TypeCheckKindField>(check_kind);
SetPackedFlag<kFlagMustDoNullCheck>(true);
SetRawInputAt(0, object);
@@ -6823,7 +6947,9 @@
public:
explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(
- SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays.
+ kMemoryBarrier,
+ SideEffects::AllWritesAndReads(), // Assume write/read on all fields/arrays.
+ dex_pc) {
SetPackedField<BarrierKindField>(barrier_kind);
}
@@ -6942,7 +7068,8 @@
//
// If in a later phase we discover that there are no writes to reference final fields,
// we can refine the side effect to a smaller set of type reads (see above constraints).
- : HVariableInputSizeInstruction(SideEffects::AllReads(),
+ : HVariableInputSizeInstruction(kConstructorFence,
+ SideEffects::AllReads(),
dex_pc,
allocator,
/* number_of_inputs */ 1,
@@ -7009,6 +7136,7 @@
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
: HTemplateInstruction(
+ kMonitorOperation,
SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
dex_pc) {
SetPackedField<OperationKindField>(kind);
@@ -7050,7 +7178,7 @@
HInstruction* true_value,
HInstruction* false_value,
uint32_t dex_pc)
- : HExpression(HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) {
+ : HExpression(kSelect, HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) {
DCHECK_EQ(HPhi::ToPhiType(true_value->GetType()), HPhi::ToPhiType(false_value->GetType()));
// First input must be `true_value` or `false_value` to allow codegens to
@@ -7163,7 +7291,7 @@
class HParallelMove FINAL : public HTemplateInstruction<0> {
public:
explicit HParallelMove(ArenaAllocator* allocator, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc),
+ : HTemplateInstruction(kParallelMove, SideEffects::None(), dex_pc),
moves_(allocator->Adapter(kArenaAllocMoveOperands)) {
moves_.reserve(kDefaultNumberOfMoves);
}
@@ -7225,7 +7353,10 @@
class HIntermediateAddress FINAL : public HExpression<2> {
public:
HIntermediateAddress(HInstruction* base_address, HInstruction* offset, uint32_t dex_pc)
- : HExpression(DataType::Type::kInt32, SideEffects::DependsOnGC(), dex_pc) {
+ : HExpression(kIntermediateAddress,
+ DataType::Type::kInt32,
+ SideEffects::DependsOnGC(),
+ dex_pc) {
DCHECK_EQ(DataType::Size(DataType::Type::kInt32),
DataType::Size(DataType::Type::kReference))
<< "kPrimInt and kPrimNot have different sizes.";
diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h
index 2c0595e..d0e0fef 100644
--- a/compiler/optimizing/nodes_mips.h
+++ b/compiler/optimizing/nodes_mips.h
@@ -24,7 +24,11 @@
public:
// Treat the value as an int32_t, but it is really a 32 bit native pointer.
HMipsComputeBaseMethodAddress()
- : HExpression(DataType::Type::kInt32, SideEffects::None(), kNoDexPc) {}
+ : HExpression(kMipsComputeBaseMethodAddress,
+ DataType::Type::kInt32,
+ SideEffects::None(),
+ kNoDexPc) {
+ }
bool CanBeMoved() const OVERRIDE { return true; }
@@ -42,7 +46,7 @@
HInstruction* input,
HMipsComputeBaseMethodAddress* method_base,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None(), dex_pc),
+ : HTemplateInstruction(kMipsPackedSwitch, SideEffects::None(), dex_pc),
start_value_(start_value),
num_entries_(num_entries) {
SetRawInputAt(0, input);
@@ -90,7 +94,10 @@
class HIntermediateArrayAddressIndex FINAL : public HExpression<2> {
public:
HIntermediateArrayAddressIndex(HInstruction* index, HInstruction* shift, uint32_t dex_pc)
- : HExpression(DataType::Type::kInt32, SideEffects::None(), dex_pc) {
+ : HExpression(kIntermediateArrayAddressIndex,
+ DataType::Type::kInt32,
+ SideEffects::None(),
+ dex_pc) {
SetRawInputAt(0, index);
SetRawInputAt(1, shift);
}
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index e837f1e..29358e1 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -32,7 +32,8 @@
HInstruction* mul_left,
HInstruction* mul_right,
uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) {
+ : HExpression(kMultiplyAccumulate, type, SideEffects::None(), dex_pc),
+ op_kind_(op) {
SetRawInputAt(kInputAccumulatorIndex, accumulator);
SetRawInputAt(kInputMulLeftIndex, mul_left);
SetRawInputAt(kInputMulRightIndex, mul_right);
@@ -68,7 +69,12 @@
HInstruction* left,
HInstruction* right,
uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc),
+ : HBinaryOperation(kBitwiseNegatedRight,
+ result_type,
+ left,
+ right,
+ SideEffects::None(),
+ dex_pc),
op_kind_(op) {
DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op;
}
@@ -143,7 +149,10 @@
public:
HIntermediateAddressIndex(
HInstruction* index, HInstruction* offset, HInstruction* shift, uint32_t dex_pc)
- : HExpression(DataType::Type::kInt32, SideEffects::None(), dex_pc) {
+ : HExpression(kIntermediateAddressIndex,
+ DataType::Type::kInt32,
+ SideEffects::None(),
+ dex_pc) {
SetRawInputAt(0, index);
SetRawInputAt(1, offset);
SetRawInputAt(2, shift);
@@ -193,7 +202,7 @@
// is an extension.
int shift = 0,
uint32_t dex_pc = kNoDexPc)
- : HExpression(instr->GetType(), SideEffects::None(), dex_pc),
+ : HExpression(kDataProcWithShifterOp, instr->GetType(), SideEffects::None(), dex_pc),
instr_kind_(instr->GetKind()), op_kind_(op),
shift_amount_(shift & (instr->GetType() == DataType::Type::kInt32
? kMaxIntShiftDistance
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index ecabdf3..0d38d57 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -71,13 +71,15 @@
// TODO: we could introduce SIMD types in HIR.
static constexpr DataType::Type kSIMDType = DataType::Type::kFloat64;
- HVecOperation(ArenaAllocator* allocator,
+ HVecOperation(InstructionKind kind,
+ ArenaAllocator* allocator,
DataType::Type packed_type,
SideEffects side_effects,
size_t number_of_inputs,
size_t vector_length,
uint32_t dex_pc)
- : HVariableInputSizeInstruction(side_effects,
+ : HVariableInputSizeInstruction(kind,
+ side_effects,
dex_pc,
allocator,
number_of_inputs,
@@ -196,12 +198,14 @@
// Abstraction of a unary vector operation.
class HVecUnaryOperation : public HVecOperation {
public:
- HVecUnaryOperation(ArenaAllocator* allocator,
+ HVecUnaryOperation(InstructionKind kind,
+ ArenaAllocator* allocator,
HInstruction* input,
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecOperation(allocator,
+ : HVecOperation(kind,
+ allocator,
packed_type,
SideEffects::None(),
/* number_of_inputs */ 1,
@@ -221,13 +225,15 @@
// Abstraction of a binary vector operation.
class HVecBinaryOperation : public HVecOperation {
public:
- HVecBinaryOperation(ArenaAllocator* allocator,
+ HVecBinaryOperation(InstructionKind kind,
+ ArenaAllocator* allocator,
HInstruction* left,
HInstruction* right,
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecOperation(allocator,
+ : HVecOperation(kind,
+ allocator,
packed_type,
SideEffects::None(),
/* number_of_inputs */ 2,
@@ -250,13 +256,15 @@
// The Android runtime guarantees elements have at least natural alignment.
class HVecMemoryOperation : public HVecOperation {
public:
- HVecMemoryOperation(ArenaAllocator* allocator,
+ HVecMemoryOperation(InstructionKind kind,
+ ArenaAllocator* allocator,
DataType::Type packed_type,
SideEffects side_effects,
size_t number_of_inputs,
size_t vector_length,
uint32_t dex_pc)
- : HVecOperation(allocator,
+ : HVecOperation(kind,
+ allocator,
packed_type,
side_effects,
number_of_inputs,
@@ -315,7 +323,8 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, scalar, packed_type, vector_length, dex_pc) {
+ : HVecUnaryOperation(
+ kVecReplicateScalar, allocator, scalar, packed_type, vector_length, dex_pc) {
DCHECK(!scalar->IsVecOperation());
}
@@ -341,7 +350,8 @@
size_t vector_length,
size_t index,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+ : HVecUnaryOperation(
+ kVecExtractScalar, allocator, input, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
DCHECK_LT(index, vector_length);
DCHECK_EQ(index, 0u);
@@ -379,7 +389,7 @@
size_t vector_length,
ReductionKind kind,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc),
+ : HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
kind_(kind) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
}
@@ -412,7 +422,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+ : HVecUnaryOperation(kVecCnv, allocator, input, packed_type, vector_length, dex_pc) {
DCHECK(input->IsVecOperation());
DCHECK_NE(GetInputType(), GetResultType()); // actual convert
}
@@ -437,7 +447,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+ : HVecUnaryOperation(kVecNeg, allocator, input, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
}
@@ -459,7 +469,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+ : HVecUnaryOperation(kVecAbs, allocator, input, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
}
@@ -481,7 +491,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+ : HVecUnaryOperation(kVecNot, allocator, input, packed_type, vector_length, dex_pc) {
DCHECK(input->IsVecOperation());
}
@@ -507,7 +517,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
}
@@ -533,7 +543,8 @@
size_t vector_length,
bool is_rounded,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(
+ kVecHalvingAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
SetPackedFlag<kFieldHAddIsRounded>(is_rounded);
@@ -571,7 +582,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecSub, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
}
@@ -594,7 +605,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecMul, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
}
@@ -617,7 +628,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecDiv, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
}
@@ -641,7 +652,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecMin, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
}
@@ -665,7 +676,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecMax, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
}
@@ -688,7 +699,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecAnd, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(left->IsVecOperation() && right->IsVecOperation());
}
@@ -710,7 +721,8 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(
+ kVecAndNot, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(left->IsVecOperation() && right->IsVecOperation());
}
@@ -732,7 +744,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecOr, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(left->IsVecOperation() && right->IsVecOperation());
}
@@ -754,7 +766,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecXor, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(left->IsVecOperation() && right->IsVecOperation());
}
@@ -776,7 +788,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecShl, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
}
@@ -798,7 +810,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecShr, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
}
@@ -820,7 +832,7 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+ : HVecBinaryOperation(kVecUShr, allocator, left, right, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(left, packed_type));
}
@@ -847,7 +859,8 @@
size_t vector_length,
size_t number_of_scalars,
uint32_t dex_pc)
- : HVecOperation(allocator,
+ : HVecOperation(kVecSetScalars,
+ allocator,
packed_type,
SideEffects::None(),
number_of_scalars,
@@ -881,7 +894,8 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecOperation(allocator,
+ : HVecOperation(kVecMultiplyAccumulate,
+ allocator,
packed_type,
SideEffects::None(),
/* number_of_inputs */ 3,
@@ -931,7 +945,8 @@
DataType::Type packed_type,
size_t vector_length,
uint32_t dex_pc)
- : HVecOperation(allocator,
+ : HVecOperation(kVecSADAccumulate,
+ allocator,
packed_type,
SideEffects::None(),
/* number_of_inputs */ 3,
@@ -965,7 +980,8 @@
size_t vector_length,
bool is_string_char_at,
uint32_t dex_pc)
- : HVecMemoryOperation(allocator,
+ : HVecMemoryOperation(kVecLoad,
+ allocator,
packed_type,
side_effects,
/* number_of_inputs */ 2,
@@ -1010,7 +1026,8 @@
SideEffects side_effects,
size_t vector_length,
uint32_t dex_pc)
- : HVecMemoryOperation(allocator,
+ : HVecMemoryOperation(kVecStore,
+ allocator,
packed_type,
side_effects,
/* number_of_inputs */ 3,
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 6326065..4c32be7 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -24,7 +24,11 @@
public:
// Treat the value as an int32_t, but it is really a 32 bit native pointer.
HX86ComputeBaseMethodAddress()
- : HExpression(DataType::Type::kInt32, SideEffects::None(), kNoDexPc) {}
+ : HExpression(kX86ComputeBaseMethodAddress,
+ DataType::Type::kInt32,
+ SideEffects::None(),
+ kNoDexPc) {
+ }
bool CanBeMoved() const OVERRIDE { return true; }
@@ -39,7 +43,10 @@
public:
HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
HConstant* constant)
- : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc) {
+ : HExpression(kX86LoadFromConstantTable,
+ constant->GetType(),
+ SideEffects::None(),
+ kNoDexPc) {
SetRawInputAt(0, method_base);
SetRawInputAt(1, constant);
}
@@ -65,7 +72,7 @@
HInstruction* input,
HX86ComputeBaseMethodAddress* method_base,
uint32_t dex_pc)
- : HExpression(result_type, SideEffects::None(), dex_pc) {
+ : HExpression(kX86FPNeg, result_type, SideEffects::None(), dex_pc) {
DCHECK(DataType::IsFloatingPointType(result_type));
SetRawInputAt(0, input);
SetRawInputAt(1, method_base);
@@ -89,7 +96,7 @@
HInstruction* input,
HX86ComputeBaseMethodAddress* method_base,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None(), dex_pc),
+ : HTemplateInstruction(kX86PackedSwitch, SideEffects::None(), dex_pc),
start_value_(start_value),
num_entries_(num_entries) {
SetRawInputAt(0, input);
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 12cfd24..6d3658f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1850,23 +1850,25 @@
GenerateOdexForTest(dex_location,
odex_location,
CompilerFilter::Filter::kQuicken,
- { "--dump-timings", "--dm-file=" + dm_file.GetFilename() },
+ { "--dump-timings",
+ "--dm-file=" + dm_file.GetFilename(),
+ // Pass -Xuse-stderr-logger have dex2oat output in output_ on target.
+ "--runtime-arg",
+ "-Xuse-stderr-logger" },
true, // expect_success
false, // use_fd
[](const OatFile& o) {
CHECK(o.ContainsDexCode());
});
- if (!kIsTargetBuild) {
- // The output_ variable is not correctly set for target, TODO: investigate.
- std::istringstream iss(output_);
- std::string line;
- bool found_fast_verify = false;
- const std::string kFastVerifyString = "Fast Verify";
- while (std::getline(iss, line) && !found_fast_verify) {
- found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos;
- }
- EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_;
+ // Check the output for "Fast verify", this is printed from --dump-timings.
+ std::istringstream iss(output_);
+ std::string line;
+ bool found_fast_verify = false;
+ const std::string kFastVerifyString = "Fast Verify";
+ while (std::getline(iss, line) && !found_fast_verify) {
+ found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos;
}
+ EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_;
}
} // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 09f9010..ee872db 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3348,9 +3348,8 @@
vdex_dex_files_offset_ = vdex_size_;
- extract_dex_files_into_vdex_ = copy_dex_files;
// If extraction is enabled, only do it if not all the dex files are aligned and uncompressed.
- if (extract_dex_files_into_vdex_) {
+ if (copy_dex_files) {
extract_dex_files_into_vdex_ = false;
for (OatDexFile& oat_dex_file : oat_dex_files_) {
if (!oat_dex_file.source_.IsZipEntry()) {
@@ -3363,6 +3362,8 @@
break;
}
}
+ } else {
+ extract_dex_files_into_vdex_ = false;
}
if (extract_dex_files_into_vdex_) {
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index 39cf86d..bd76bf1 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -19,8 +19,8 @@
#include "android-base/stringprintf.h"
#include "base/logging.h"
#include "base/time_utils.h"
-#include "dex/compact_dex_debug_info.h"
#include "dex/compact_dex_file.h"
+#include "dex/compact_offset_table.h"
#include "dexlayout.h"
namespace art {
@@ -76,12 +76,12 @@
std::vector<uint8_t> data;
debug_info_base_ = 0u;
debug_info_offsets_table_offset_ = 0u;
- CompactDexDebugInfoOffsetTable::Build(debug_info_offsets,
- &data,
- &debug_info_base_,
- &debug_info_offsets_table_offset_);
+ CompactOffsetTable::Build(debug_info_offsets,
+ &data,
+ &debug_info_base_,
+ &debug_info_offsets_table_offset_);
// Align the table and write it out.
- stream->AlignTo(CompactDexDebugInfoOffsetTable::kAlignment);
+ stream->AlignTo(CompactOffsetTable::kAlignment);
debug_info_offsets_pos_ = stream->Tell();
stream->Write(data.data(), data.size());
@@ -90,12 +90,12 @@
if (kMeasureAndTestOutput && !debug_info_offsets.empty()) {
uint64_t start_time = NanoTime();
stream->Begin();
- CompactDexDebugInfoOffsetTable::Accessor accessor(stream->Begin() + debug_info_offsets_pos_,
- debug_info_base_,
- debug_info_offsets_table_offset_);
+ CompactOffsetTable::Accessor accessor(stream->Begin() + debug_info_offsets_pos_,
+ debug_info_base_,
+ debug_info_offsets_table_offset_);
for (size_t i = 0; i < debug_info_offsets.size(); ++i) {
- CHECK_EQ(accessor.GetDebugInfoOffset(i), debug_info_offsets[i]);
+ CHECK_EQ(accessor.GetOffset(i), debug_info_offsets[i]);
}
uint64_t end_time = NanoTime();
VLOG(dex) << "Average lookup time (ns) for debug info offsets: "
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 90c603f..382a46a 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -19,8 +19,8 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: [
- "dex/compact_dex_debug_info.cc",
"dex/compact_dex_file.cc",
+ "dex/compact_offset_table.cc",
"dex/descriptors_names.cc",
"dex/dex_file.cc",
"dex/dex_file_exception_helpers.cc",
@@ -108,8 +108,8 @@
],
srcs: [
"dex/code_item_accessors_test.cc",
- "dex/compact_dex_debug_info_test.cc",
"dex/compact_dex_file_test.cc",
+ "dex/compact_offset_table_test.cc",
"dex/dex_file_loader_test.cc",
"dex/dex_file_verifier_test.cc",
"dex/dex_instruction_test.cc",
diff --git a/libdexfile/dex/compact_dex_file.h b/libdexfile/dex/compact_dex_file.h
index 47b170c..2bea071 100644
--- a/libdexfile/dex/compact_dex_file.h
+++ b/libdexfile/dex/compact_dex_file.h
@@ -19,7 +19,7 @@
#include "base/casts.h"
#include "dex_file.h"
-#include "dex/compact_dex_debug_info.h"
+#include "dex/compact_offset_table.h"
namespace art {
@@ -260,7 +260,7 @@
uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const {
- return debug_info_offsets_.GetDebugInfoOffset(dex_method_index);
+ return debug_info_offsets_.GetOffset(dex_method_index);
}
static uint32_t CalculateChecksum(const uint8_t* base_begin,
@@ -279,7 +279,7 @@
const OatDexFile* oat_dex_file,
DexFileContainer* container);
- CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_;
+ CompactOffsetTable::Accessor debug_info_offsets_;
friend class DexFile;
friend class DexFileLoader;
diff --git a/libdexfile/dex/compact_dex_debug_info.cc b/libdexfile/dex/compact_offset_table.cc
similarity index 61%
rename from libdexfile/dex/compact_dex_debug_info.cc
rename to libdexfile/dex/compact_offset_table.cc
index 19495ca..8cee0e3 100644
--- a/libdexfile/dex/compact_dex_debug_info.cc
+++ b/libdexfile/dex/compact_offset_table.cc
@@ -14,25 +14,25 @@
* limitations under the License.
*/
-#include "compact_dex_debug_info.h"
+#include "compact_offset_table.h"
#include "compact_dex_utils.h"
#include "leb128.h"
namespace art {
-constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex;
+constexpr size_t CompactOffsetTable::kElementsPerIndex;
-CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
- uint32_t debug_info_base,
- uint32_t debug_info_table_offset)
- : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)),
- debug_info_base_(debug_info_base),
+CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
+ uint32_t minimum_offset,
+ uint32_t table_offset)
+ : table_(reinterpret_cast<const uint32_t*>(data_begin + table_offset)),
+ minimum_offset_(minimum_offset),
data_begin_(data_begin) {}
-uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const {
- const uint32_t offset = table_[method_idx / kElementsPerIndex];
- const size_t bit_index = method_idx % kElementsPerIndex;
+uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const {
+ const uint32_t offset = table_[index / kElementsPerIndex];
+ const size_t bit_index = index % kElementsPerIndex;
const uint8_t* block = data_begin_ + offset;
uint16_t bit_mask = *block;
@@ -40,14 +40,14 @@
bit_mask = (bit_mask << kBitsPerByte) | *block;
++block;
if ((bit_mask & (1 << bit_index)) == 0) {
- // Bit is not set means the offset is 0 for the debug info.
+ // Bit is not set means the offset is 0.
return 0u;
}
// Trim off the bits above the index we want and count how many bits are set. This is how many
// lebs we need to decode.
size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index));
DCHECK_GT(count, 0u);
- uint32_t current_offset = debug_info_base_;
+ uint32_t current_offset = minimum_offset_;
do {
current_offset += DecodeUnsignedLeb128(&block);
--count;
@@ -55,15 +55,15 @@
return current_offset;
}
-void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets,
- std::vector<uint8_t>* out_data,
- uint32_t* out_min_offset,
- uint32_t* out_table_offset) {
+void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets,
+ std::vector<uint8_t>* out_data,
+ uint32_t* out_min_offset,
+ uint32_t* out_table_offset) {
DCHECK(out_data != nullptr);
DCHECK(out_data->empty());
// Calculate the base offset and return it.
*out_min_offset = std::numeric_limits<uint32_t>::max();
- for (const uint32_t offset : debug_info_offsets) {
+ for (const uint32_t offset : offsets) {
if (offset != 0u) {
*out_min_offset = std::min(*out_min_offset, offset);
}
@@ -74,17 +74,17 @@
std::vector<uint32_t> offset_table;
// Write data first then the table.
- while (block_start < debug_info_offsets.size()) {
+ while (block_start < offsets.size()) {
// Write the offset of the block for each block.
offset_table.push_back(out_data->size());
// Block size of up to kElementsPerIndex
- const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex);
+ const size_t block_size = std::min(offsets.size() - block_start, kElementsPerIndex);
// Calculate bit mask since need to write that first.
uint16_t bit_mask = 0u;
for (size_t i = 0; i < block_size; ++i) {
- if (debug_info_offsets[block_start + i] != 0u) {
+ if (offsets[block_start + i] != 0u) {
bit_mask |= 1 << i;
}
}
@@ -92,14 +92,14 @@
out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte));
out_data->push_back(static_cast<uint8_t>(bit_mask));
- // Write debug info offsets relative to the current offset.
- uint32_t current_offset = *out_min_offset;
+ // Write offsets relative to the previous offset.
+ uint32_t prev_offset = *out_min_offset;
for (size_t i = 0; i < block_size; ++i) {
- const uint32_t debug_info_offset = debug_info_offsets[block_start + i];
- if (debug_info_offset != 0u) {
- uint32_t delta = debug_info_offset - current_offset;
+ const uint32_t offset = offsets[block_start + i];
+ if (offset != 0u) {
+ uint32_t delta = offset - prev_offset;
EncodeUnsignedLeb128(out_data, delta);
- current_offset = debug_info_offset;
+ prev_offset = offset;
}
}
diff --git a/libdexfile/dex/compact_dex_debug_info.h b/libdexfile/dex/compact_offset_table.h
similarity index 63%
rename from libdexfile/dex/compact_dex_debug_info.h
rename to libdexfile/dex/compact_offset_table.h
index bfd0bbe..17e6bb4 100644
--- a/libdexfile/dex/compact_dex_debug_info.h
+++ b/libdexfile/dex/compact_offset_table.h
@@ -14,44 +14,44 @@
* limitations under the License.
*/
-#ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
-#define ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
+#ifndef ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
+#define ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
#include <cstdint>
#include <vector>
namespace art {
-// Debug offset table for compact dex, aims to minimize size while still providing reasonable
-// speed (10-20ns average time per lookup on host).
-class CompactDexDebugInfoOffsetTable {
+// Compact offset table that aims to minimize size while still providing reasonable speed (10-20ns
+// average time per lookup on host).
+class CompactOffsetTable {
public:
// This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the
// integer is modified.
static constexpr size_t kElementsPerIndex = 16;
// Leb block format:
- // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk.
+ // [uint16_t] 16 bit mask for what indexes actually have a non zero offset for the chunk.
// [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset
// changes compared to the previous index.
class Accessor {
public:
Accessor(const uint8_t* data_begin,
- uint32_t debug_info_base,
- uint32_t debug_info_table_offset);
+ uint32_t minimum_offset,
+ uint32_t table_offset);
- // Return the debug info for a method index (or 0 if it doesn't have one).
- uint32_t GetDebugInfoOffset(uint32_t method_idx) const;
+ // Return the offset for the index.
+ uint32_t GetOffset(uint32_t index) const;
private:
const uint32_t* const table_;
- const uint32_t debug_info_base_;
+ const uint32_t minimum_offset_;
const uint8_t* const data_begin_;
};
- // Returned offsets are all relative to debug_info_offsets.
- static void Build(const std::vector<uint32_t>& debug_info_offsets,
+ // Returned offsets are all relative to out_min_offset.
+ static void Build(const std::vector<uint32_t>& offsets,
std::vector<uint8_t>* out_data,
uint32_t* out_min_offset,
uint32_t* out_table_offset);
@@ -62,4 +62,4 @@
} // namespace art
-#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
+#endif // ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
diff --git a/libdexfile/dex/compact_dex_debug_info_test.cc b/libdexfile/dex/compact_offset_table_test.cc
similarity index 72%
rename from libdexfile/dex/compact_dex_debug_info_test.cc
rename to libdexfile/dex/compact_offset_table_test.cc
index 7911a86..7eb0156 100644
--- a/libdexfile/dex/compact_dex_debug_info_test.cc
+++ b/libdexfile/dex/compact_offset_table_test.cc
@@ -17,12 +17,12 @@
#include <vector>
#include <android-base/logging.h>
-#include "dex/compact_dex_debug_info.h"
+#include "dex/compact_offset_table.h"
#include "gtest/gtest.h"
namespace art {
-TEST(CompactDexDebugInfoTest, TestBuildAndAccess) {
+TEST(CompactOffsetTableTest, TestBuildAndAccess) {
const size_t kDebugInfoMinOffset = 1234567;
std::vector<uint32_t> offsets = {
0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0,
@@ -38,13 +38,10 @@
}
std::vector<uint8_t> data;
- uint32_t base_offset = 0;
+ uint32_t min_offset = 0;
uint32_t table_offset = 0;
- CompactDexDebugInfoOffsetTable::Build(offsets,
- /*out*/ &data,
- /*out*/ &base_offset,
- /*out*/ &table_offset);
- EXPECT_GE(base_offset, kDebugInfoMinOffset);
+ CompactOffsetTable::Build(offsets, /*out*/ &data, /*out*/ &min_offset, /*out*/ &table_offset);
+ EXPECT_GE(min_offset, kDebugInfoMinOffset);
EXPECT_LT(table_offset, data.size());
ASSERT_GT(data.size(), 0u);
const size_t before_size = offsets.size() * sizeof(offsets.front());
@@ -57,21 +54,19 @@
std::vector<uint8_t> fake_dex(data.size() + kExtraOffset);
std::copy(data.begin(), data.end(), fake_dex.data() + kExtraOffset);
- CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset,
- base_offset,
- table_offset);
+ CompactOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset, min_offset, table_offset);
for (size_t i = 0; i < offsets.size(); ++i) {
- EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i));
+ EXPECT_EQ(offsets[i], accessor.GetOffset(i));
}
// Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller
// for sorted increasing order.
std::sort(offsets.begin(), offsets.end());
std::vector<uint8_t> sorted_data;
- CompactDexDebugInfoOffsetTable::Build(offsets,
- /*out*/ &sorted_data,
- /*out*/ &base_offset,
- /*out*/ &table_offset);
+ CompactOffsetTable::Build(offsets,
+ /*out*/ &sorted_data,
+ /*out*/ &min_offset,
+ /*out*/ &table_offset);
EXPECT_LT(sorted_data.size(), data.size());
{
android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 4d54d75..d510ae5 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -69,7 +69,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
-#include "ti_class_loader.h"
+#include "ti_class_loader-inl.h"
#include "ti_phase.h"
#include "ti_redefine.h"
#include "utils.h"
@@ -862,6 +862,108 @@
return ERR(NONE);
}
+// Copies unique class descriptors into the classes list from dex_files.
+static jvmtiError CopyClassDescriptors(jvmtiEnv* env,
+ const std::vector<const art::DexFile*>& dex_files,
+ /*out*/jint* count_ptr,
+ /*out*/char*** classes) {
+ jvmtiError res = OK;
+ std::set<art::StringPiece> unique_descriptors;
+ std::vector<const char*> descriptors;
+ auto add_descriptor = [&](const char* desc) {
+ // Don't add duplicates.
+ if (res == OK && unique_descriptors.find(desc) == unique_descriptors.end()) {
+ // The desc will remain valid since we hold a ref to the class_loader.
+ unique_descriptors.insert(desc);
+ descriptors.push_back(CopyString(env, desc, &res).release());
+ }
+ };
+ for (const art::DexFile* dex_file : dex_files) {
+ uint32_t num_defs = dex_file->NumClassDefs();
+ for (uint32_t i = 0; i < num_defs; i++) {
+ add_descriptor(dex_file->GetClassDescriptor(dex_file->GetClassDef(i)));
+ }
+ }
+ char** out_data = nullptr;
+ if (res == OK) {
+ res = env->Allocate(sizeof(char*) * descriptors.size(),
+ reinterpret_cast<unsigned char**>(&out_data));
+ }
+ if (res != OK) {
+ env->Deallocate(reinterpret_cast<unsigned char*>(out_data));
+ // Failed to allocate. Cleanup everything.
+ for (const char* data : descriptors) {
+ env->Deallocate(reinterpret_cast<unsigned char*>(const_cast<char*>(data)));
+ }
+ descriptors.clear();
+ return res;
+ }
+ // Everything is good.
+ memcpy(out_data, descriptors.data(), sizeof(char*) * descriptors.size());
+ *count_ptr = static_cast<jint>(descriptors.size());
+ *classes = out_data;
+ return OK;
+}
+
+jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env,
+ jobject loader,
+ /*out*/jint* count_ptr,
+ /*out*/char*** classes) {
+ art::Thread* self = art::Thread::Current();
+ if (env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (self == nullptr) {
+ return ERR(UNATTACHED_THREAD);
+ } else if (count_ptr == nullptr || classes == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ if (loader == nullptr ||
+ jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_BootClassLoader)) {
+ // We can just get the dex files directly for the boot class path.
+ return CopyClassDescriptors(env,
+ art::Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+ count_ptr,
+ classes);
+ }
+ if (!jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_ClassLoader)) {
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (!jnienv->IsInstanceOf(loader,
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ LOG(ERROR) << "GetClassLoaderClassDescriptors is only implemented for BootClassPath and "
+ << "dalvik.system.BaseDexClassLoader class loaders";
+ // TODO Possibly return OK With no classes would be better since these ones cannot have any
+ // real classes associated with them.
+ return ERR(NOT_IMPLEMENTED);
+ }
+
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
+ std::vector<const art::DexFile*> dex_files;
+ ClassLoaderHelper::VisitDexFileObjects(
+ self,
+ class_loader,
+ [&] (art::ObjPtr<art::mirror::Object> dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::StackHandleScope<2> hs(self);
+ art::Handle<art::mirror::Object> h_dex_file(hs.NewHandle(dex_file));
+ art::Handle<art::mirror::LongArray> cookie(
+ hs.NewHandle(ClassLoaderHelper::GetDexFileCookie(h_dex_file)));
+ size_t num_elements = cookie->GetLength();
+ // We need to skip over the oat_file that's the first element. The other elements are all
+ // dex files.
+ for (size_t i = 1; i < num_elements; i++) {
+ dex_files.push_back(
+ reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(cookie->Get(i))));
+ }
+ // Iterate over all dex files.
+ return true;
+ });
+ // We hold the loader so the dex files won't go away until after this call at worst.
+ return CopyClassDescriptors(env, dex_files, count_ptr, classes);
+}
+
jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env,
jobject initiating_loader,
jint* class_count_ptr,
diff --git a/openjdkjvmti/ti_class.h b/openjdkjvmti/ti_class.h
index dd99e36..7e427a0 100644
--- a/openjdkjvmti/ti_class.h
+++ b/openjdkjvmti/ti_class.h
@@ -75,6 +75,11 @@
jint* class_count_ptr,
jclass** classes_ptr);
+ static jvmtiError GetClassLoaderClassDescriptors(jvmtiEnv* env,
+ jobject loader,
+ jint* count_ptr,
+ char*** classes);
+
static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h
new file mode 100644
index 0000000..95278f4
--- /dev/null
+++ b/openjdkjvmti/ti_class_loader-inl.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_
+#define ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_
+
+#include "ti_class_loader.h"
+#include "art_field-inl.h"
+#include "handle.h"
+#include "handle_scope.h"
+#include "jni_internal.h"
+#include "mirror/object.h"
+#include "mirror/object_array-inl.h"
+#include "well_known_classes.h"
+
+namespace openjdkjvmti {
+
+template<typename Visitor>
+inline void ClassLoaderHelper::VisitDexFileObjects(art::Thread* self,
+ art::Handle<art::mirror::ClassLoader> loader,
+ const Visitor& visitor) {
+ art::StackHandleScope<1> hs(self);
+ art::ArtField* element_dex_file_field = art::jni::DecodeArtField(
+ art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+
+ art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(
+ hs.NewHandle(GetDexElementList(self, loader)));
+ if (dex_elements_list == nullptr) {
+ return;
+ }
+
+ size_t num_elements = dex_elements_list->GetLength();
+ // Iterate over the DexPathList$Element to find the right one
+ for (size_t i = 0; i < num_elements; i++) {
+ art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
+ CHECK(!current_element.IsNull());
+ art::ObjPtr<art::mirror::Object> dex_file(element_dex_file_field->GetObject(current_element));
+ if (!dex_file.IsNull()) {
+ if (!visitor(dex_file)) {
+ return;
+ }
+ }
+ }
+}
+
+} // namespace openjdkjvmti
+
+#endif // ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_
diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
index d594d6e..3df5de9 100644
--- a/openjdkjvmti/ti_class_loader.cc
+++ b/openjdkjvmti/ti_class_loader.cc
@@ -29,7 +29,7 @@
* questions.
*/
-#include "ti_class_loader.h"
+#include "ti_class_loader-inl.h"
#include <limits>
@@ -134,45 +134,28 @@
return new_cookie.Get();
}
-// TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
-art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
- art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) {
- const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
- const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
- const char* dex_file_name = "Ldalvik/system/DexFile;";
- const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
- const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
+art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> ClassLoaderHelper::GetDexElementList(
+ art::Thread* self,
+ art::Handle<art::mirror::ClassLoader> loader) {
+ art::StackHandleScope<4> hs(self);
- CHECK(!self->IsExceptionPending());
- art::StackHandleScope<5> hs(self);
- art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
-
- art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
- nullptr));
- art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
- self, dex_class_loader_name, null_loader)));
+ art::Handle<art::mirror::Class>
+ base_dex_loader_class(hs.NewHandle(self->DecodeJObject(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader)->AsClass()));
// Get all the ArtFields so we can look in the BaseDexClassLoader
- art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
- "pathList", dex_path_list_name);
- CHECK(path_list_field != nullptr);
-
+ art::ArtField* path_list_field = art::jni::DecodeArtField(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
art::ArtField* dex_path_list_element_field =
- class_linker->FindClass(self, dex_path_list_name, null_loader)
- ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
- CHECK(dex_path_list_element_field != nullptr);
-
- art::ArtField* element_dex_file_field =
- class_linker->FindClass(self, dex_path_list_element_name, null_loader)
- ->FindDeclaredInstanceField("dexFile", dex_file_name);
- CHECK(element_dex_file_field != nullptr);
+ art::jni::DecodeArtField(art::WellKnownClasses::dalvik_system_DexPathList_dexElements);
// Check if loader is a BaseDexClassLoader
art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
// Currently only base_dex_loader is allowed to actually define classes but if this changes in the
// future we should make sure to support all class loader types.
if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
- LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
+ LOG(ERROR) << "The classloader " << loader_class->PrettyClass() << " is not a "
+ << base_dex_loader_class->PrettyClass() << " which is currently the only "
<< "supported class loader type!";
return nullptr;
}
@@ -180,28 +163,28 @@
art::Handle<art::mirror::Object> path_list(
hs.NewHandle(path_list_field->GetObject(loader.Get())));
CHECK(path_list != nullptr);
- CHECK(!self->IsExceptionPending());
- art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
- dex_path_list_element_field->GetObject(path_list.Get())->
- AsObjectArray<art::mirror::Object>()));
- CHECK(!self->IsExceptionPending());
- CHECK(dex_elements_list != nullptr);
- size_t num_elements = dex_elements_list->GetLength();
- // Iterate over the DexPathList$Element to find the right one
- for (size_t i = 0; i < num_elements; i++) {
- art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
- CHECK(!current_element.IsNull());
- // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
- // comes from but it is more annoying because we would need to find this class. It is not
- // necessary for proper function since we just need to be in front of the classes old dex file
- // in the path.
- art::ObjPtr<art::mirror::Object> first_dex_file(
- element_dex_file_field->GetObject(current_element));
- if (!first_dex_file.IsNull()) {
- return first_dex_file;
- }
- }
- return nullptr;
+ art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list =
+ dex_path_list_element_field->GetObject(path_list.Get())->AsObjectArray<art::mirror::Object>();
+ return dex_elements_list;
+}
+
+// TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
+art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
+ art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) {
+ art::ObjPtr<art::mirror::Object> res = nullptr;
+ VisitDexFileObjects(self,
+ loader,
+ [&] (art::ObjPtr<art::mirror::Object> dex_file) {
+ res = dex_file;
+ // Just stop at the first one.
+ // TODO It would be cleaner to put the art::DexFile into the
+ // dalvik.system.DexFile the class comes from but it is more annoying
+ // because we would need to find this class. It is not necessary for proper
+ // function since we just need to be in front of the classes old dex file in
+ // the path.
+ return false;
+ });
+ return res;
}
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h
index ceb7b33..5c9497b 100644
--- a/openjdkjvmti/ti_class_loader.h
+++ b/openjdkjvmti/ti_class_loader.h
@@ -82,6 +82,14 @@
art::Thread* self, art::Handle<art::mirror::ClassLoader> loader)
REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // Calls visitor on each java.lang.DexFile associated with the given loader. The visitor should
+ // return true to continue on to the next DexFile or false to stop iterating.
+ template<typename Visitor>
+ static inline void VisitDexFileObjects(art::Thread* self,
+ art::Handle<art::mirror::ClassLoader> loader,
+ const Visitor& visitor)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
static art::ObjPtr<art::mirror::LongArray> GetDexFileCookie(
art::Handle<art::mirror::Object> java_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_);
@@ -93,6 +101,11 @@
static void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
art::ObjPtr<art::mirror::LongArray> new_cookie)
REQUIRES(art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+ static art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetDexElementList(
+ art::Thread* self, art::Handle<art::mirror::ClassLoader> loader)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
};
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 79a8cd6..5b1a16c 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -36,6 +36,7 @@
#include "art_jvmti.h"
#include "events.h"
#include "ti_allocator.h"
+#include "ti_class.h"
#include "ti_ddms.h"
#include "ti_heap.h"
#include "thread-inl.h"
@@ -226,6 +227,31 @@
return error;
}
+ // GetClassLoaderClassDescriptors extension
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::GetClassLoaderClassDescriptors),
+ "com.android.art.class.get_class_loader_class_descriptors",
+ "Retrieves a list of all the classes (as class descriptors) that the given class loader is"
+ " capable of being the defining class loader for. The return format is a list of"
+ " null-terminated descriptor strings of the form \"L/java/lang/Object;\". Each descriptor"
+ " will be in the list at most once. If the class_loader is null the bootclassloader will be"
+ " used. If the class_loader is not null it must either be a java.lang.BootClassLoader, a"
+ " dalvik.system.BaseDexClassLoader or a derived type. The data_out list and all elements"
+ " must be deallocated by the caller.",
+ {
+ { "class_loader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, true },
+ { "class_descriptor_count_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
+ { "data_out", JVMTI_KIND_ALLOC_ALLOC_BUF, JVMTI_TYPE_CCHAR, false },
+ },
+ {
+ ERR(NULL_POINTER),
+ ERR(ILLEGAL_ARGUMENT),
+ ERR(OUT_OF_MEMORY),
+ ERR(NOT_IMPLEMENTED),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 4900f17..9267b18 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -1093,10 +1093,10 @@
UsageError(" --input-image-location=<file.art>: Specifies the 'location' of the image file to");
UsageError(" be patched.");
UsageError("");
- UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched");
- UsageError(" image file to.");
+ UsageError(" --output-image-directory=<dir>: Specifies the directory to write the patched");
+ UsageError(" image file(s) to.");
UsageError("");
- UsageError(" --output-image-relocation-file=<file.art.rel>: Specifies the exact file to write");
+ UsageError(" --output-image-relocation-directory=<dir>: Specifies the directory to write");
UsageError(" the image relocation information to.");
UsageError("");
UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
@@ -1116,13 +1116,13 @@
InstructionSet isa,
const std::string& input_image_location,
const std::string& output_image_directory,
- const std::string& output_image_relocation_filename,
+ const std::string& output_image_relocation_directory,
off_t base_delta,
bool base_delta_set,
bool debug) {
CHECK(!input_image_location.empty());
- if ((output_image_directory.empty()) && (output_image_relocation_filename.empty())) {
- Usage("Image patching requires --output-image-file or --output-image-relocation-file");
+ if ((output_image_directory.empty()) && (output_image_relocation_directory.empty())) {
+ Usage("Image patching requires --output-image-directory or --output-image-relocation-directory");
}
if (!base_delta_set) {
@@ -1141,9 +1141,6 @@
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
- std::string output_image_relocation_directory =
- output_image_relocation_filename.substr(
- 0, output_image_relocation_filename.find_last_of('/'));
bool ret =
PatchOat::Patch(
input_image_location,
@@ -1201,8 +1198,8 @@
bool isa_set = false;
InstructionSet isa = InstructionSet::kNone;
std::string input_image_location;
- std::string output_image_filename;
- std::string output_image_relocation_filename;
+ std::string output_image_directory;
+ std::string output_image_relocation_directory;
off_t base_delta = 0;
bool base_delta_set = false;
bool dump_timings = kIsDebugBuild;
@@ -1223,11 +1220,11 @@
}
} else if (option.starts_with("--input-image-location=")) {
input_image_location = option.substr(strlen("--input-image-location=")).data();
- } else if (option.starts_with("--output-image-file=")) {
- output_image_filename = option.substr(strlen("--output-image-file=")).data();
- } else if (option.starts_with("--output-image-relocation-file=")) {
- output_image_relocation_filename =
- option.substr(strlen("--output-image-relocation-file=")).data();
+ } else if (option.starts_with("--output-image-directory=")) {
+ output_image_directory = option.substr(strlen("--output-image-directory=")).data();
+ } else if (option.starts_with("--output-image-relocation-directory=")) {
+ output_image_relocation_directory =
+ option.substr(strlen("--output-image-relocation-directory=")).data();
} else if (option.starts_with("--base-offset-delta=")) {
const char* base_delta_str = option.substr(strlen("--base-offset-delta=")).data();
base_delta_set = true;
@@ -1245,12 +1242,6 @@
}
}
- // TODO: Have calls to patchoat pass in the output_image directory instead of
- // the output_image_filename.
- std::string output_image_directory;
- if (!output_image_filename.empty())
- output_image_directory = android::base::Dirname(output_image_filename);
-
// The instruction set is mandatory. This simplifies things...
if (!isa_set) {
Usage("Instruction set must be set.");
@@ -1267,7 +1258,7 @@
isa,
input_image_location,
output_image_directory,
- output_image_relocation_filename,
+ output_image_relocation_directory,
base_delta,
base_delta_set,
debug);
diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc
index 69c6bfa..ac3abd0 100644
--- a/patchoat/patchoat_test.cc
+++ b/patchoat/patchoat_test.cc
@@ -137,32 +137,32 @@
}
bool RelocateBootImage(const std::string& input_image_location,
- const std::string& output_image_filename,
+ const std::string& output_image_directory,
off_t base_offset_delta,
std::string* error_msg) {
std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
- argv.push_back("--output-image-file=" + output_image_filename);
+ argv.push_back("--output-image-directory=" + output_image_directory);
return RunDex2OatOrPatchoat(argv, error_msg);
}
bool VerifyBootImage(const std::string& input_image_location,
- const std::string& output_image_filename,
+ const std::string& output_image_directory,
off_t base_offset_delta,
std::string* error_msg) {
std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
- argv.push_back("--output-image-file=" + output_image_filename);
+ argv.push_back("--output-image-directory=" + output_image_directory);
argv.push_back("--verify");
return RunDex2OatOrPatchoat(argv, error_msg);
}
bool GenerateBootImageRelFile(const std::string& input_image_location,
- const std::string& output_rel_filename,
+ const std::string& output_rel_directory,
off_t base_offset_delta,
std::string* error_msg) {
std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
- argv.push_back("--output-image-relocation-file=" + output_rel_filename);
+ argv.push_back("--output-image-relocation-directory=" + output_rel_directory);
return RunDex2OatOrPatchoat(argv, error_msg);
}
@@ -375,7 +375,7 @@
ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str()));
if (!RelocateBootImage(
dex2oat_orig_dir + "/boot.art",
- patchoat_dir + "/boot.art",
+ patchoat_dir,
base_addr_delta,
&error_msg)) {
FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -467,7 +467,7 @@
off_t base_addr_delta = 0x100000;
if (!GenerateBootImageRelFile(
dex2oat_orig_dir + "/boot.art",
- dex2oat_orig_dir + "/boot.art.rel",
+ dex2oat_orig_dir,
base_addr_delta,
&error_msg)) {
FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -483,7 +483,7 @@
base_addr_delta -= 0x10000;
if (!RelocateBootImage(
dex2oat_orig_dir + "/boot.art",
- relocated_dir + "/boot.art",
+ relocated_dir,
base_addr_delta,
&error_msg)) {
FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -524,7 +524,7 @@
// Assert that verification works with the .rel files.
if (!VerifyBootImage(
dex2oat_orig_dir + "/boot.art",
- relocated_dir + "/boot.art",
+ relocated_dir,
base_addr_delta,
&error_msg)) {
FAIL() << "VerifyBootImage failed: " << error_msg;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 05f099f..02580cc 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -61,6 +61,10 @@
static constexpr uint64_t kCriticalWarnTimeUs = MsToUs(16);
static_assert(kCriticalWarnTimeUs > 0, "No JNI critical warn time set");
+// True if primitives within specific ranges cause a fatal error,
+// otherwise just warn.
+static constexpr bool kBrokenPrimitivesAreFatal = kIsDebugBuild;
+
// Flags passed into ScopedCheck.
static constexpr uint16_t kFlag_Default = 0x0000;
@@ -206,10 +210,12 @@
JniValueType o;
if (type_ == kTypeVaList) {
switch (fmt) {
- case 'Z': o.Z = static_cast<jboolean>(va_arg(vargs_, jint)); break;
- case 'B': o.B = static_cast<jbyte>(va_arg(vargs_, jint)); break;
- case 'C': o.C = static_cast<jchar>(va_arg(vargs_, jint)); break;
- case 'S': o.S = static_cast<jshort>(va_arg(vargs_, jint)); break;
+ // Assign a full int for va_list values as this is what is done in reflection.cc.
+ // TODO(b/73656264): avoid undefined behavior.
+ case 'Z': FALLTHROUGH_INTENDED;
+ case 'B': FALLTHROUGH_INTENDED;
+ case 'C': FALLTHROUGH_INTENDED;
+ case 'S': FALLTHROUGH_INTENDED;
case 'I': o.I = va_arg(vargs_, jint); break;
case 'J': o.J = va_arg(vargs_, jlong); break;
case 'F': o.F = static_cast<jfloat>(va_arg(vargs_, jdouble)); break;
@@ -224,10 +230,14 @@
jvalue v = ptr_[cnt_];
cnt_++;
switch (fmt) {
- case 'Z': o.Z = v.z; break;
- case 'B': o.B = v.b; break;
- case 'C': o.C = v.c; break;
- case 'S': o.S = v.s; break;
+ // Copy just the amount of the jvalue necessary, as done in
+ // reflection.cc, but extend to an int to be consistent with
+ // var args in CheckNonHeapValue.
+ // TODO(b/73656264): avoid undefined behavior.
+ case 'Z': o.I = v.z; break;
+ case 'B': o.I = v.b; break;
+ case 'C': o.I = v.c; break;
+ case 'S': o.I = v.s; break;
case 'I': o.I = v.i; break;
case 'J': o.J = v.j; break;
case 'F': o.F = v.f; break;
@@ -911,17 +921,20 @@
switch (fmt) {
case 'p': // TODO: pointer - null or readable?
case 'v': // JavaVM*
- case 'B': // jbyte
- case 'C': // jchar
case 'D': // jdouble
case 'F': // jfloat
- case 'I': // jint
case 'J': // jlong
- case 'S': // jshort
+ case 'I': // jint
break; // Ignored.
case 'b': // jboolean, why two? Fall-through.
case 'Z':
- return CheckBoolean(arg.Z);
+ return CheckBoolean(arg.I);
+ case 'B': // jbyte
+ return CheckByte(arg.I);
+ case 'C': // jchar
+ return CheckChar(arg.I);
+ case 'S': // jshort
+ return CheckShort(arg.I);
case 'u': // utf8
if ((flags_ & kFlag_Release) != 0) {
return CheckNonNull(arg.u);
@@ -1152,14 +1165,54 @@
return true;
}
- bool CheckBoolean(jboolean z) {
+ bool CheckBoolean(jint z) {
if (z != JNI_TRUE && z != JNI_FALSE) {
+ // Note, broken booleans are always fatal.
AbortF("unexpected jboolean value: %d", z);
return false;
}
return true;
}
+ bool CheckByte(jint b) {
+ if (b < std::numeric_limits<jbyte>::min() ||
+ b > std::numeric_limits<jbyte>::max()) {
+ if (kBrokenPrimitivesAreFatal) {
+ AbortF("unexpected jbyte value: %d", b);
+ return false;
+ } else {
+ LOG(WARNING) << "Unexpected jbyte value: " << b;
+ }
+ }
+ return true;
+ }
+
+ bool CheckShort(jint s) {
+ if (s < std::numeric_limits<jshort>::min() ||
+ s > std::numeric_limits<jshort>::max()) {
+ if (kBrokenPrimitivesAreFatal) {
+ AbortF("unexpected jshort value: %d", s);
+ return false;
+ } else {
+ LOG(WARNING) << "Unexpected jshort value: " << s;
+ }
+ }
+ return true;
+ }
+
+ bool CheckChar(jint c) {
+ if (c < std::numeric_limits<jchar>::min() ||
+ c > std::numeric_limits<jchar>::max()) {
+ if (kBrokenPrimitivesAreFatal) {
+ AbortF("unexpected jchar value: %d", c);
+ return false;
+ } else {
+ LOG(WARNING) << "Unexpected jchar value: " << c;
+ }
+ }
+ return true;
+ }
+
bool CheckLengthPositive(jsize length) {
if (length < 0) {
AbortF("negative jsize: %d", length);
@@ -1813,7 +1866,7 @@
static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) {
ScopedObjectAccess soa(env);
ScopedCheck sc(kFlag_Default, __FUNCTION__);
- JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.b = isStatic}};
+ JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.I = isStatic}};
if (sc.Check(soa, true, "Ecmb", args)) {
JniValueType result;
result.L = baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic);
@@ -1828,7 +1881,7 @@
static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) {
ScopedObjectAccess soa(env);
ScopedCheck sc(kFlag_Default, __FUNCTION__);
- JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.b = isStatic}};
+ JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.I = isStatic}};
if (sc.Check(soa, true, "Ecfb", args)) {
JniValueType result;
result.L = baseEnv(env)->ToReflectedField(env, cls, fid, isStatic);
@@ -2113,7 +2166,7 @@
return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, true);
}
-#define FIELD_ACCESSORS(jtype, name, ptype, shorty) \
+#define FIELD_ACCESSORS(jtype, name, ptype, shorty, slot_sized_shorty) \
static jtype GetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid) { \
return GetField(__FUNCTION__, env, c, fid, true, ptype).shorty; \
} \
@@ -2124,25 +2177,25 @@
\
static void SetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid, jtype v) { \
JniValueType value; \
- value.shorty = v; \
+ value.slot_sized_shorty = v; \
SetField(__FUNCTION__, env, c, fid, true, ptype, value); \
} \
\
static void Set##name##Field(JNIEnv* env, jobject obj, jfieldID fid, jtype v) { \
JniValueType value; \
- value.shorty = v; \
+ value.slot_sized_shorty = v; \
SetField(__FUNCTION__, env, obj, fid, false, ptype, value); \
}
- FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L)
- FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z)
- FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B)
- FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C)
- FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S)
- FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I)
- FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J)
- FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F)
- FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D)
+ FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L, L)
+ FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z, I)
+ FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B, I)
+ FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C, I)
+ FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S, I)
+ FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I, I)
+ FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J, J)
+ FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F, F)
+ FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D, D)
#undef FIELD_ACCESSORS
static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) {
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 20cde53..75642fc 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,26 +160,6 @@
+ "/core.art";
}
- bool GetCachedImageFile(const std::string& image_location,
- /*out*/std::string* image,
- /*out*/std::string* error_msg) const {
- std::string cache;
- bool have_android_data;
- bool dalvik_cache_exists;
- bool is_global_cache;
- GetDalvikCache(GetInstructionSetString(kRuntimeISA),
- true,
- &cache,
- &have_android_data,
- &dalvik_cache_exists,
- &is_global_cache);
- if (!dalvik_cache_exists) {
- *error_msg = "Failed to create dalvik cache";
- return false;
- }
- return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg);
- }
-
// Returns the path to an image location whose contents differ from the
// image at GetImageLocation(). This is used for testing mismatched
// image checksums in the oat_file_assistant_tests.
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 8ce7921..c19fa82 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -20,6 +20,7 @@
#include <backtrace/BacktraceMap.h>
#include <gtest/gtest.h>
+#include "base/file_utils.h"
#include "common_runtime_test.h"
#include "compiler_callbacks.h"
#include "dex2oat_environment_test.h"
@@ -197,8 +198,18 @@
}
bool DexoptTest::PreRelocateImage(const std::string& image_location, std::string* error_msg) {
- std::string image;
- if (!GetCachedImageFile(image_location, &image, error_msg)) {
+ std::string dalvik_cache;
+ bool have_android_data;
+ bool dalvik_cache_exists;
+ bool is_global_cache;
+ GetDalvikCache(GetInstructionSetString(kRuntimeISA),
+ true,
+ &dalvik_cache,
+ &have_android_data,
+ &dalvik_cache_exists,
+ &is_global_cache);
+ if (!dalvik_cache_exists) {
+ *error_msg = "Failed to create dalvik cache";
return false;
}
@@ -208,7 +219,7 @@
std::vector<std::string> argv;
argv.push_back(patchoat);
argv.push_back("--input-image-location=" + image_location);
- argv.push_back("--output-image-file=" + image);
+ argv.push_back("--output-image-directory=" + dalvik_cache);
argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
argv.push_back("--base-offset-delta=0x00008000");
return Exec(argv, error_msg);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 17913fc..cb43601 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3453,7 +3453,8 @@
if (gc_type != collector::kGcTypeSticky) {
// Grow the heap for non sticky GC.
ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
- CHECK_GE(delta, 0);
+ CHECK_GE(delta, 0) << "bytes_allocated=" << bytes_allocated
+ << " target_utilization_=" << target_utilization_;
target_size = bytes_allocated + delta * multiplier;
target_size = std::min(target_size, bytes_allocated + adjusted_max_free);
target_size = std::max(target_size, bytes_allocated + adjusted_min_free);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 7fb634f..9d2d2ed 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1331,7 +1331,7 @@
// The ideal maximum free size, when we grow the heap for utilization.
size_t max_free_;
- // Target ideal heap utilization ratio
+ // Target ideal heap utilization ratio.
double target_utilization_;
// How much more we grow the heap when we are a foreground app instead of background.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 2b06e83..9ae1f45 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -240,7 +240,7 @@
// Relocate the image at image_location to dest_filename and relocate it by a random amount.
static bool RelocateImage(const char* image_location,
- const char* dest_filename,
+ const char* dest_directory,
InstructionSet isa,
std::string* error_msg) {
// We should clean up so we are more likely to have room for the image.
@@ -254,8 +254,8 @@
std::string input_image_location_arg("--input-image-location=");
input_image_location_arg += image_location;
- std::string output_image_filename_arg("--output-image-file=");
- output_image_filename_arg += dest_filename;
+ std::string output_image_directory_arg("--output-image-directory=");
+ output_image_directory_arg += dest_directory;
std::string instruction_set_arg("--instruction-set=");
instruction_set_arg += GetInstructionSetString(isa);
@@ -267,7 +267,7 @@
argv.push_back(patchoat);
argv.push_back(input_image_location_arg);
- argv.push_back(output_image_filename_arg);
+ argv.push_back(output_image_directory_arg);
argv.push_back(instruction_set_arg);
argv.push_back(base_offset_arg);
@@ -278,7 +278,7 @@
}
static bool VerifyImage(const char* image_location,
- const char* dest_filename,
+ const char* dest_directory,
InstructionSet isa,
std::string* error_msg) {
std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
@@ -286,8 +286,8 @@
std::string input_image_location_arg("--input-image-location=");
input_image_location_arg += image_location;
- std::string output_image_filename_arg("--output-image-file=");
- output_image_filename_arg += dest_filename;
+ std::string output_image_directory_arg("--output-image-directory=");
+ output_image_directory_arg += dest_directory;
std::string instruction_set_arg("--instruction-set=");
instruction_set_arg += GetInstructionSetString(isa);
@@ -296,7 +296,7 @@
argv.push_back(patchoat);
argv.push_back(input_image_location_arg);
- argv.push_back(output_image_filename_arg);
+ argv.push_back(output_image_directory_arg);
argv.push_back(instruction_set_arg);
@@ -1536,7 +1536,7 @@
std::string local_error_msg;
// All secondary images are verified when the primary image is verified.
bool verified = secondary_image || VerifyImage(image_location,
- cache_filename.c_str(),
+ dalvik_cache.c_str(),
image_isa,
&local_error_msg);
if (!(verified && CheckSpace(dalvik_cache, &local_error_msg))) {
@@ -1628,7 +1628,7 @@
_exit(1);
} else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
bool patch_success =
- RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg);
+ RelocateImage(image_location, dalvik_cache.c_str(), image_isa, &local_error_msg);
if (patch_success) {
std::unique_ptr<ImageSpace> patched_space =
ImageSpaceLoader::Load(image_location,
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index ad24c94..6300038 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -835,6 +835,73 @@
check_jni_abort_catcher.Check("null");
}
+TEST_F(JniInternalTest, CallVarArgMethodBadPrimitive) {
+ // Check that bad primitive values cause check JNI to abort when
+ // passed out-of-range primitive value var args. As var args can't
+ // differentiate type sizes less than an int, and this isn't
+ // corrected by JNI, this helps ensure JNI code is valid.
+#define DoCall(boxed_type, shorty, c_type, bad_value) \
+ { \
+ jclass prim_class = env_->FindClass("java/lang/" #boxed_type); \
+ jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf", \
+ "(" #shorty ")Ljava/lang/" #boxed_type ";"); \
+ EXPECT_NE(nullptr, method); \
+ EXPECT_FALSE(env_->ExceptionCheck()); \
+ CheckJniAbortCatcher check_jni_abort_catcher; \
+ env_->CallStaticObjectMethod(prim_class, method, bad_value); \
+ check_jni_abort_catcher.Check("unexpected " #c_type " value: " #bad_value); \
+ }
+
+ DoCall(Boolean, Z, jboolean, 2);
+ DoCall(Byte, B, jbyte, 128);
+ DoCall(Byte, B, jbyte, -129);
+ DoCall(Short, S, jshort, 32768);
+ DoCall(Short, S, jshort, -32769);
+ DoCall(Character, C, jchar, 65536);
+ DoCall(Character, C, jchar, -1);
+#undef DoCall
+}
+
+TEST_F(JniInternalTest, CallJValueMethodBadPrimitive) {
+ // Check that bad primitive values, passed as jvalues, cause check
+ // JNI to abort. Unlike with var args, sizes less than an int should
+ // be truncated or sign extended and not cause an abort except for
+ // jbooleans that are passed as bytes.
+#define DoFailCall(boxed_type, shorty, c_type, bad_value) \
+ { \
+ jclass prim_class = env_->FindClass("java/lang/" #boxed_type); \
+ jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf", \
+ "(" #shorty ")Ljava/lang/" #boxed_type ";"); \
+ EXPECT_NE(nullptr, method); \
+ EXPECT_FALSE(env_->ExceptionCheck()); \
+ CheckJniAbortCatcher check_jni_abort_catcher; \
+ jvalue jval; \
+ jval.i = bad_value; \
+ env_->CallStaticObjectMethodA(prim_class, method, &jval); \
+ check_jni_abort_catcher.Check("unexpected " #c_type " value: " #bad_value); \
+ }
+#define DoGoodCall(boxed_type, shorty, c_type, bad_value) \
+ { \
+ jclass prim_class = env_->FindClass("java/lang/" #boxed_type); \
+ jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf", \
+ "(" #shorty ")Ljava/lang/" #boxed_type ";"); \
+ EXPECT_NE(nullptr, method); \
+ EXPECT_FALSE(env_->ExceptionCheck()); \
+ jvalue jval; \
+ jval.i = bad_value; \
+ env_->CallStaticObjectMethodA(prim_class, method, &jval); \
+ }
+
+ DoFailCall(Boolean, Z, jboolean, 2);
+ DoGoodCall(Byte, B, jbyte, 128);
+ DoGoodCall(Byte, B, jbyte, -129);
+ DoGoodCall(Short, S, jshort, 32768);
+ DoGoodCall(Short, S, jshort, -32769);
+ DoGoodCall(Character, C, jchar, 65536);
+ DoGoodCall(Character, C, jchar, -1);
+#undef DoCall
+}
+
TEST_F(JniInternalTest, GetStaticMethodID) {
jclass jlobject = env_->FindClass("java/lang/Object");
jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 635a03a..068bc28 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -35,6 +35,7 @@
#include "well_known_classes.h"
namespace art {
+namespace {
using android::base::StringPrintf;
@@ -157,6 +158,7 @@
Append(args[args_offset].s);
break;
case 'I':
+ FALLTHROUGH_INTENDED;
case 'F':
Append(args[args_offset].i);
break;
@@ -164,6 +166,7 @@
Append(soa.Decode<mirror::Object>(args[args_offset].l));
break;
case 'D':
+ FALLTHROUGH_INTENDED;
case 'J':
AppendWide(args[args_offset].j);
break;
@@ -361,7 +364,7 @@
std::unique_ptr<uint32_t[]> large_arg_array_;
};
-static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args)
+void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile::TypeList* params = m->GetParameterTypeList();
if (params == nullptr) {
@@ -436,13 +439,13 @@
}
}
-static ArtMethod* FindVirtualMethod(ObjPtr<mirror::Object> receiver, ArtMethod* method)
+ArtMethod* FindVirtualMethod(ObjPtr<mirror::Object> receiver, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method, kRuntimePointerSize);
}
-static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
+void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
ArtMethod* method, ArgArray* arg_array, JValue* result,
const char* shorty)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -453,6 +456,8 @@
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
}
+} // anonymous namespace
+
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
va_list args)
REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d0aec11..2074f1e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -844,6 +844,18 @@
0);
}
+ // In case we have a profile path passed as a command line argument,
+ // register the current class path for profiling now. Note that we cannot do
+ // this before we create the JIT and having it here is the most convenient way.
+ // This is used when testing profiles with dalvikvm command as there is no
+ // framework to register the dex files for profiling.
+ if (jit_.get() != nullptr && jit_options_->GetSaveProfilingInfo() &&
+ !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) {
+ std::vector<std::string> dex_filenames;
+ Split(class_path_string_, ':', &dex_filenames);
+ RegisterAppInfo(dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath());
+ }
+
return true;
}
@@ -2410,18 +2422,6 @@
LOG(WARNING) << "Failed to create JIT " << error_msg;
return;
}
-
- // In case we have a profile path passed as a command line argument,
- // register the current class path for profiling now. Note that we cannot do
- // this before we create the JIT and having it here is the most convenient way.
- // This is used when testing profiles with dalvikvm command as there is no
- // framework to register the dex files for profiling.
- if (jit_options_->GetSaveProfilingInfo() &&
- !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) {
- std::vector<std::string> dex_filenames;
- Split(class_path_string_, ':', &dex_filenames);
- RegisterAppInfo(dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath());
- }
}
bool Runtime::CanRelocate() const {
diff --git a/test/1946-list-descriptors/descriptors.cc b/test/1946-list-descriptors/descriptors.cc
new file mode 100644
index 0000000..01b306d
--- /dev/null
+++ b/test/1946-list-descriptors/descriptors.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 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 "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1946Descriptors {
+
+typedef jvmtiError (*GetDescriptorList)(jvmtiEnv* env, jobject loader, jint* cnt, char*** descs);
+
+struct DescriptorData {
+ GetDescriptorList get_descriptor_list;
+};
+
+template <typename T>
+static void Dealloc(T* t) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename ...Rest>
+static void Dealloc(T* t, Rest... rs) {
+ Dealloc(t);
+ Dealloc(rs...);
+}
+
+static void Cleanup(char** data, jint cnt) {
+ for (jint i = 0; i < cnt; i++) {
+ Dealloc(data[i]);
+ }
+ Dealloc(data);
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1946_getClassloaderDescriptors(
+ JNIEnv* env, jclass, jobject loader) {
+ DescriptorData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return nullptr;
+ }
+ if (data == nullptr || data->get_descriptor_list == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Alloc tracking data not initialized.");
+ return nullptr;
+ }
+ char** classes = nullptr;
+ jint cnt = -1;
+ if (JvmtiErrorToException(env, jvmti_env, data->get_descriptor_list(jvmti_env,
+ loader,
+ &cnt,
+ &classes))) {
+ return nullptr;
+ }
+ ScopedLocalRef<jobjectArray> arr(env, env->NewObjectArray(cnt,
+ env->FindClass("java/lang/String"),
+ nullptr));
+ if (env->ExceptionCheck()) {
+ Cleanup(classes, cnt);
+ return nullptr;
+ }
+
+ for (jint i = 0; i < cnt; i++) {
+ env->SetObjectArrayElement(arr.get(), i, env->NewStringUTF(classes[i]));
+ if (env->ExceptionCheck()) {
+ Cleanup(classes, cnt);
+ return nullptr;
+ }
+ }
+ Cleanup(classes, cnt);
+ return arr.release();
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+ for (jint i = 0; i < n_params; i++) {
+ Dealloc(params[i].name);
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1946_initializeTest(JNIEnv* env, jclass) {
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ DescriptorData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(DescriptorData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(DescriptorData));
+ // Get the extensions.
+ jint n_ext = 0;
+ jvmtiExtensionFunctionInfo* infos = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+ return;
+ }
+ for (jint i = 0; i < n_ext; i++) {
+ jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+ if (strcmp("com.android.art.class.get_class_loader_class_descriptors", cur_info->id) == 0) {
+ data->get_descriptor_list = reinterpret_cast<GetDescriptorList>(cur_info->func);
+ }
+ // Cleanup the cur_info
+ DeallocParams(cur_info->params, cur_info->param_count);
+ Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+ }
+ // Cleanup the array.
+ Dealloc(infos);
+ if (data->get_descriptor_list == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions.");
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data));
+ return;
+}
+
+} // namespace Test1946Descriptors
+} // namespace art
diff --git a/test/1946-list-descriptors/expected.txt b/test/1946-list-descriptors/expected.txt
new file mode 100644
index 0000000..53e0935
--- /dev/null
+++ b/test/1946-list-descriptors/expected.txt
@@ -0,0 +1 @@
+Passed!
diff --git a/test/1946-list-descriptors/info.txt b/test/1946-list-descriptors/info.txt
new file mode 100644
index 0000000..924e0b3
--- /dev/null
+++ b/test/1946-list-descriptors/info.txt
@@ -0,0 +1 @@
+Tests the jvmti-extension to get the classes contained in class-loaders.
diff --git a/test/1946-list-descriptors/run b/test/1946-list-descriptors/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1946-list-descriptors/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1946-list-descriptors/src-art/art/Test1946.java b/test/1946-list-descriptors/src-art/art/Test1946.java
new file mode 100644
index 0000000..3e5ec65
--- /dev/null
+++ b/test/1946-list-descriptors/src-art/art/Test1946.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.*;
+import java.lang.reflect.*;
+import java.nio.ByteBuffer;
+import dalvik.system.InMemoryDexClassLoader;
+
+public class Test1946 {
+ // Base64 encoded dex file containing the following classes. Note the class E cannot be loaded.
+ // public class A {}
+ // public class B {}
+ // public class C {}
+ // public class D {}
+ // public class E extends ClassNotThere {}
+ private static final byte[] TEST_CLASSES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDzTO8rVDlKlz80vQF4NLYV5MjMMjHlOtRoAwAAcAAAAHhWNBIAAAAAAAAAAOACAAAO" +
+ "AAAAcAAAAAgAAACoAAAAAQAAAMgAAAAAAAAAAAAAAAcAAADUAAAABQAAAAwBAAC8AQAArAEAACQC" +
+ "AAAsAgAANAIAADwCAABEAgAATAIAAFQCAABZAgAAXgIAAGMCAAB0AgAAeQIAAH4CAACSAgAABgAA" +
+ "AAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAANAAAABwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA" +
+ "AgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAB" +
+ "AAAAAAAAAK4CAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAIAAAAAAAAAuAIAAAAAAAACAAAAAAAAAAYA" +
+ "AAAAAAAAAwAAAAAAAADCAgAAAAAAAAQAAAAAAAAABgAAAAAAAAAEAAAAAAAAAMwCAAAAAAAABQAA" +
+ "AAAAAAADAAAAAAAAAAUAAAAAAAAA1gIAAAAAAAABAAEAAQAAAJUCAAAEAAAAcBAGAAAADgABAAEA" +
+ "AQAAAJoCAAAEAAAAcBAGAAAADgABAAEAAQAAAJ8CAAAEAAAAcBAGAAAADgABAAEAAQAAAKQCAAAE" +
+ "AAAAcBAGAAAADgABAAEAAQAAAKkCAAAEAAAAcBADAAAADgAGPGluaXQ+AAZBLmphdmEABkIuamF2" +
+ "YQAGQy5qYXZhAAZELmphdmEABkUuamF2YQADTEE7AANMQjsAA0xDOwAPTENsYXNzTm90VGhlcmU7" +
+ "AANMRDsAA0xFOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEABw4AAQAHDgABAAcOAAEABw4AAQAH" +
+ "DgAAAAEAAICABKwDAAABAAGAgATEAwAAAQACgIAE3AMAAAEABICABPQDAAABAAWAgASMBAsAAAAA" +
+ "AAAAAQAAAAAAAAABAAAADgAAAHAAAAACAAAACAAAAKgAAAADAAAAAQAAAMgAAAAFAAAABwAAANQA" +
+ "AAAGAAAABQAAAAwBAAABIAAABQAAAKwBAAACIAAADgAAACQCAAADIAAABQAAAJUCAAAAIAAABQAA" +
+ "AK4CAAAAEAAAAQAAAOACAAA=");
+ public class TMP1 {}
+ public class TMP2 {}
+ public class TMP3 extends ArrayList {}
+
+ private static void check(boolean b, String msg) {
+ if (!b) {
+ throw new Error("Test failed! " + msg);
+ }
+ }
+
+ private static <T> void checkEq(T[] full, T[] sub, String msg) {
+ List<T> f = Arrays.asList(full);
+ check(full.length == sub.length, "not equal length");
+ msg = Arrays.toString(full) + " is not same as " + Arrays.toString(sub) + ": " + msg;
+ check(Arrays.asList(full).containsAll(Arrays.asList(sub)), msg);
+ }
+
+ private static <T> void checkSubset(T[] full, T[] sub, String msg) {
+ msg = Arrays.toString(full) + " does not contain all of " + Arrays.toString(sub) + ": " + msg;
+ check(Arrays.asList(full).containsAll(Arrays.asList(sub)), msg);
+ }
+
+ public static void run() throws Exception {
+ initializeTest();
+ // Check a few random classes in BCP.
+ checkSubset(getClassloaderDescriptors(null),
+ new String[] { "Ljava/lang/String;", "Ljava/util/TreeSet;" },
+ "Missing entries for null classloader.");
+ // Make sure that null is the same as BootClassLoader
+ checkEq(getClassloaderDescriptors(null),
+ getClassloaderDescriptors(Object.class.getClassLoader()), "Object not in bcp!");
+ // Check the current class loader gets expected classes.
+ checkSubset(getClassloaderDescriptors(Test1946.class.getClassLoader()),
+ new String[] {
+ "Lart/Test1946;",
+ "Lart/Test1946$TMP1;",
+ "Lart/Test1946$TMP2;",
+ "Lart/Test1946$TMP3;"
+ },
+ "Missing entries for current class classloader.");
+ // Check that the result is exactly what we expect and includes classes that fail verification.
+ checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
+ ClassLoader.getSystemClassLoader())),
+ new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+ "Unexpected classes in custom classloader");
+ checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
+ Object.class.getClassLoader())),
+ new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+ "Unexpected classes in custom classloader");
+ checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
+ Test1946.class.getClassLoader())),
+ new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+ "Unexpected classes in custom classloader");
+ // Check we only get 1 copy of each descriptor.
+ checkEq(getClassloaderDescriptors(makeClassLoaderFrom(Arrays.asList(TEST_CLASSES, TEST_CLASSES),
+ Test1946.class.getClassLoader())),
+ new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+ "Unexpected classes in custom classloader");
+ System.out.println("Passed!");
+ }
+
+ private static ClassLoader makeClassLoaderFrom(byte[] data, ClassLoader parent) throws Exception {
+ return new InMemoryDexClassLoader(ByteBuffer.wrap(data), parent);
+ }
+
+ private static ClassLoader makeClassLoaderFrom(List<byte[]> data, ClassLoader parent)
+ throws Exception {
+ ArrayList<ByteBuffer> bufs = new ArrayList<>();
+ for (byte[] d : data) {
+ bufs.add(ByteBuffer.wrap(d));
+ }
+ return new InMemoryDexClassLoader(bufs.toArray(new ByteBuffer[0]), parent);
+ }
+
+ private static native void initializeTest();
+ private static native String[] getClassloaderDescriptors(ClassLoader loader);
+}
diff --git a/test/1946-list-descriptors/src/Main.java b/test/1946-list-descriptors/src/Main.java
new file mode 100644
index 0000000..7d6f7ce
--- /dev/null
+++ b/test/1946-list-descriptors/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1946.run();
+ }
+}
diff --git a/test/1946-list-descriptors/src/art/Test1946.java b/test/1946-list-descriptors/src/art/Test1946.java
new file mode 100644
index 0000000..9636957
--- /dev/null
+++ b/test/1946-list-descriptors/src/art/Test1946.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Test1946 {
+ public static void run() {
+ System.out.println("Failed! This should not be run. The test is in src-art/art/Test1946.java");
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 4e79b75..72cdd41 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -276,6 +276,7 @@
"1941-dispose-stress/dispose_stress.cc",
"1942-suspend-raw-monitor-exit/native_suspend_monitor.cc",
"1943-suspend-raw-monitor-wait/native_suspend_monitor.cc",
+ "1946-list-descriptors/descriptors.cc",
],
// Use NDK-compatible headers for ctstiagent.
header_libs: [