diff options
77 files changed, 1178 insertions, 543 deletions
diff --git a/build/art.go b/build/art.go index 59480a0d0f..3dabce3975 100644 --- a/build/art.go +++ b/build/art.go @@ -278,6 +278,7 @@ func init() { android.RegisterModuleType("art_cc_test", artTest) android.RegisterModuleType("art_cc_test_library", artTestLibrary) android.RegisterModuleType("art_cc_defaults", artDefaultsFactory) + android.RegisterModuleType("libart_cc_defaults", libartDefaultsFactory) android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory) android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory) } @@ -304,6 +305,33 @@ func artDefaultsFactory() android.Module { return module } +func libartDefaultsFactory() android.Module { + c := &codegenProperties{} + module := cc.DefaultsFactory(c) + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + codegen(ctx, c, true) + + type props struct { + Target struct { + Android struct { + Shared_libs []string + } + } + } + + p := &props{} + // TODO: express this in .bp instead b/79671158 + if !envTrue(ctx, "ART_TARGET_LINUX") { + p.Target.Android.Shared_libs = []string { + "libmetricslogger", + } + } + ctx.AppendProperties(p) + }) + + return module +} + func artLibrary() android.Module { m, _ := cc.NewLibrary(android.HostAndDeviceSupported) module := m.Init() diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index f57333741c..fb556f435a 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -771,7 +771,7 @@ void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary( void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) { LocationSummary* locations = method_type->GetLocations(); - MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex()); + MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex().index_); CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>(); InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc()); } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 35a39456a2..0e20a65a2d 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1055,7 +1055,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, uint32_t dex_pc, uint32_t method_idx, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -1896,17 +1896,17 @@ bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) } } -void HInstructionBuilder::BuildLoadMethodHandle(uint16_t proto_idx, uint32_t dex_pc) { +void HInstructionBuilder::BuildLoadMethodHandle(uint16_t method_handle_index, uint32_t dex_pc) { const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - HLoadMethodHandle* load_method_handle = - new (allocator_) HLoadMethodHandle(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + HLoadMethodHandle* load_method_handle = new (allocator_) HLoadMethodHandle( + graph_->GetCurrentMethod(), method_handle_index, dex_file, dex_pc); AppendInstruction(load_method_handle); } -void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) { +void HInstructionBuilder::BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc) { const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); HLoadMethodType* load_method_type = - new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc); + new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_index, dex_file, dex_pc); AppendInstruction(load_method_type); } @@ -2189,7 +2189,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INVOKE_POLYMORPHIC: { uint16_t method_idx = instruction.VRegB_45cc(); - uint16_t proto_idx = instruction.VRegH_45cc(); + dex::ProtoIndex proto_idx(instruction.VRegH_45cc()); uint32_t number_of_vreg_arguments = instruction.VRegA_45cc(); uint32_t args[5]; instruction.GetVarArgs(args); @@ -2205,7 +2205,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::INVOKE_POLYMORPHIC_RANGE: { uint16_t method_idx = instruction.VRegB_4rcc(); - uint16_t proto_idx = instruction.VRegH_4rcc(); + dex::ProtoIndex proto_idx(instruction.VRegH_4rcc()); uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc(); uint32_t register_index = instruction.VRegC_4rcc(); return BuildInvokePolymorphic(instruction, @@ -2949,7 +2949,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::CONST_METHOD_TYPE: { - uint16_t proto_idx = instruction.VRegB_21c(); + dex::ProtoIndex proto_idx(instruction.VRegB_21c()); BuildLoadMethodType(proto_idx, dex_pc); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 95ffa6b054..9d886a8ef2 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -178,7 +178,7 @@ class HInstructionBuilder : public ValueObject { bool BuildInvokePolymorphic(const Instruction& instruction, uint32_t dex_pc, uint32_t method_idx, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, uint32_t number_of_vreg_arguments, bool is_range, uint32_t* args, @@ -240,11 +240,11 @@ class HInstructionBuilder : public ValueObject { bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); - // Builds a `HLoadMethodHandle` loading the given `method_handle_idx`. + // Builds a `HLoadMethodHandle` loading the given `method_handle_index`. void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc); - // Builds a `HLoadMethodType` loading the given `proto_idx`. - void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc); + // Builds a `HLoadMethodType` loading the given `proto_index`. + void BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc); // Returns the outer-most compiling method's class. ObjPtr<mirror::Class> GetOutermostCompilingClass() const; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index a7c2d0b125..e786502dee 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6548,7 +6548,7 @@ class HLoadMethodHandle FINAL : public HInstruction { class HLoadMethodType FINAL : public HInstruction { public: HLoadMethodType(HCurrentMethod* current_method, - uint16_t proto_idx, + dex::ProtoIndex proto_index, const DexFile& dex_file, uint32_t dex_pc) : HInstruction(kLoadMethodType, @@ -6556,7 +6556,7 @@ class HLoadMethodType FINAL : public HInstruction { SideEffectsForArchRuntimeCalls(), dex_pc), special_input_(HUserRecord<HInstruction*>(current_method)), - proto_idx_(proto_idx), + proto_index_(proto_index), dex_file_(dex_file) { } @@ -6568,7 +6568,7 @@ class HLoadMethodType FINAL : public HInstruction { bool IsClonable() const OVERRIDE { return true; } - uint16_t GetProtoIndex() const { return proto_idx_; } + dex::ProtoIndex GetProtoIndex() const { return proto_index_; } const DexFile& GetDexFile() const { return dex_file_; } @@ -6585,7 +6585,7 @@ class HLoadMethodType FINAL : public HInstruction { // The special input is the HCurrentMethod for kRuntimeCall. HUserRecord<HInstruction*> special_input_; - const uint16_t proto_idx_; + const dex::ProtoIndex proto_index_; const DexFile& dex_file_; }; diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 9536381ed0..e72d49e05f 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -786,11 +786,10 @@ static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) { static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, const Instruction* pDecInsn, size_t bufSize) { - static const u4 kInvalidIndex = std::numeric_limits<u4>::max(); std::unique_ptr<char[]> buf(new char[bufSize]); // Determine index and width of the string. u4 index = 0; - u4 secondary_index = kInvalidIndex; + u2 secondary_index = 0; u4 width = 4; switch (Instruction::FormatOf(pDecInsn->Opcode())) { // SOME NOT SUPPORTED: @@ -898,7 +897,7 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, signature.ToString().c_str()); } if (secondary_index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index); + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(secondary_index)); const Signature signature = pDexFile->GetProtoSignature(protoId); proto = signature.ToString(); } @@ -916,7 +915,7 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, break; case Instruction::kIndexProtoRef: if (index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(index); + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(dex::ProtoIndex(index)); const Signature signature = pDexFile->GetProtoSignature(protoId); const std::string& proto = signature.ToString(); outSize = snprintf(buf.get(), bufSize, "%s // proto@%0*x", proto.c_str(), width, index); @@ -1705,7 +1704,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i); const char* method_name = pDexFile->StringDataByIdx(method_name_idx); it.Next(); - uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i); + dex::ProtoIndex method_type_idx = static_cast<dex::ProtoIndex>(it.GetJavaValue().i); const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx); std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString(); it.Next(); @@ -1763,7 +1762,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) { break; case EncodedArrayValueIterator::ValueType::kMethodType: { type = "MethodType"; - uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i); + dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(it.GetJavaValue().i); const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx); value = pDexFile->GetProtoSignature(proto_id).ToString(); break; diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 1525d537b7..b7d9db6da5 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -332,7 +332,7 @@ void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { } void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { - const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i); + const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(dex::ProtoIndex(i)); const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id); TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); @@ -353,7 +353,7 @@ void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i); MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), - GetProtoId(disk_method_id.proto_idx_), + GetProtoId(disk_method_id.proto_idx_.index_), GetStringId(disk_method_id.name_idx_.index_)); AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); } diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 065f3eb07a..c8a06ed3b0 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -19,11 +19,13 @@ cc_defaults { defaults: ["art_defaults"], host_supported: true, srcs: [ + "arch/instruction_set.cc", "base/allocator.cc", "base/arena_allocator.cc", "base/arena_bit_vector.cc", "base/bit_vector.cc", "base/file_magic.cc", + "base/file_utils.cc", "base/hex_dump.cc", "base/logging.cc", "base/malloc_arena_pool.cc", @@ -80,6 +82,7 @@ gensrcs { cmd: "$(location generate_operator_out) art/libartbase $(in) > $(out)", tools: ["generate_operator_out"], srcs: [ + "arch/instruction_set.h", "base/allocator.h", "base/callee_save_type.h", "base/unix_file/fd_file.h", @@ -141,12 +144,14 @@ art_cc_test { "art_gtest_defaults", ], srcs: [ + "arch/instruction_set_test.cc", "base/arena_allocator_test.cc", "base/bit_field_test.cc", "base/bit_string_test.cc", "base/bit_struct_test.cc", "base/bit_utils_test.cc", "base/bit_vector_test.cc", + "base/file_utils_test.cc", "base/hash_set_test.cc", "base/hex_dump_test.cc", "base/histogram_test.cc", diff --git a/runtime/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc index b848eb27fc..a187663062 100644 --- a/runtime/arch/instruction_set.cc +++ b/libartbase/arch/instruction_set.cc @@ -16,8 +16,6 @@ #include "instruction_set.h" -// Explicitly include our own elf.h to avoid Linux and other dependencies. -#include "../elf.h" #include "android-base/logging.h" #include "base/bit_utils.h" #include "base/globals.h" @@ -83,29 +81,6 @@ InstructionSet GetInstructionSetFromString(const char* isa_str) { return InstructionSet::kNone; } -InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) { - switch (e_machine) { - case EM_ARM: - return InstructionSet::kArm; - case EM_AARCH64: - return InstructionSet::kArm64; - case EM_386: - return InstructionSet::kX86; - case EM_X86_64: - return InstructionSet::kX86_64; - case EM_MIPS: { - if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || - (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { - return InstructionSet::kMips; - } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { - return InstructionSet::kMips64; - } - break; - } - } - return InstructionSet::kNone; -} - size_t GetInstructionSetAlignment(InstructionSet isa) { switch (isa) { case InstructionSet::kArm: diff --git a/runtime/arch/instruction_set.h b/libartbase/arch/instruction_set.h index 6434005dda..06bd53a6a9 100644 --- a/runtime/arch/instruction_set.h +++ b/libartbase/arch/instruction_set.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ -#define ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ +#ifndef ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ +#define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ #include <iosfwd> #include <string> @@ -89,8 +89,6 @@ const char* GetInstructionSetString(InstructionSet isa); // Note: Returns kNone when the string cannot be parsed to a known value. InstructionSet GetInstructionSetFromString(const char* instruction_set); -InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags); - // Fatal logging out of line to keep the header clean of logging.h. NO_RETURN void InstructionSetAbort(InstructionSet isa); @@ -299,4 +297,4 @@ static inline TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) { } // namespace art -#endif // ART_RUNTIME_ARCH_INSTRUCTION_SET_H_ +#endif // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_ diff --git a/runtime/arch/instruction_set_test.cc b/libartbase/arch/instruction_set_test.cc index 12a117d7a1..12a117d7a1 100644 --- a/runtime/arch/instruction_set_test.cc +++ b/libartbase/arch/instruction_set_test.cc diff --git a/runtime/base/file_utils.cc b/libartbase/base/file_utils.cc index 537216c198..9450e1e8c1 100644 --- a/runtime/base/file_utils.cc +++ b/libartbase/base/file_utils.cc @@ -47,7 +47,6 @@ #include "base/os.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" -#include "dex/dex_file_loader.h" #if defined(__APPLE__) #include <crt_externs.h> @@ -64,6 +63,8 @@ namespace art { using android::base::StringAppendF; using android::base::StringPrintf; +static constexpr const char* kClassesDex = "classes.dex"; + bool ReadFileToString(const std::string& file_name, std::string* result) { File file(file_name, O_RDONLY, false); if (!file.IsOpened()) { @@ -224,7 +225,7 @@ bool GetDalvikCacheFilename(const char* location, const char* cache_location, !android::base::EndsWith(location, ".art") && !android::base::EndsWith(location, ".oat")) { cache_file += "/"; - cache_file += DexFileLoader::kClassesDex; + cache_file += kClassesDex; } std::replace(cache_file.begin(), cache_file.end(), '/', '@'); *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str()); diff --git a/runtime/base/file_utils.h b/libartbase/base/file_utils.h index d4f6c576c0..063393bd3b 100644 --- a/runtime/base/file_utils.h +++ b/libartbase/base/file_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_BASE_FILE_UTILS_H_ -#define ART_RUNTIME_BASE_FILE_UTILS_H_ +#ifndef ART_LIBARTBASE_BASE_FILE_UTILS_H_ +#define ART_LIBARTBASE_BASE_FILE_UTILS_H_ #include <stdlib.h> @@ -46,6 +46,7 @@ std::string GetDefaultBootImageLocation(std::string* error_msg); // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache // could not be found. std::string GetDalvikCache(const char* subdir); + // Return true if we found the dalvik cache and stored it in the dalvik_cache argument. // have_android_data will be set to true if we have an ANDROID_DATA that exists, // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present. @@ -79,4 +80,4 @@ bool LocationIsOnSystemFramework(const char* location); } // namespace art -#endif // ART_RUNTIME_BASE_FILE_UTILS_H_ +#endif // ART_LIBARTBASE_BASE_FILE_UTILS_H_ diff --git a/runtime/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc index e74dfe5e64..e74dfe5e64 100644 --- a/runtime/base/file_utils_test.cc +++ b/libartbase/base/file_utils_test.cc diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index d1b32007c3..e78e8d7a44 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -127,7 +127,7 @@ inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) con return StringByTypeIdx(proto_id.return_type_idx_); } -inline const char* DexFile::GetShorty(uint32_t proto_idx) const { +inline const char* DexFile::GetShorty(dex::ProtoIndex proto_idx) const { const ProtoId& proto_id = GetProtoId(proto_idx); return StringDataByIdx(proto_id.shorty_idx_); } diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc index 8cfee6655e..9de260c862 100644 --- a/libdexfile/dex/dex_file.cc +++ b/libdexfile/dex/dex_file.cc @@ -281,7 +281,7 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); const dex::StringIndex name_idx = GetIndexForStringId(name); - const uint16_t proto_idx = GetIndexForProtoId(signature); + const dex::ProtoIndex proto_idx = GetIndexForProtoId(signature); int32_t lo = 0; int32_t hi = NumMethodIds() - 1; while (hi >= lo) { @@ -373,7 +373,8 @@ const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx, int32_t hi = NumProtoIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const DexFile::ProtoId& proto = GetProtoId(mid); + const dex::ProtoIndex proto_idx = static_cast<dex::ProtoIndex>(mid); + const DexFile::ProtoId& proto = GetProtoId(proto_idx); int compare = return_type_idx.index_ - proto.return_type_idx_.index_; if (compare == 0) { DexFileParameterIterator it(*this, proto); @@ -777,6 +778,11 @@ void EncodedArrayValueIterator::Next() { namespace dex { +std::ostream& operator<<(std::ostream& os, const ProtoIndex& index) { + os << "ProtoIndex[" << index.index_ << "]"; + return os; +} + std::ostream& operator<<(std::ostream& os, const StringIndex& index) { os << "StringIndex[" << index.index_ << "]"; return os; diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 4ca735ad66..87d2c48ff1 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -189,7 +189,7 @@ class DexFile { // Raw method_id_item. struct MethodId { dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - uint16_t proto_idx_; // index into proto_ids_ array for method prototype + dex::ProtoIndex proto_idx_; // index into proto_ids_ array for method prototype dex::StringIndex name_idx_; // index into string_ids_ array for method name private: @@ -692,15 +692,15 @@ class DexFile { } // Returns the ProtoId at the specified index. - const ProtoId& GetProtoId(uint16_t idx) const { - DCHECK_LT(idx, NumProtoIds()) << GetLocation(); - return proto_ids_[idx]; + const ProtoId& GetProtoId(dex::ProtoIndex idx) const { + DCHECK_LT(idx.index_, NumProtoIds()) << GetLocation(); + return proto_ids_[idx.index_]; } - uint16_t GetIndexForProtoId(const ProtoId& proto_id) const { + dex::ProtoIndex GetIndexForProtoId(const ProtoId& proto_id) const { CHECK_GE(&proto_id, proto_ids_) << GetLocation(); CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation(); - return &proto_id - proto_ids_; + return dex::ProtoIndex(&proto_id - proto_ids_); } // Looks up a proto id for a given return type and signature type list @@ -722,7 +722,7 @@ class DexFile { const Signature CreateSignature(const StringPiece& signature) const; // Returns the short form method descriptor for the given prototype. - const char* GetShorty(uint32_t proto_idx) const; + const char* GetShorty(dex::ProtoIndex proto_idx) const; const TypeList* GetProtoParameters(const ProtoId& proto_id) const { return DataPointer<TypeList>(proto_id.parameters_off_); diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h index 2bb70ff261..d4fb3de504 100644 --- a/libdexfile/dex/dex_file_types.h +++ b/libdexfile/dex/dex_file_types.h @@ -25,72 +25,66 @@ namespace dex { constexpr uint32_t kDexNoIndex = 0xFFFFFFFF; -class StringIndex { +template<typename T> +class DexIndex { public: - uint32_t index_; + T index_; - constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} - explicit constexpr StringIndex(uint32_t idx) : index_(idx) {} + constexpr DexIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} + explicit constexpr DexIndex(T idx) : index_(idx) {} bool IsValid() const { return index_ != std::numeric_limits<decltype(index_)>::max(); } - static StringIndex Invalid() { - return StringIndex(std::numeric_limits<decltype(index_)>::max()); + static constexpr DexIndex Invalid() { + return DexIndex(std::numeric_limits<decltype(index_)>::max()); } - - bool operator==(const StringIndex& other) const { + bool operator==(const DexIndex& other) const { return index_ == other.index_; } - bool operator!=(const StringIndex& other) const { + bool operator!=(const DexIndex& other) const { return index_ != other.index_; } - bool operator<(const StringIndex& other) const { + bool operator<(const DexIndex& other) const { return index_ < other.index_; } - bool operator<=(const StringIndex& other) const { + bool operator<=(const DexIndex& other) const { return index_ <= other.index_; } - bool operator>(const StringIndex& other) const { + bool operator>(const DexIndex& other) const { return index_ > other.index_; } - bool operator>=(const StringIndex& other) const { + bool operator>=(const DexIndex& other) const { return index_ >= other.index_; } }; -std::ostream& operator<<(std::ostream& os, const StringIndex& index); -class TypeIndex { +class ProtoIndex : public DexIndex<uint16_t> { public: - uint16_t index_; - - constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} - explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {} - - bool IsValid() const { - return index_ != std::numeric_limits<decltype(index_)>::max(); - } - static TypeIndex Invalid() { - return TypeIndex(std::numeric_limits<decltype(index_)>::max()); + ProtoIndex() {} + explicit constexpr ProtoIndex(uint16_t index) : DexIndex<decltype(index_)>(index) {} + static constexpr ProtoIndex Invalid() { + return ProtoIndex(std::numeric_limits<decltype(index_)>::max()); } +}; +std::ostream& operator<<(std::ostream& os, const ProtoIndex& index); - bool operator==(const TypeIndex& other) const { - return index_ == other.index_; - } - bool operator!=(const TypeIndex& other) const { - return index_ != other.index_; - } - bool operator<(const TypeIndex& other) const { - return index_ < other.index_; - } - bool operator<=(const TypeIndex& other) const { - return index_ <= other.index_; - } - bool operator>(const TypeIndex& other) const { - return index_ > other.index_; +class StringIndex : public DexIndex<uint32_t> { + public: + StringIndex() {} + explicit constexpr StringIndex(uint32_t index) : DexIndex<decltype(index_)>(index) {} + static constexpr StringIndex Invalid() { + return StringIndex(std::numeric_limits<decltype(index_)>::max()); } - bool operator>=(const TypeIndex& other) const { - return index_ >= other.index_; +}; +std::ostream& operator<<(std::ostream& os, const StringIndex& index); + +class TypeIndex : public DexIndex<uint16_t> { + public: + TypeIndex() {} + explicit constexpr TypeIndex(uint16_t index) : DexIndex<uint16_t>(index) {} + static constexpr TypeIndex Invalid() { + return TypeIndex(std::numeric_limits<decltype(index_)>::max()); } }; std::ostream& operator<<(std::ostream& os, const TypeIndex& index); @@ -100,15 +94,21 @@ std::ostream& operator<<(std::ostream& os, const TypeIndex& index); namespace std { +template<> struct hash<art::dex::ProtoIndex> { + size_t operator()(const art::dex::ProtoIndex& index) const { + return hash<decltype(index.index_)>()(index.index_); + } +}; + template<> struct hash<art::dex::StringIndex> { size_t operator()(const art::dex::StringIndex& index) const { - return hash<uint32_t>()(index.index_); + return hash<decltype(index.index_)>()(index.index_); } }; template<> struct hash<art::dex::TypeIndex> { size_t operator()(const art::dex::TypeIndex& index) const { - return hash<uint16_t>()(index.index_); + return hash<decltype(index.index_)>()(index.index_); } }; diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc index a32f64e49f..78db8b9a35 100644 --- a/libdexfile/dex/dex_file_verifier.cc +++ b/libdexfile/dex/dex_file_verifier.cc @@ -127,8 +127,9 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const return &dex_file_->GetMethodId(idx); } -const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) { +const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(dex::ProtoIndex idx, + const char* err_string) { + if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumProtoIds(), err_string))) { return nullptr; } return &dex_file_->GetProtoId(idx); @@ -2208,7 +2209,7 @@ bool DexFileVerifier::CheckInterMethodIdItem() { } // Check that the proto id is valid. - if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(), + if (UNLIKELY(!CheckIndex(item->proto_idx_.index_, dex_file_->NumProtoIds(), "inter_method_id_item proto_idx"))) { return false; } diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h index 04d8d71fa8..43d1093809 100644 --- a/libdexfile/dex/dex_file_verifier.h +++ b/libdexfile/dex/dex_file_verifier.h @@ -164,7 +164,7 @@ class DexFileVerifier { // error if not. If there is an error, null is returned. const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt); const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt); - const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt); + const DexFile::ProtoId* CheckLoadProtoId(dex::ProtoIndex idx, const char* error_fmt); void ErrorStringPrintf(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc index f82081f8ee..c9bac0fef2 100644 --- a/libdexfile/dex/dex_file_verifier_test.cc +++ b/libdexfile/dex/dex_file_verifier_test.cc @@ -161,7 +161,7 @@ TEST_F(DexFileVerifierTest, MethodId) { "method_id_proto_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0)); - method_id->proto_idx_ = 0xFF; + method_id->proto_idx_ = dex::ProtoIndex(0xFF); }, "inter_method_id_item proto_idx"); @@ -1425,12 +1425,13 @@ TEST_F(DexFileVerifierTest, ProtoOrdering) { CHECK_LT(method_idx + 1u, dex_file->NumMethodIds()); CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_, dex_file->GetMethodId(method_idx + 1).name_idx_); - CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u, - dex_file->GetMethodId(method_idx + 1).proto_idx_); + CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u, + dex_file->GetMethodId(method_idx + 1).proto_idx_.index_); // Their return types should be the same. - uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; + dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx); - const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u); + dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u); + const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx); CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_); // And the first should not have any parameters while the second should have some. CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext()); diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc index 886218129e..8378211c43 100644 --- a/libdexfile/dex/dex_instruction.cc +++ b/libdexfile/dex/dex_instruction.cc @@ -467,10 +467,10 @@ std::string Instruction::DumpString(const DexFile* file) const { case k45cc: { uint32_t arg[kMaxVarArgRegs]; GetVarArgs(arg); - uint32_t method_idx = VRegB_45cc(); - uint32_t proto_idx = VRegH_45cc(); + uint16_t method_idx = VRegB_45cc(); + dex::ProtoIndex proto_idx(VRegH_45cc()); os << opcode << " {"; - for (int i = 0; i < VRegA_45cc(); ++i) { + for (uint32_t i = 0; i < VRegA_45cc(); ++i) { if (i != 0) { os << ", "; } @@ -478,7 +478,8 @@ std::string Instruction::DumpString(const DexFile* file) const { } os << "}"; if (file != nullptr) { - os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + os << ", " << file->PrettyMethod(method_idx) + << ", " << file->GetShorty(proto_idx) << " // "; } else { os << ", "; @@ -490,18 +491,19 @@ std::string Instruction::DumpString(const DexFile* file) const { switch (Opcode()) { case INVOKE_POLYMORPHIC_RANGE: { if (file != nullptr) { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); + uint16_t method_idx = VRegB_4rcc(); + dex::ProtoIndex proto_idx(VRegH_4rcc()); os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) - << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << "}, " << file->PrettyMethod(method_idx) + << ", " << file->GetShorty(dex::ProtoIndex(proto_idx)) << " // method@" << method_idx << ", proto@" << proto_idx; break; } } FALLTHROUGH_INTENDED; default: { - uint32_t method_idx = VRegB_4rcc(); - uint32_t proto_idx = VRegH_4rcc(); + uint16_t method_idx = VRegB_4rcc(); + dex::ProtoIndex proto_idx(VRegH_4rcc()); os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) << "}, method@" << method_idx << ", proto@" << proto_idx; } diff --git a/patchoat/Android.bp b/patchoat/Android.bp index 0e8e517cd4..1e2f328ffd 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -25,6 +25,7 @@ cc_defaults { }, }, shared_libs: [ + "libartbase", "libbase", "libcrypto", // For computing the digest of image file ], diff --git a/runtime/Android.bp b/runtime/Android.bp index 64e6796ba0..0540b2087e 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -23,7 +23,7 @@ JIT_DEBUG_REGISTER_CODE_LDFLAGS = [ "-Wl,--keep-unique,__dex_debug_register_code" ] -cc_defaults { +libart_cc_defaults { name: "libart_defaults", defaults: ["art_defaults"], host_supported: true, @@ -32,7 +32,6 @@ cc_defaults { "art_field.cc", "art_method.cc", "barrier.cc", - "base/file_utils.cc", "base/mem_map_arena_pool.cc", "base/mutex.cc", "base/quasi_atomic.cc", @@ -196,6 +195,7 @@ cc_defaults { "ti/agent.cc", "trace.cc", "transaction.cc", + "var_handles.cc", "vdex_file.cc", "verifier/instruction_flags.cc", "verifier/method_verifier.cc", @@ -207,7 +207,6 @@ cc_defaults { "well_known_classes.cc", "arch/context.cc", - "arch/instruction_set.cc", "arch/instruction_set_features.cc", "arch/memcmp16.cc", "arch/arm/instruction_set_features_arm.cc", @@ -396,7 +395,6 @@ cc_defaults { "libbacktrace", "liblz4", "liblog", - "libmetricslogger", // For atrace, properties, ashmem, set_sched_policy and socket_peer_is_trusted. "libcutils", // For common macros. @@ -420,7 +418,6 @@ gensrcs { cmd: "$(location generate_operator_out) art/runtime $(in) > $(out)", tools: ["generate_operator_out"], srcs: [ - "arch/instruction_set.h", "base/mutex.h", "class_loader_context.h", "class_status.h", @@ -531,7 +528,6 @@ art_cc_test { ], srcs: [ "arch/arch_test.cc", - "arch/instruction_set_test.cc", "arch/instruction_set_features_test.cc", "arch/memcmp16_test.cc", "arch/stub_test.cc", @@ -542,7 +538,6 @@ art_cc_test { "arch/x86/instruction_set_features_x86_test.cc", "arch/x86_64/instruction_set_features_x86_64_test.cc", "barrier_test.cc", - "base/file_utils_test.cc", "base/mutex_test.cc", "base/timing_logger_test.cc", "cha_test.cc", diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h index 8e8dde4c4c..f0c6d22ef2 100644 --- a/runtime/arch/code_offset.h +++ b/runtime/arch/code_offset.h @@ -21,9 +21,9 @@ #include <android-base/logging.h> +#include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/macros.h" -#include "instruction_set.h" namespace art { diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h index 5f1a507f7a..c31c927668 100644 --- a/runtime/arch/instruction_set_features.h +++ b/runtime/arch/instruction_set_features.h @@ -21,8 +21,8 @@ #include <ostream> #include <vector> +#include "arch/instruction_set.h" #include "base/macros.h" -#include "instruction_set.h" namespace art { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 87fcb20698..608e33cf65 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -710,6 +710,23 @@ bool ArtMethod::HasAnyCompiledCode() { return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr; } +void ArtMethod::SetNotIntrinsic() { + if (!IsIntrinsic()) { + return; + } + + // Query the hidden API access flags of the intrinsic. + HiddenApiAccessFlags::ApiList intrinsic_api_list = GetHiddenApiAccessFlags(); + + // Clear intrinsic-related access flags. + ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); + + // Re-apply hidden API access flags now that the method is not an intrinsic. + SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list)); + DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list); +} + + void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src), Size(image_pointer_size)); diff --git a/runtime/art_method.h b/runtime/art_method.h index acaa4a68a1..012d706756 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -194,9 +194,7 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccIntrinsicBits) >> kAccFlagsShift; } - void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_) { - ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); - } + void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_); bool IsCopied() { static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0, diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 9c8b4383dc..be9e08fbec 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8174,7 +8174,7 @@ ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr<mirror::Class> klass, ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType( Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); @@ -8236,7 +8236,7 @@ ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType( } ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, ArtMethod* referrer) { StackHandleScope<2> hs(self); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 60cff9ee37..52ecf82c86 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -416,14 +416,14 @@ class ClassLinker { // Resolve a method type with a given ID from the DexFile associated with a given DexCache // and ClassLoader, storing the result in the DexCache. ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self, - uint32_t proto_idx, + dex::ProtoIndex proto_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 37e074d552..9c2a40b50d 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -29,6 +29,7 @@ #include "instrumentation.h" #include "interpreter/shadow_frame.h" #include "interpreter/unstarted_runtime.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/object.h" #include "obj_ptr-inl.h" diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc index 274a6df702..d353c26b35 100644 --- a/runtime/dex/art_dex_file_loader_test.cc +++ b/runtime/dex/art_dex_file_loader_test.cc @@ -43,48 +43,7 @@ static void Copy(const std::string& src, const std::string& dst) { dst_stream << src_stream.rdbuf(); } -class ArtDexFileLoaderTest : public CommonRuntimeTest { - public: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); - - std::string dex_location = GetTestDexFileName("Main"); - std::string multidex_location = GetTestDexFileName("MultiDex"); - - data_location_path_ = android_data_ + "/foo.jar"; - system_location_path_ = GetAndroidRoot() + "/foo.jar"; - system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar"; - data_multi_location_path_ = android_data_ + "/multifoo.jar"; - system_multi_location_path_ = GetAndroidRoot() + "/multifoo.jar"; - system_framework_multi_location_path_ = GetAndroidRoot() + "/framework/multifoo.jar"; - - Copy(dex_location, data_location_path_); - Copy(dex_location, system_location_path_); - Copy(dex_location, system_framework_location_path_); - - Copy(multidex_location, data_multi_location_path_); - Copy(multidex_location, system_multi_location_path_); - Copy(multidex_location, system_framework_multi_location_path_); - } - - virtual void TearDown() { - remove(data_location_path_.c_str()); - remove(system_location_path_.c_str()); - remove(system_framework_location_path_.c_str()); - remove(data_multi_location_path_.c_str()); - remove(system_multi_location_path_.c_str()); - remove(system_framework_multi_location_path_.c_str()); - CommonRuntimeTest::TearDown(); - } - - protected: - std::string data_location_path_; - std::string system_location_path_; - std::string system_framework_location_path_; - std::string data_multi_location_path_; - std::string system_multi_location_path_; - std::string system_framework_multi_location_path_; -}; +class ArtDexFileLoaderTest : public CommonRuntimeTest {}; // TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and // the tests that depend upon them should be moved to dex_file_loader_test.cc @@ -286,7 +245,7 @@ TEST_F(ArtDexFileLoaderTest, FindTypeId) { TEST_F(ArtDexFileLoaderTest, FindProtoId) { for (size_t i = 0; i < java_lang_dex_file_->NumProtoIds(); i++) { - const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(i); + const DexFile::ProtoId& to_find = java_lang_dex_file_->GetProtoId(dex::ProtoIndex(i)); const DexFile::TypeList* to_find_tl = java_lang_dex_file_->GetProtoParameters(to_find); std::vector<dex::TypeIndex> to_find_types; if (to_find_tl != nullptr) { @@ -297,7 +256,7 @@ TEST_F(ArtDexFileLoaderTest, FindProtoId) { const DexFile::ProtoId* found = java_lang_dex_file_->FindProtoId(to_find.return_type_idx_, to_find_types); ASSERT_TRUE(found != nullptr); - EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), i); + EXPECT_EQ(java_lang_dex_file_->GetIndexForProtoId(*found), dex::ProtoIndex(i)); } } @@ -353,21 +312,24 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) { ASSERT_EQ(0, unlink(dex_location_sym.c_str())); } -TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir) { + // Load file from a non-system directory and check that it is not flagged as framework. + std::string data_location_path = android_data_ + "/foo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), data_location_path); + ArtDexFileLoader loader; - bool success; - std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - - // Load file from a non-system directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str())); - success = loader.Open(data_location_path_.c_str(), - data_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); + std::string error_msg; + bool success = loader.Open(data_location_path.c_str(), + data_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -375,15 +337,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(data_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir) { // Load file from a system, non-framework directory and check that it is not flagged as framework. - ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str())); - success = loader.Open(system_location_path_.c_str(), - system_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_location_path = GetAndroidRoot() + "/foo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), system_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_location_path.c_str(), + system_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -391,15 +365,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir) { // Load file from a system/framework directory and check that it is flagged as a framework dex. - ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str())); - success = loader.Open(system_framework_location_path_.c_str(), - system_framework_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar"; + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str())); + + Copy(GetTestDexFileName("Main"), system_framework_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_framework_location_path.c_str(), + system_framework_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GE(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); @@ -407,14 +393,27 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_framework_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir_MultiDex) { // Load multidex file from a non-system directory and check that it is not flagged as framework. - success = loader.Open(data_multi_location_path_.c_str(), - data_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); + std::string data_multi_location_path = android_data_ + "/multifoo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), data_multi_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(data_multi_location_path.c_str(), + data_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -422,15 +421,28 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(data_multi_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir_MultiDex) { // Load multidex file from a system, non-framework directory and check that it is not flagged // as framework. - success = loader.Open(system_multi_location_path_.c_str(), - system_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar"; + ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), system_multi_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_multi_location_path.c_str(), + system_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_FALSE(dex_file->IsPlatformDexFile()); @@ -438,19 +450,36 @@ TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) { dex_files.clear(); + ASSERT_EQ(0, remove(system_multi_location_path.c_str())); +} + +TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir_MultiDex) { // Load multidex file from a system/framework directory and check that it is flagged as a // framework dex. - success = loader.Open(system_framework_multi_location_path_.c_str(), - system_framework_multi_location_path_, - /* verify */ false, - /* verify_checksum */ false, - &error_msg, - &dex_files); - ASSERT_TRUE(success); + std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar"; + ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str())); + + Copy(GetTestDexFileName("MultiDex"), system_framework_multi_location_path); + + ArtDexFileLoader loader; + std::vector<std::unique_ptr<const DexFile>> dex_files; + std::string error_msg; + bool success = loader.Open(system_framework_multi_location_path.c_str(), + system_framework_multi_location_path, + /* verify */ false, + /* verify_checksum */ false, + &error_msg, + &dex_files); + ASSERT_TRUE(success) << error_msg; + ASSERT_GT(dex_files.size(), 1u); for (std::unique_ptr<const DexFile>& dex_file : dex_files) { ASSERT_TRUE(dex_file->IsPlatformDexFile()); } + + dex_files.clear(); + + ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str())); } } // namespace art diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 719b4af293..026b5da748 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1073,6 +1073,29 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) return true; } +static InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) { + switch (e_machine) { + case EM_ARM: + return InstructionSet::kArm; + case EM_AARCH64: + return InstructionSet::kArm64; + case EM_386: + return InstructionSet::kX86; + case EM_X86_64: + return InstructionSet::kX86_64; + case EM_MIPS: { + if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || + (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + return InstructionSet::kMips; + } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { + return InstructionSet::kMips64; + } + break; + } + } + return InstructionSet::kNone; +} + template <typename ElfTypes> bool ElfFileImpl<ElfTypes>::Load(File* file, bool executable, diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index a58946ae66..7fc8db375b 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -268,7 +268,7 @@ ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer, } ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, - uint32_t proto_idx) { + dex::ProtoIndex proto_idx) { Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::MethodType> method_type = referrer->GetDexCache()->GetResolvedMethodType(proto_idx); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 0a3b5dfc93..e33de9c45a 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -158,7 +158,7 @@ ObjPtr<mirror::MethodHandle> ResolveMethodHandleFromCode(ArtMethod* referrer, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx) +ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index cf9ddd8aa8..fa536c77a9 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -200,7 +200,7 @@ extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, proto_idx); + ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, dex::ProtoIndex(proto_idx)); return result.Ptr(); } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 39429c5b41..0a186f4dc5 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -42,6 +42,7 @@ #include "mirror/method_handle_impl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "mirror/var_handle.h" #include "oat_file.h" #include "oat_quick_method_header.h" #include "quick_exception_handler.h" @@ -49,6 +50,7 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread-inl.h" +#include "var_handles.h" #include "well_known_classes.h" namespace art { @@ -2766,7 +2768,7 @@ extern "C" uintptr_t artInvokePolymorphic( const Instruction& inst = caller_method->DexInstructions().InstructionAt(dex_pc); DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC || inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); - const uint32_t proto_idx = inst.VRegH(); + const dex::ProtoIndex proto_idx(inst.VRegH()); const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); const size_t shorty_length = strlen(shorty); static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. @@ -2789,13 +2791,6 @@ extern "C" uintptr_t artInvokePolymorphic( return static_cast<uintptr_t>('V'); } - // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996). - DCHECK_EQ(resolved_method->GetDeclaringClass(), - WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle)); - - Handle<mirror::MethodHandle> method_handle(hs.NewHandle( - ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get())))); - Handle<mirror::MethodType> method_type( hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method))); @@ -2835,24 +2830,43 @@ extern "C" uintptr_t artInvokePolymorphic( // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in // consecutive order. RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); - bool isExact = (jni::EncodeArtMethod(resolved_method) == - WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); + Intrinsics intrinsic = static_cast<Intrinsics>(resolved_method->GetIntrinsic()); bool success = false; - if (isExact) { - success = MethodHandleInvokeExact(self, + if (resolved_method->GetDeclaringClass() == mirror::MethodHandle::StaticClass()) { + Handle<mirror::MethodHandle> method_handle(hs.NewHandle( + ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get())))); + if (intrinsic == Intrinsics::kMethodHandleInvokeExact) { + success = MethodHandleInvokeExact(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); + } else { + DCHECK_EQ(static_cast<uint32_t>(intrinsic), + static_cast<uint32_t>(Intrinsics::kMethodHandleInvoke)); + success = MethodHandleInvoke(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); + } + } else { + DCHECK_EQ(mirror::VarHandle::StaticClass(), resolved_method->GetDeclaringClass()); + Handle<mirror::VarHandle> var_handle(hs.NewHandle( + ObjPtr<mirror::VarHandle>::DownCast(MakeObjPtr(receiver_handle.Get())))); + mirror::VarHandle::AccessMode access_mode = + mirror::VarHandle::GetAccessModeByIntrinsic(intrinsic); + success = VarHandleInvokeAccessor(self, *shadow_frame, - method_handle, + var_handle, method_type, + access_mode, &operands, result); - } else { - success = MethodHandleInvoke(self, - *shadow_frame, - method_handle, - method_type, - &operands, - result); } + DCHECK(success || self->IsExceptionPending()); // Pop transition record. diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index 9354d721b7..ee518ae1ea 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -14,8 +14,6 @@ * limitations under the License. */ -#include <metricslogger/metrics_logger.h> - #include "hidden_api.h" #include <nativehelper/scoped_local_ref.h> @@ -24,11 +22,14 @@ #include "thread-current-inl.h" #include "well_known_classes.h" +#ifdef ART_TARGET_ANDROID +#include <metricslogger/metrics_logger.h> using android::metricslogger::ComplexEventLogger; using android::metricslogger::ACTION_HIDDEN_API_ACCESSED; using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD; using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED; using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE; +#endif namespace art { namespace hiddenapi { @@ -137,6 +138,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ") << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")"; } +#ifdef ART_TARGET_ANDROID // Convert an AccessMethod enum to a value for logging from the proto enum. // This method may look odd (the enum values are current the same), but it // prevents coupling the internal enum to the proto enum (which should never @@ -156,8 +158,10 @@ inline static int32_t GetEnumValueForLog(AccessMethod access_method) { DCHECK(false); } } +#endif void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) { +#ifdef ART_TARGET_ANDROID if (access_method == kLinking || access_method == kNone) { // Linking warnings come from static analysis/compilation of the bytecode // and can contain false positives (i.e. code that is never run). We choose @@ -174,6 +178,10 @@ void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action act Dump(signature_str); log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str()); log_maker.Record(); +#else + UNUSED(access_method); + UNUSED(action_taken); +#endif } static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) { @@ -228,7 +236,7 @@ Action GetMemberActionImpl(T* member, } } - if (kIsTargetBuild) { + if (kIsTargetBuild && !kIsTargetLinux) { uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); // Assert that RAND_MAX is big enough, to ensure sampling below works as expected. static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index 8e21fd3b8f..580224e439 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -95,6 +95,22 @@ inline Action GetActionFromAccessFlags(HiddenApiAccessFlags::ApiList api_list) { } } +class ScopedHiddenApiEnforcementPolicySetting { + public: + explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy) + : initial_policy_(Runtime::Current()->GetHiddenApiEnforcementPolicy()) { + Runtime::Current()->SetHiddenApiEnforcementPolicy(new_policy); + } + + ~ScopedHiddenApiEnforcementPolicySetting() { + Runtime::Current()->SetHiddenApiEnforcementPolicy(initial_policy_); + } + + private: + const EnforcementPolicy initial_policy_; + DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiEnforcementPolicySetting); +}; + // Implementation details. DO NOT ACCESS DIRECTLY. namespace detail { diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 8a85ee41f8..5a50ec5586 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -24,7 +24,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "intrinsics_enum.h" #include "jit/jit.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "method_handles-inl.h" #include "method_handles.h" #include "mirror/array-inl.h" @@ -37,6 +37,7 @@ #include "stack.h" #include "thread-inl.h" #include "transaction.h" +#include "var_handles.h" #include "well_known_classes.h" namespace art { @@ -626,7 +627,8 @@ static bool DoMethodHandleInvokeCommon(Thread* self, // The vRegH value gives the index of the proto_id associated with this // signature polymorphic call site. - const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const dex::ProtoIndex callsite_proto_id(vRegH); // Call through to the classlinker and ask it to resolve the static type associated // with the callsite. This information is stored in the dex cache so it's @@ -724,38 +726,6 @@ bool DoMethodHandleInvoke(Thread* self, } } -static bool DoVarHandleInvokeChecked(Thread* self, - Handle<mirror::VarHandle> var_handle, - Handle<mirror::MethodType> callsite_type, - mirror::VarHandle::AccessMode access_mode, - ShadowFrame& shadow_frame, - InstructionOperands* operands, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType() - // which is only required if we need to convert argument and/or - // return types. - StackHandleScope<1> hs(self); - Handle<mirror::MethodType> accessor_type(hs.NewHandle( - var_handle->GetMethodTypeForAccessMode(self, access_mode))); - const size_t num_vregs = accessor_type->NumberOfVRegs(); - const int num_params = accessor_type->GetPTypes()->GetLength(); - ShadowFrameAllocaUniquePtr accessor_frame = - CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); - ShadowFrameGetter getter(shadow_frame, operands); - static const uint32_t kFirstDestinationReg = 0; - ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); - if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { - return false; - } - RangeInstructionOperands accessor_operands(kFirstDestinationReg, - kFirstDestinationReg + num_vregs); - if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { - return false; - } - return ConvertReturnValue(callsite_type, accessor_type, result); -} - static bool DoVarHandleInvokeCommon(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -768,59 +738,43 @@ static bool DoVarHandleInvokeCommon(Thread* self, return false; } - bool is_var_args = inst->HasVarArgs(); - const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); - ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC)); - if (receiver.IsNull()) { - ThrowNullPointerExceptionFromDexPC(); - return false; - } - StackHandleScope<2> hs(self); - Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr()))); - if (!var_handle->IsAccessModeSupported(access_mode)) { - ThrowUnsupportedOperationException(); - return false; - } - - const uint32_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); + bool is_var_args = inst->HasVarArgs(); + const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc(); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::MethodType> callsite_type(hs.NewHandle( - class_linker->ResolveMethodType(self, vRegH, shadow_frame.GetMethod()))); + class_linker->ResolveMethodType(self, dex::ProtoIndex(vRegH), shadow_frame.GetMethod()))); // This implies we couldn't resolve one or more types in this VarHandle. if (UNLIKELY(callsite_type == nullptr)) { CHECK(self->IsExceptionPending()); return false; } - if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) { - ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), - callsite_type.Get()); - return false; - } - + const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc(); + ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC)); + Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr()))); if (is_var_args) { uint32_t args[Instruction::kMaxVarArgRegs]; inst->GetVarArgs(args, inst_data); VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc()); NoReceiverInstructionOperands operands(&all_operands); - return DoVarHandleInvokeChecked(self, - var_handle, - callsite_type, - access_mode, - shadow_frame, - &operands, - result); + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } else { RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc()); NoReceiverInstructionOperands operands(&all_operands); - return DoVarHandleInvokeChecked(self, - var_handle, - callsite_type, - access_mode, - shadow_frame, - &operands, - result); + return VarHandleInvokeAccessor(self, + shadow_frame, + var_handle, + callsite_type, + access_mode, + &operands, + result); } } @@ -965,9 +919,10 @@ static bool GetArgumentForBootstrapMethod(Thread* self, StackHandleScope<2> hs(self); Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - uint32_t index = static_cast<uint32_t>(encoded_value->GetI()); + dex::ProtoIndex proto_idx(encoded_value->GetC()); ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ObjPtr<mirror::MethodType> o = cl->ResolveMethodType(self, index, dex_cache, class_loader); + ObjPtr<mirror::MethodType> o = + cl->ResolveMethodType(self, proto_idx, dex_cache, class_loader); if (UNLIKELY(o.IsNull())) { DCHECK(self->IsExceptionPending()); return false; diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 0818e06675..67a0349d7a 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -217,7 +217,7 @@ static inline ObjPtr<mirror::MethodHandle> ResolveMethodHandle(Thread* self, } static inline ObjPtr<mirror::MethodType> ResolveMethodType(Thread* self, - uint32_t method_type_index, + dex::ProtoIndex method_type_index, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 283885e522..5c7838cd66 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -565,7 +565,7 @@ void ExecuteSwitchImplCpp(SwitchImplContext* ctx) { PREAMBLE(); ClassLinker* cl = Runtime::Current()->GetClassLinker(); ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self, - inst->VRegB_21c(), + dex::ProtoIndex(inst->VRegB_21c()), shadow_frame.GetMethod()); if (UNLIKELY(mt == nullptr)) { HANDLE_PENDING_EXCEPTION(); diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 2a9ef2ce98..1b39a7422d 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -408,7 +408,8 @@ extern "C" size_t MterpConstMethodType(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::MethodType> mt = ResolveMethodType(self, index, shadow_frame->GetMethod()); + ObjPtr<mirror::MethodType> mt = + ResolveMethodType(self, dex::ProtoIndex(index), shadow_frame->GetMethod()); if (UNLIKELY(mt == nullptr)) { return true; } diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h index 25e34b2a74..5bd4f176f6 100644 --- a/runtime/jvalue-inl.h +++ b/runtime/jvalue-inl.h @@ -19,7 +19,7 @@ #include "jvalue.h" -#include "obj_ptr.h" +#include "obj_ptr-inl.h" namespace art { diff --git a/runtime/jvalue.h b/runtime/jvalue.h index 266abcf399..b42d995d5c 100644 --- a/runtime/jvalue.h +++ b/runtime/jvalue.h @@ -33,7 +33,7 @@ union PACKED(alignof(mirror::Object*)) JValue { // We default initialize JValue instances to all-zeros. JValue() : j(0) {} - template<typename T> static JValue FromPrimitive(T v); + template<typename T> ALWAYS_INLINE static JValue FromPrimitive(T v); int8_t GetB() const { return b; } void SetB(int8_t new_b) { @@ -62,6 +62,7 @@ union PACKED(alignof(mirror::Object*)) JValue { mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) { return l; } + ALWAYS_INLINE void SetL(ObjPtr<mirror::Object> new_l) REQUIRES_SHARED(Locks::mutator_lock_); int16_t GetS() const { return s; } diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 41c8384e0d..00a8c00880 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -22,7 +22,7 @@ #include "common_throws.h" #include "dex/dex_instruction.h" #include "interpreter/interpreter_common.h" -#include "jvalue.h" +#include "jvalue-inl.h" #include "mirror/class.h" #include "mirror/method_type.h" #include "mirror/object.h" diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 64ab78997f..1d45aaeb2e 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -20,7 +20,6 @@ #include "common_dex_operations.h" #include "jvalue-inl.h" -#include "jvalue.h" #include "mirror/emulated_stack_frame.h" #include "mirror/method_handle_impl-inl.h" #include "mirror/method_type.h" diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 7a4876c412..72f1443dfa 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -127,23 +127,23 @@ inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) { } } -inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) { +inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds()); - const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize; + DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds()); + const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize; DCHECK_LT(slot_idx, NumResolvedMethodTypes()); return slot_idx; } -inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) { +inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) { return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load( - std::memory_order_relaxed).GetObjectForIndex(proto_idx); + std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_); } -inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) { +inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) { DCHECK(resolved != nullptr); GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store( - MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed); + MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index d940964edb..9aff9ec49a 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -307,9 +307,9 @@ class MANAGED DexCache FINAL : public Object { ALWAYS_INLINE void ClearResolvedField(uint32_t idx, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_); - MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + MethodType* GetResolvedMethodType(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); - void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) + void SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_); CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_); @@ -432,7 +432,7 @@ class MANAGED DexCache FINAL : public Object { uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_); - uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); private: void Init(const DexFile* dex_file, diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index d2bff2c19a..97e0ce6684 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -169,9 +169,9 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) { const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed); - if (pair.index == method1_id.proto_idx_) { + if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) { ASSERT_EQ(method1_type.Get(), pair.object.Read()); - } else if (pair.index == method2_id.proto_idx_) { + } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) { ASSERT_EQ(method2_type.Get(), pair.object.Read()); } else { ASSERT_TRUE(false); diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc index b309f596fd..d31e06cf36 100644 --- a/runtime/mirror/var_handle.cc +++ b/runtime/mirror/var_handle.cc @@ -1425,21 +1425,24 @@ int32_t VarHandle::GetAccessModesBitMask() { return GetField32(AccessModesBitMaskOffset()); } -bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) { - StackHandleScope<3> hs(Thread::Current()); - Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType())); - Handle<VarHandle> vh(hs.NewHandle(this)); - Handle<Class> var_type(hs.NewHandle(vh->GetVarType())); +VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode, + MethodType* method_type) { + MatchKind match = MatchKind::kExact; + + ObjPtr<VarHandle> vh = this; + ObjPtr<Class> var_type = vh->GetVarType(); + ObjPtr<Class> mt_rtype = method_type->GetRType(); AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); - // Check return type first. - if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) { - // The result of the operation will be discarded. The return type - // of the VarHandle is immaterial. - } else { - ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get())); - if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) { - return false; + // Check return type first. If the return type of the method + // of the VarHandle is immaterial. + if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) { + ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type.Ptr()); + if (vh_rtype != mt_rtype) { + if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) { + return MatchKind::kNone; + } + match = MatchKind::kWithConversions; } } @@ -1447,21 +1450,25 @@ bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* metho ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters]; const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes, access_mode_template, - var_type.Get(), + var_type, GetCoordinateType0(), GetCoordinateType1()); if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) { - return false; + return MatchKind::kNone; } // Check the parameter types are compatible. ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes(); for (int32_t i = 0; i < vh_ptypes_count; ++i) { + if (mt_ptypes->Get(i) == vh_ptypes[i]) { + continue; + } if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) { - return false; + return MatchKind::kNone; } + match = MatchKind::kWithConversions; } - return true; + return match; } bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, @@ -1508,7 +1515,7 @@ bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode, MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, ObjPtr<VarHandle> var_handle, AccessMode access_mode) { - // This is a static as the var_handle might be moved by the GC during it's execution. + // This is a static method as the var_handle might be moved by the GC during it's execution. AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode); StackHandleScope<3> hs(self); @@ -1540,7 +1547,7 @@ MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self, AccessMode acces bool VarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { Class* klass = GetClass(); if (klass == FieldVarHandle::StaticClass()) { @@ -1671,7 +1678,7 @@ ArtField* FieldVarHandle::GetField() { bool FieldVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); ArtField* field = GetField(); @@ -1743,7 +1750,7 @@ GcRoot<Class> FieldVarHandle::static_class_; bool ArrayElementVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -1856,7 +1863,7 @@ bool ByteArrayViewVarHandle::GetNativeByteOrder() { bool ByteArrayViewVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); @@ -1965,7 +1972,7 @@ bool ByteBufferViewVarHandle::GetNativeByteOrder() { bool ByteBufferViewVarHandle::Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) { ShadowFrameGetter getter(*shadow_frame, operands); diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h index d46d900a8d..eb3704ee48 100644 --- a/runtime/mirror/var_handle.h +++ b/runtime/mirror/var_handle.h @@ -99,14 +99,16 @@ class MANAGED VarHandle : public Object { return (GetAccessModesBitMask() & (1u << static_cast<uint32_t>(accessMode))) != 0; } - // Returns true if the MethodType specified is compatible with the - // method type associated with the specified AccessMode. The - // supplied MethodType is assumed to be from the point of invocation - // so it is valid for the supplied MethodType to have a void return - // value when the return value for the AccessMode is non-void. This - // corresponds to the result of the accessor being discarded. - bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) - REQUIRES_SHARED(Locks::mutator_lock_); + enum MatchKind : uint8_t { + kNone, + kWithConversions, + kExact + }; + + // Returns match information on the compatability between the exact method type for + // 'access_mode' and the provided 'method_type'. + MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, MethodType* method_type) + REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the MethodType specified is compatible with the // specified access_mode if the first parameter of method_type is @@ -124,7 +126,7 @@ class MANAGED VarHandle : public Object { bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -192,7 +194,7 @@ class MANAGED FieldVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -225,7 +227,7 @@ class MANAGED ArrayElementVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -248,7 +250,7 @@ class MANAGED ByteArrayViewVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -281,7 +283,7 @@ class MANAGED ByteBufferViewVarHandle : public VarHandle { public: bool Access(AccessMode access_mode, ShadowFrame* shadow_frame, - InstructionOperands* operands, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc index d9fa07f207..005aba3edd 100644 --- a/runtime/mirror/var_handle_test.cc +++ b/runtime/mirror/var_handle_test.cc @@ -246,6 +246,47 @@ static MethodType* MethodTypeOf(const std::string& method_descriptor) { return MethodType::Create(self, rtype, ptypes); } +static bool AccessModeMatch(VarHandle* vh, + VarHandle::AccessMode access_mode, + MethodType* method_type, + VarHandle::MatchKind expected_match) + REQUIRES_SHARED(Locks::mutator_lock_) { + return vh->GetMethodTypeMatchForAccessMode(access_mode, method_type) == expected_match; +} + +template <typename VH> +static bool AccessModeExactMatch(Handle<VH> vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kExact); +} + +template <typename VH> +static bool AccessModeWithConversionsMatch(Handle<VH> vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kWithConversions); +} + +template <typename VH> +static bool AccessModeNoMatch(Handle<VH> vh, + VarHandle::AccessMode access_mode, + const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + return AccessModeMatch(vh.Get(), + access_mode, + MethodTypeOf(descriptor), + VarHandle::MatchKind::kNone); +} + TEST_F(VarHandleTest, InstanceFieldVarHandle) { Thread * const self = Thread::Current(); ScopedObjectAccess soa(self); @@ -296,47 +337,53 @@ TEST_F(VarHandleTest, InstanceFieldVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;)D")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;S)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(Ljava/lang/Integer;II)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Z")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Ljava/lang/Boolean;")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;IB)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)J")); + EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;BS)F")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIII)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S")); } // Check synthesized method types match expected forms. @@ -430,48 +477,47 @@ TEST_F(VarHandleTest, StaticFieldVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(F)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(II)Ljava/lang/String;"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(ID)I")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIJ)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I"))); - EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z"))); - EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)I")); + EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(I)Z")); + EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -594,50 +640,46 @@ TEST_F(VarHandleTest, ArrayElementVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;Ljava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("([Ljava/lang/String;III)I"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;III)I")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;II)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -747,50 +789,46 @@ TEST_F(VarHandleTest, ByteArrayViewVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("([BICC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("([BIII)I"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIII)I")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BII)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)C")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIC)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. @@ -900,50 +938,46 @@ TEST_F(VarHandleTest, ByteBufferViewVarHandle) { // Check compatibility - "Get" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;D)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z")); } // Check compatibility - "Set" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndSet" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet; - EXPECT_TRUE( - vh->IsMethodTypeCompatible( - access_mode, - MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, - MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDI)D")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V")); } // Check compatibility - "CompareAndExchange" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;II)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V")); } // Check compatibility - "GetAndUpdate" pattern { const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd; - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D"))); - EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z"))); - EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V"))); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)D")); + EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)Z")); + EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V")); } // Check synthesized method types match expected forms. diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index e0afbee845..25599843e9 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -27,6 +27,7 @@ #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" #include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/field-inl.h" #include "native_util.h" diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index c00e47f9b8..6c869cada5 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -36,6 +36,7 @@ #include "exec_utils.h" #include "gc/heap.h" #include "gc/space/image_space.h" +#include "hidden_api.h" #include "image.h" #include "oat.h" #include "runtime.h" @@ -823,6 +824,11 @@ bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, argv.push_back("--compiler-filter=verify-none"); } + if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) { + argv.push_back("--runtime-arg"); + argv.push_back("-Xhidden-api-checks"); + } + if (runtime->MustRelocateIfPossible()) { argv.push_back("--runtime-arg"); argv.push_back("-Xrelocate"); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 99bc0b2c6e..0b3c61d474 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -33,6 +33,7 @@ #include "class_loader_context.h" #include "common_runtime_test.h" #include "dexopt_test.h" +#include "hidden_api.h" #include "oat_file.h" #include "oat_file_manager.h" #include "scoped_thread_state_change-inl.h" @@ -43,6 +44,8 @@ namespace art { static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4] static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr; +static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks"; + class OatFileAssistantTest : public DexoptTest { public: void VerifyOptimizationStatus(const std::string& file, @@ -1413,6 +1416,46 @@ TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) { oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey)); } +TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) { + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kNoChecks); + + std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + + const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + EXPECT_NE(nullptr, cmd_line); + EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg)); +} + +TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) { + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kBlacklistOnly); + + std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar"; + Copy(GetDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + std::string error_msg; + int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg); + EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg; + + std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); + EXPECT_NE(nullptr, oat_file.get()); + + const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey); + EXPECT_NE(nullptr, cmd_line); + EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg)); +} + TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) { std::string dex_location = GetScratchDir() + "/TestDex.jar"; std::string context_location = GetScratchDir() + "/ContextDex.jar"; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index dfa4b3daab..66eba1e1d4 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -25,6 +25,7 @@ #include "indirect_reference_table-inl.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" +#include "jvalue-inl.h" #include "mirror/class-inl.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b8775b874f..4142cb0c95 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1198,7 +1198,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // As is, we're encoding some logic here about which specific policy to use, which would be better // controlled by the framework. hidden_api_policy_ = do_hidden_api_checks - ? hiddenapi::EnforcementPolicy::kBlacklistOnly + ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList : hiddenapi::EnforcementPolicy::kNoChecks; no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain); diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc new file mode 100644 index 0000000000..d71745e531 --- /dev/null +++ b/runtime/var_handles.cc @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "var_handles.h" + +#include "common_throws.h" +#include "dex/dex_instruction.h" +#include "handle.h" +#include "method_handles-inl.h" +#include "mirror/method_type.h" +#include "mirror/var_handle.h" + +namespace art { + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + Handle<mirror::MethodType> callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) { + if (var_handle.IsNull()) { + ThrowNullPointerExceptionFromDexPC(); + return false; + } + + if (!var_handle->IsAccessModeSupported(access_mode)) { + ThrowUnsupportedOperationException(); + return false; + } + + mirror::VarHandle::MatchKind match_kind = + var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get()); + if (match_kind == mirror::VarHandle::MatchKind::kExact) { + return var_handle->Access(access_mode, &shadow_frame, operands, result); + } + if (match_kind == mirror::VarHandle::MatchKind::kNone) { + ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode), + callsite_type.Get()); + return false; + } + DCHECK_EQ(mirror::VarHandle::MatchKind::kWithConversions, match_kind); + + StackHandleScope<1> hs(self); + Handle<mirror::MethodType> accessor_type(hs.NewHandle( + var_handle->GetMethodTypeForAccessMode(self, access_mode))); + const size_t num_vregs = accessor_type->NumberOfVRegs(); + const int num_params = accessor_type->GetPTypes()->GetLength(); + ShadowFrameAllocaUniquePtr accessor_frame = + CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC()); + ShadowFrameGetter getter(shadow_frame, operands); + static const uint32_t kFirstDestinationReg = 0; + ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg); + if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) { + return false; + } + RangeInstructionOperands accessor_operands(kFirstDestinationReg, + kFirstDestinationReg + num_vregs); + if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) { + return false; + } + return ConvertReturnValue(callsite_type, accessor_type, result); +} + +} // namespace art diff --git a/runtime/var_handles.h b/runtime/var_handles.h new file mode 100644 index 0000000000..2ff8405f03 --- /dev/null +++ b/runtime/var_handles.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_VAR_HANDLES_H_ +#define ART_RUNTIME_VAR_HANDLES_H_ + +#include "mirror/var_handle.h" + +namespace art { + +bool VarHandleInvokeAccessor(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::VarHandle> var_handle, + Handle<mirror::MethodType> callsite_type, + const mirror::VarHandle::AccessMode access_mode, + const InstructionOperands* const operands, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + +} // namespace art + +#endif // ART_RUNTIME_VAR_HANDLES_H_ diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 3518d2facd..91cec23dd5 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3086,7 +3086,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { DCHECK(HasFailures()); break; } - const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); + const dex::ProtoIndex proto_idx(vRegH); const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); const RegType& return_type = @@ -3117,7 +3118,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); it.Next(); // Skip to name. it.Next(); // Skip to method type of the method handle - const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i); + const dex::ProtoIndex proto_idx(it.GetJavaValue().c); const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx); DexFileParameterIterator param_it(*dex_file_, proto_id); // Treat method as static as it has yet to be determined. @@ -4190,7 +4191,8 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( if (UNLIKELY(method_type == METHOD_POLYMORPHIC)) { // Process the signature of the calling site that is invoking the method handle. - DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(inst->VRegH())); + dex::ProtoIndex proto_idx(inst->VRegH()); + DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx)); return VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method); } else { // Process the target method's signature. @@ -4208,8 +4210,6 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name); } else if (klass == mirror::VarHandle::StaticClass()) { expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name); - // TODO: add compiler support for VarHandle accessor methods (b/71781600) - Fail(VERIFY_ERROR_FORCE_INTERPRETER); } else { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor(); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index b79334ac7f..4843061be6 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -282,26 +282,9 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { } #undef STRING_INIT_LIST -class ScopedHiddenApiExemption { - public: - explicit ScopedHiddenApiExemption(Runtime* runtime) - : runtime_(runtime), - initial_policy_(runtime_->GetHiddenApiEnforcementPolicy()) { - runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); - } - - ~ScopedHiddenApiExemption() { - runtime_->SetHiddenApiEnforcementPolicy(initial_policy_); - } - - private: - Runtime* runtime_; - const hiddenapi::EnforcementPolicy initial_policy_; - DISALLOW_COPY_AND_ASSIGN(ScopedHiddenApiExemption); -}; - void WellKnownClasses::Init(JNIEnv* env) { - ScopedHiddenApiExemption hiddenapi_exemption(Runtime::Current()); + hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption( + hiddenapi::EnforcementPolicy::kNoChecks); dalvik_annotation_optimization_CriticalNative = CacheClass(env, "dalvik/annotation/optimization/CriticalNative"); diff --git a/test/999-redefine-hiddenapi/api-blacklist.txt b/test/999-redefine-hiddenapi/api-blacklist.txt new file mode 100644 index 0000000000..63e37aa757 --- /dev/null +++ b/test/999-redefine-hiddenapi/api-blacklist.txt @@ -0,0 +1,2 @@ +Lart/Test999;->foo()V +Lart/Test999;->bar:I diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build new file mode 100644 index 0000000000..f4b029fb82 --- /dev/null +++ b/test/999-redefine-hiddenapi/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +USE_HIDDENAPI=true ./default-build "$@" diff --git a/test/999-redefine-hiddenapi/expected.txt b/test/999-redefine-hiddenapi/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/999-redefine-hiddenapi/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/999-redefine-hiddenapi/info.txt b/test/999-redefine-hiddenapi/info.txt new file mode 100644 index 0000000000..87bc30cf80 --- /dev/null +++ b/test/999-redefine-hiddenapi/info.txt @@ -0,0 +1 @@ +Tests that JVMTI class redefinition does not strip away hidden API access flags. diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/999-redefine-hiddenapi/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/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java new file mode 100644 index 0000000000..97495c5a47 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-ex/Test999.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +public class Test999 { + public void foo() { + System.out.println("hello"); + } + + public int bar = 42; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java new file mode 100644 index 0000000000..c1b838ccc7 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +public class Test999 { + public void foo() { + System.out.println("Goodbye"); + } + + public int bar = 64; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh new file mode 100755 index 0000000000..6948cbbfc3 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TMP=`mktemp -d` + +CLASS "art/Test999" + +(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class") + +echo ' private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/${CLASS}.class" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' +echo ' private static final byte[] DEX_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/classes.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' + +rm -rf "$TMP" diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java new file mode 100644 index 0000000000..c6365ac234 --- /dev/null +++ b/test/999-redefine-hiddenapi/src/Main.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Base64; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + // Run the initialization routine. This will enable hidden API checks in + // the runtime, in case they are not enabled by default. + init(); + + // Load the '-ex' APK and attach it to the boot class path. + appendToBootClassLoader(DEX_EXTRA); + + // Find the test class in boot class loader and verify that its members are hidden. + Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER); + assertMethodIsHidden(klass, "before redefinition"); + assertFieldIsHidden(klass, "before redefinition"); + + // Redefine the class using JVMTI. + art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE); + art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES); + + // Verify that the class members are still hidden. + assertMethodIsHidden(klass, "after redefinition"); + assertFieldIsHidden(klass, "after redefinition"); + } + + private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception { + try { + klass.getDeclaredMethod("foo"); + // Unexpected. Should have thrown NoSuchMethodException. + throw new Exception("Method should not be accessible " + msg); + } catch (NoSuchMethodException ex) { + // Expected. + } + } + + private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception { + try { + klass.getDeclaredField("bar"); + // Unexpected. Should have thrown NoSuchFieldException. + throw new Exception("Field should not be accessible " + msg); + } catch (NoSuchFieldException ex) { + // Expected. + } + } + + private static final String DEX_EXTRA = + new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath(); + + private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader(); + + // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc. + private static native void appendToBootClassLoader(String dexPath); + private static native void init(); + + /** + * base64 encoded class/dex file for + * + * public class Test999 { + * public void foo() { + * System.out.println("Goodbye"); + * } + * + * public int bar = 64; + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" + + "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" + + "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" + + "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" + + "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" + + "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" + + "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" + + "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" + + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" + + "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" + + "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" + + "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" + + "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + + "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" + + "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" + + "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" + + "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" + + "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" + + "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" + + "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" + + "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" + + "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA"); +} diff --git a/test/999-redefine-hiddenapi/src/art/Redefinition.java b/test/999-redefine-hiddenapi/src/art/Redefinition.java new file mode 100644 index 0000000000..1eec70b48c --- /dev/null +++ b/test/999-redefine-hiddenapi/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/Android.bp b/test/Android.bp index 76189f62a9..84f2e224d5 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -370,22 +370,27 @@ cc_library_static { } art_cc_defaults { - name: "libtistress-defaults", + name: "libtistress-srcs", defaults: ["libartagent-defaults"], srcs: [ "ti-stress/stress.cc", ], + header_libs: ["libopenjdkjvmti_headers"], +} + +art_cc_defaults { + name: "libtistress-defaults", + defaults: ["libtistress-srcs"], shared_libs: [ "libbase", "slicer", ], - header_libs: ["libopenjdkjvmti_headers"], } art_cc_test_library { name: "libtistress", defaults: ["libtistress-defaults"], - shared_libs: ["libart"], + shared_libs: ["libartbase"], } art_cc_test_library { @@ -394,7 +399,30 @@ art_cc_test_library { "art_debug_defaults", "libtistress-defaults", ], - shared_libs: ["libartd"], + shared_libs: ["libartbased"], +} + +art_cc_defaults { + name: "libtistress-static-defaults", + defaults: ["libtistress-srcs"], + static_libs: art_static_dependencies + [ + "slicer", + ], +} + +art_cc_test_library { + name: "libtistresss", + defaults: ["libtistress-static-defaults"], + static_libs: ["libartbase"], +} + +art_cc_test_library { + name: "libtistressds", + defaults: [ + "art_debug_defaults", + "libtistress-static-defaults" + ], + static_libs: ["libartbased"], } cc_defaults { diff --git a/test/etc/default-build b/test/etc/default-build index 8bb898c7c1..c61de0ab6e 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -561,6 +561,11 @@ fi if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then make_dex classes-ex + # Apply hiddenapi on the dex files if the test has API list file(s). + if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then + make_hiddenapi classes-ex.dex + fi + # quick shuffle so that the stored name is "classes.dex" mv classes.dex classes-1.dex mv classes-ex.dex classes.dex diff --git a/test/knownfailures.json b/test/knownfailures.json index f473a99a27..a202044786 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -454,7 +454,8 @@ "674-hiddenapi", "649-vdex-duplicate-method", "804-class-extends-itself", - "921-hello-failure" + "921-hello-failure", + "999-redefine-hiddenapi" ], "description": [ "Tests that use illegal dex files or otherwise break dexter assumptions" @@ -471,7 +472,8 @@ "629-vdex-speed", "647-jni-get-field-id", "674-hiddenapi", - "944-transform-classloaders" + "944-transform-classloaders", + "999-redefine-hiddenapi" ], "description": [ "Tests that use custom class loaders or other features not supported ", @@ -876,7 +878,6 @@ "667-jit-jni-stub", "667-out-of-bounds", "668-aiobe", - "674-hiddenapi", "674-hotness-compiled", "674-vdex-uncompress", "675-checker-unverified-method", @@ -954,8 +955,10 @@ }, { "tests": ["616-cha-unloading", + "674-hiddenapi", "678-quickening", - "679-locks"], + "679-locks", + "999-redefine-hiddenapi"], "variant": "jvm", "description": ["Doesn't run on RI."] }, diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index bbe74656dd..0eba7426c0 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -25,7 +25,6 @@ #include <jni.h> #include "base/utils.h" -#include "exec_utils.h" #include "jvmti.h" #pragma clang diagnostic push @@ -920,4 +919,8 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, return 0; } +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { + return Agent_OnLoad(vm, options, reserved); +} + } // namespace art diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc index a5f647cc56..c4aebc6f56 100644 --- a/tools/dexanalyze/dexanalyze.cc +++ b/tools/dexanalyze/dexanalyze.cc @@ -15,6 +15,7 @@ */ #include <cstdint> +#include <iostream> #include <set> #include <sstream> @@ -31,6 +32,15 @@ namespace art { class DexAnalyze { static const int kExitCodeUsageError = 1; + static void StdoutLogger(android::base::LogId, + android::base::LogSeverity, + const char*, + const char*, + unsigned int, + const char* message) { + std::cout << message << std::endl; + } + static int Usage(char** argv) { LOG(ERROR) << "Usage " << argv[0] << " [options] <dex files>\n" @@ -105,6 +115,8 @@ class DexAnalyze { public: static int Run(int argc, char** argv) { + android::base::SetLogger(StdoutLogger); + Options options; int result = options.Parse(argc, argv); if (result != 0) { @@ -154,7 +166,6 @@ class DexAnalyze { } // namespace art int main(int argc, char** argv) { - android::base::SetLogger(android::base::StderrLogger); return art::DexAnalyze::Run(argc, argv); } |