Merge "Reland: "ART: Hook Runtime APEX checker into build""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bd306b6..21eee7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -38,6 +38,7 @@
GetMethodSignature \
HiddenApi \
HiddenApiSignatures \
+ HiddenApiStubs \
ImageLayoutA \
ImageLayoutB \
IMTA \
@@ -188,7 +189,7 @@
ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed StringLiterals
ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
-ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi
+ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi HiddenApiStubs
ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit
ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 7e82944..e3e5b6e 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -13,12 +13,11 @@
]
// - Base requirements (libraries).
art_runtime_base_native_shared_libs = [
- "libadbconnection",
"libart",
"libart-compiler",
- "libdexfile_external",
"libopenjdkjvm",
"libopenjdkjvmti",
+ "libadbconnection",
]
bionic_native_shared_libs = [
"libc",
@@ -42,11 +41,11 @@
// - Debug variants (libraries).
art_runtime_debug_native_shared_libs = [
- "libadbconnectiond",
"libartd",
"libartd-compiler",
"libopenjdkjvmd",
"libopenjdkjvmtid",
+ "libadbconnectiond",
]
libcore_debug_native_shared_libs = [
"libopenjdkd",
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index a8b104f..1abc466 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -353,7 +353,6 @@
self._checker.check_library('libartpalette.so')
self._checker.check_no_library('libartpalette-system.so')
self._checker.check_library('libdexfile.so')
- self._checker.check_library('libdexfile_external.so')
self._checker.check_library('libopenjdkjvm.so')
self._checker.check_library('libopenjdkjvmti.so')
self._checker.check_library('libprofile.so')
diff --git a/build/apex/ld.config.txt b/build/apex/ld.config.txt
index 3cfda65..9bf2ae5 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -30,7 +30,6 @@
namespace.platform.asan.search.paths = /data/asan/system/${LIB}
namespace.platform.links = default
namespace.platform.link.default.shared_libs = libart.so:libartd.so
-namespace.platform.link.default.shared_libs += libdexfile_external.so
namespace.platform.link.default.shared_libs += libnativebridge.so
namespace.platform.link.default.shared_libs += libnativehelper.so
namespace.platform.link.default.shared_libs += libnativeloader.so
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 393db3d..e5c09aa 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -127,15 +127,21 @@
new linker::ElfBuilder<ElfTypes>(isa, features, &out));
builder->Start(/* write_program_headers= */ false);
// Mirror ELF sections as NOBITS since the added symbols will reference them.
- builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+ if (text_section_size != 0) {
+ builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+ }
if (dex_section_size != 0) {
builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size);
}
- WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
- WriteCFISection(builder.get(),
- debug_info.compiled_methods,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- /* write_oat_patches= */ false);
+ if (!debug_info.Empty()) {
+ WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
+ }
+ if (!debug_info.compiled_methods.empty()) {
+ WriteCFISection(builder.get(),
+ debug_info.compiled_methods,
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ /* write_oat_patches= */ false);
+ }
builder->End();
CHECK(builder->Good());
std::vector<uint8_t> compressed_buffer;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 0d35fec..4d7ae9b 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -99,7 +99,10 @@
}
}
}
+
if (instruction_set_features == nullptr) {
+ // '--instruction-set-features/--instruction-set-variant' were not used.
+ // Use build-time defined features.
instruction_set_features = InstructionSetFeatures::FromCppDefines();
}
compiler_options_->instruction_set_features_ = std::move(instruction_set_features);
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 2721cb5..2de0f0c 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -20,6 +20,7 @@
#include "art_method-inl.h"
#include "base/utils.h"
#include "class_linker.h"
+#include "class_root.h"
#include "dex/invoke_type.h"
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
@@ -362,4 +363,13 @@
return info;
}
+void IntrinsicVisitor::AssertNonMovableStringClass() {
+ if (kIsDebugBuild) {
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ ObjPtr<mirror::Class> string_class = GetClassRoot<art::mirror::String>();
+ CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 50b13c8..ab68cce 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -142,6 +142,8 @@
protected:
IntrinsicVisitor() {}
+ static void AssertNonMovableStringClass();
+
private:
DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
};
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index ca790f6..ec5d17a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1464,8 +1464,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp = str->klass_
__ Ldr(temp, MemOperand(str.X(), class_offset));
+ // /* HeapReference<Class> */ temp1 = arg->klass_
__ Ldr(temp1, MemOperand(arg.X(), class_offset));
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Cmp(temp, temp1);
__ B(&return_false, ne);
}
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 396ff62..f0aa92e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1529,8 +1529,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp = str->klass_
__ Ldr(temp, MemOperand(str, class_offset));
+ // /* HeapReference<Class> */ out = arg->klass_
__ Ldr(out, MemOperand(arg, class_offset));
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Cmp(temp, out);
__ B(ne, &return_false, /* is_far_target= */ false);
}
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 5b35974..3da0e57 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1575,8 +1575,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp1 = str->klass_
__ Lw(temp1, str, class_offset);
+ // /* HeapReference<Class> */ temp2 = arg->klass_
__ Lw(temp2, arg, class_offset);
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Bne(temp1, temp2, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index afaa4ca..3e68765 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1429,8 +1429,16 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // /* HeapReference<Class> */ temp1 = str->klass_
__ Lw(temp1, str, class_offset);
+ // /* HeapReference<Class> */ temp2 = arg->klass_
__ Lw(temp2, arg, class_offset);
+ // Also, because we use the previously loaded class references only in the
+ // following comparison, we don't need to unpoison them.
__ Bnec(temp1, temp2, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 8747f06..de697f0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1072,7 +1072,15 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // Also, because we use the loaded class references only to compare them, we
+ // don't need to unpoison them.
+ // /* HeapReference<Class> */ ecx = str->klass_
__ movl(ecx, Address(str, class_offset));
+ // if (ecx != /* HeapReference<Class> */ arg->klass_) return false
__ cmpl(ecx, Address(arg, class_offset));
__ j(kNotEqual, &return_false);
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 167c1d8..e79c0c9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1275,7 +1275,15 @@
// All string objects must have the same type since String cannot be subclassed.
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
+ //
+ // As the String class is expected to be non-movable, we can read the class
+ // field from String.equals' arguments without read barriers.
+ AssertNonMovableStringClass();
+ // Also, because we use the loaded class references only to compare them, we
+ // don't need to unpoison them.
+ // /* HeapReference<Class> */ rcx = str->klass_
__ movl(rcx, Address(str, class_offset));
+ // if (rcx != /* HeapReference<Class> */ arg->klass_) return false
__ cmpl(rcx, Address(arg, class_offset));
__ j(kNotEqual, &return_false);
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 808ad6c..ad1dda4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -296,6 +296,10 @@
UsageError(" Default: arm");
UsageError("");
UsageError(" --instruction-set-features=...,: Specify instruction set features");
+ UsageError(" On target the value 'runtime' can be used to detect features at run time.");
+ UsageError(" If target does not support run-time detection the value 'runtime'");
+ UsageError(" has the same effect as the value 'default'.");
+ UsageError(" Note: the value 'runtime' has no effect if it is used on host.");
UsageError(" Example: --instruction-set-features=div");
UsageError(" Default: default");
UsageError("");
@@ -875,9 +879,9 @@
oat_unstripped_ = std::move(parser_options->oat_symbols);
}
- // If no instruction set feature was given, use the default one for the target
- // instruction set.
- if (compiler_options_->instruction_set_features_.get() == nullptr) {
+ if (compiler_options_->instruction_set_features_ == nullptr) {
+ // '--instruction-set-features/--instruction-set-variant' were not used.
+ // Use features for the 'default' variant.
compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
compiler_options_->instruction_set_, "default", &parser_options->error_msg);
if (compiler_options_->instruction_set_features_ == nullptr) {
@@ -890,9 +894,9 @@
std::unique_ptr<const InstructionSetFeatures> runtime_features(
InstructionSetFeatures::FromCppDefines());
if (!compiler_options_->GetInstructionSetFeatures()->Equals(runtime_features.get())) {
- LOG(WARNING) << "Mismatch between dex2oat instruction set features ("
+ LOG(WARNING) << "Mismatch between dex2oat instruction set features to use ("
<< *compiler_options_->GetInstructionSetFeatures()
- << ") and those of dex2oat executable (" << *runtime_features
+ << ") and those from CPP defines (" << *runtime_features
<< ") for the command line:\n" << CommandLine();
}
}
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 524bce0..d3bfb57 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <algorithm>
#include <regex>
#include <sstream>
#include <string>
@@ -28,6 +29,7 @@
#include "common_runtime_test.h"
+#include "arch/instruction_set_features.h"
#include "base/macros.h"
#include "base/mutex-inl.h"
#include "base/utils.h"
@@ -2315,4 +2317,38 @@
}));
}
+class Dex2oatISAFeaturesRuntimeDetectionTest : public Dex2oatTest {
+ protected:
+ void RunTest(const std::vector<std::string>& extra_args = {}) {
+ std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+ std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+ Copy(GetTestDexFileName(), dex_location);
+
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeed,
+ extra_args));
+ }
+
+ std::string GetTestDexFileName() {
+ return GetDexSrc1();
+ }
+};
+
+TEST_F(Dex2oatISAFeaturesRuntimeDetectionTest, TestCurrentRuntimeFeaturesAsDex2OatArguments) {
+ std::vector<std::string> argv;
+ Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+ auto option_pos =
+ std::find(std::begin(argv), std::end(argv), "--instruction-set-features=runtime");
+ if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ EXPECT_TRUE(kIsTargetBuild);
+ EXPECT_NE(option_pos, std::end(argv));
+ } else {
+ EXPECT_EQ(option_pos, std::end(argv));
+ }
+
+ RunTest();
+}
+
} // namespace art
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 9fbcca4..b3e8290 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -261,7 +261,7 @@
template <typename ElfTypes>
void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(const debug::DebugInfo& debug_info) {
- if (!debug_info.Empty() && compiler_options_.GetGenerateMiniDebugInfo()) {
+ if (compiler_options_.GetGenerateMiniDebugInfo()) {
// Prepare the mini-debug-info in background while we do other I/O.
Thread* self = Thread::Current();
debug_info_task_ = std::make_unique<DebugInfoTask>(
@@ -280,19 +280,17 @@
template <typename ElfTypes>
void ElfWriterQuick<ElfTypes>::WriteDebugInfo(const debug::DebugInfo& debug_info) {
- if (!debug_info.Empty()) {
- if (compiler_options_.GetGenerateMiniDebugInfo()) {
- // Wait for the mini-debug-info generation to finish and write it to disk.
- Thread* self = Thread::Current();
- DCHECK(debug_info_thread_pool_ != nullptr);
- debug_info_thread_pool_->Wait(self, true, false);
- builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
- }
- // The Strip method expects debug info to be last (mini-debug-info is not stripped).
- if (compiler_options_.GetGenerateDebugInfo()) {
- // Generate all the debug information we can.
- debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
- }
+ if (compiler_options_.GetGenerateMiniDebugInfo()) {
+ // Wait for the mini-debug-info generation to finish and write it to disk.
+ Thread* self = Thread::Current();
+ DCHECK(debug_info_thread_pool_ != nullptr);
+ debug_info_thread_pool_->Wait(self, true, false);
+ builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
+ }
+ // The Strip method expects debug info to be last (mini-debug-info is not stripped).
+ if (!debug_info.Empty() && compiler_options_.GetGenerateDebugInfo()) {
+ // Generate all the debug information we can.
+ debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
}
}
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index feb5e38..f83f18c 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -228,9 +228,28 @@
"libdexfile",
],
- stubs: {
- symbol_file: "external/libdexfile_external.map.txt",
- versions: ["1"],
+ // TODO(b/120670568): Enable this when linking bug is fixed.
+ // stubs: {
+ // symbol_file: "external/libdexfile_external.map.txt",
+ // versions: ["1"],
+ // },
+
+ // Hide symbols using version scripts for targets that support it, i.e. all
+ // but Darwin.
+ // TODO(b/120670568): Clean this up when stubs above is enabled.
+ target: {
+ android: {
+ version_script: "external/libdexfile_external.map.txt",
+ },
+ linux_bionic: {
+ version_script: "external/libdexfile_external.map.txt",
+ },
+ linux_glibc: {
+ version_script: "external/libdexfile_external.map.txt",
+ },
+ windows: {
+ version_script: "external/libdexfile_external.map.txt",
+ },
},
}
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index f56265c..8849a7a 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -97,8 +97,8 @@
"libart-disassembler",
"libvixl",
],
- // TODO(b/122885634): This is necessary for the static lib ordering bug with
- // APEX stubs.
+ // We need this to resolve libartpalette symbols
+ // correctly. Multiple source libraries depend on it.
group_static_libs: true,
}
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index fcf3c75..fdf4dbd 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -319,8 +319,9 @@
bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_;
bool has_div = has_div_;
bool has_armv8a = has_armv8a_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "div") {
has_div = true;
} else if (feature == "-div") {
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 4a2b9d5..196f358 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -315,8 +315,9 @@
bool has_lse = has_lse_;
bool has_fp16 = has_fp16_;
bool has_dotprod = has_dotprod_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "a53") {
is_a53 = true;
} else if (feature == "-a53") {
@@ -367,4 +368,17 @@
has_dotprod));
}
+std::unique_ptr<const InstructionSetFeatures>
+Arm64InstructionSetFeatures::AddRuntimeDetectedFeatures(
+ const InstructionSetFeatures *features) const {
+ const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
+ return std::unique_ptr<const InstructionSetFeatures>(
+ new Arm64InstructionSetFeatures(fix_cortex_a53_835769_,
+ fix_cortex_a53_843419_,
+ arm64_features->has_crc_,
+ arm64_features->has_lse_,
+ arm64_features->has_fp16_,
+ arm64_features->has_dotprod_));
+}
+
} // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index 4ec8fa2..432b9ef 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -98,6 +98,9 @@
AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const override;
+ std::unique_ptr<const InstructionSetFeatures>
+ AddRuntimeDetectedFeatures(const InstructionSetFeatures *features) const override;
+
private:
Arm64InstructionSetFeatures(bool needs_a53_835769_fix,
bool needs_a53_843419_fix,
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index 99d6b0d..eef8f08 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -170,4 +170,54 @@
EXPECT_EQ(armv8_2a_cpu_features->AsBitmap(), 14U);
}
+TEST(Arm64InstructionSetFeaturesTest, IsRuntimeDetectionSupported) {
+ if (kRuntimeISA == InstructionSet::kArm64) {
+ EXPECT_TRUE(InstructionSetFeatures::IsRuntimeDetectionSupported());
+ }
+}
+
+TEST(Arm64InstructionSetFeaturesTest, FeaturesFromRuntimeDetection) {
+ if (kRuntimeISA != InstructionSet::kArm64) {
+ return;
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+ InstructionSetFeatures::FromHwcap());
+ std::unique_ptr<const InstructionSetFeatures> runtime_detected_features(
+ InstructionSetFeatures::FromRuntimeDetection());
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ EXPECT_NE(runtime_detected_features, nullptr);
+ EXPECT_TRUE(InstructionSetFeatures::IsRuntimeDetectionSupported());
+ EXPECT_TRUE(runtime_detected_features->Equals(hwcap_features.get()));
+ EXPECT_TRUE(runtime_detected_features->HasAtLeast(cpp_defined_features.get()));
+}
+
+TEST(Arm64InstructionSetFeaturesTest, AddFeaturesFromStringRuntime) {
+ std::unique_ptr<const InstructionSetFeatures> features(
+ InstructionSetFeatures::FromBitmap(InstructionSet::kArm64, 0x0));
+ std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+ InstructionSetFeatures::FromHwcap());
+
+ std::string error_msg;
+ features = features->AddFeaturesFromString("runtime", &error_msg);
+
+ EXPECT_NE(features, nullptr);
+ EXPECT_TRUE(error_msg.empty());
+
+ if (kRuntimeISA == InstructionSet::kArm64) {
+ EXPECT_TRUE(features->Equals(hwcap_features.get()));
+ EXPECT_EQ(features->GetFeatureString(), hwcap_features->GetFeatureString());
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> a53_features(
+ Arm64InstructionSetFeatures::FromVariant("cortex-a53", &error_msg));
+ features = a53_features->AddFeaturesFromString("runtime", &error_msg);
+ EXPECT_NE(features, nullptr);
+ EXPECT_TRUE(error_msg.empty()) << error_msg;
+ const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
+ EXPECT_TRUE(arm64_features->NeedFixCortexA53_835769());
+ EXPECT_TRUE(arm64_features->NeedFixCortexA53_843419());
+}
+
} // namespace art
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index 886b40a..c5c2d31 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <algorithm>
+
#include "instruction_set_features.h"
#include <algorithm>
@@ -113,6 +115,16 @@
UNREACHABLE();
}
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromRuntimeDetection() {
+ switch (kRuntimeISA) {
+#ifdef ART_TARGET_ANDROID
+ case InstructionSet::kArm64:
+ return Arm64InstructionSetFeatures::FromHwcap();
+#endif
+ default:
+ return nullptr;
+ }
+}
std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromCpuInfo() {
switch (kRuntimeISA) {
@@ -184,44 +196,57 @@
}
std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddFeaturesFromString(
- const std::string& feature_list, std::string* error_msg) const {
- if (feature_list.empty()) {
- *error_msg = "No instruction set features specified";
- return std::unique_ptr<const InstructionSetFeatures>();
- }
+ const std::string& feature_list, /* out */ std::string* error_msg) const {
std::vector<std::string> features;
Split(feature_list, ',', &features);
- bool use_default = false; // Have we seen the 'default' feature?
- bool first = false; // Is this first feature?
- for (auto it = features.begin(); it != features.end();) {
- if (use_default) {
- *error_msg = "Unexpected instruction set features after 'default'";
- return std::unique_ptr<const InstructionSetFeatures>();
- }
- std::string feature = android::base::Trim(*it);
- bool erase = false;
- if (feature == "default") {
- if (!first) {
- use_default = true;
- erase = true;
- } else {
- *error_msg = "Unexpected instruction set features before 'default'";
- return std::unique_ptr<const InstructionSetFeatures>();
- }
- }
- if (!erase) {
- ++it;
- } else {
- it = features.erase(it);
- }
- first = true;
+ std::transform(std::begin(features), std::end(features), std::begin(features),
+ [](const std::string &s) { return android::base::Trim(s); });
+ auto empty_strings_begin = std::copy_if(std::begin(features), std::end(features),
+ std::begin(features),
+ [](const std::string& s) { return !s.empty(); });
+ features.erase(empty_strings_begin, std::end(features));
+ if (features.empty()) {
+ *error_msg = "No instruction set features specified";
+ return nullptr;
}
- // Expectation: "default" is standalone, no other flags. But an empty features vector after
- // processing can also come along if the handled flags are the only ones in the list. So
- // logically, we check "default -> features.empty."
- DCHECK(!use_default || features.empty());
- return AddFeaturesFromSplitString(features, error_msg);
+ bool use_default = false;
+ bool use_runtime_detection = false;
+ for (const std::string& feature : features) {
+ if (feature == "default") {
+ if (features.size() > 1) {
+ *error_msg = "Specific instruction set feature(s) cannot be used when 'default' is used.";
+ return nullptr;
+ }
+ use_default = true;
+ features.pop_back();
+ break;
+ } else if (feature == "runtime") {
+ if (features.size() > 1) {
+ *error_msg = "Specific instruction set feature(s) cannot be used when 'runtime' is used.";
+ return nullptr;
+ }
+ use_runtime_detection = true;
+ features.pop_back();
+ break;
+ }
+ }
+ // Expectation: "default" and "runtime" are standalone, no other feature names.
+ // But an empty features vector after processing can also come along if the
+ // handled feature names are the only ones in the list. So
+ // logically, we check "default or runtime => features.empty."
+ DCHECK((!use_default && !use_runtime_detection) || features.empty());
+
+ std::unique_ptr<const InstructionSetFeatures> runtime_detected_features;
+ if (use_runtime_detection) {
+ runtime_detected_features = FromRuntimeDetection();
+ }
+
+ if (runtime_detected_features != nullptr) {
+ return AddRuntimeDetectedFeatures(runtime_detected_features.get());
+ } else {
+ return AddFeaturesFromSplitString(features, error_msg);
+ }
}
const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
@@ -262,6 +287,12 @@
return std::find(begin, end, variant) != end;
}
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddRuntimeDetectedFeatures(
+ const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const {
+ UNIMPLEMENTED(FATAL) << kRuntimeISA;
+ UNREACHABLE();
+}
+
std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) {
os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString();
return os;
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index f910a41..9222a7b 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -48,6 +48,20 @@
// Turn C pre-processor #defines into the equivalent instruction set features for kRuntimeISA.
static std::unique_ptr<const InstructionSetFeatures> FromCppDefines();
+ // Check if run-time detection of instruction set features is supported.
+ //
+ // Return: true - if run-time detection is supported on a target device.
+ // false - otherwise
+ static bool IsRuntimeDetectionSupported() {
+ return FromRuntimeDetection() != nullptr;
+ }
+
+ // Use run-time detection to get instruction set features.
+ //
+ // Return: a set of detected features or nullptr if runtime detection is not
+ // supported on a target.
+ static std::unique_ptr<const InstructionSetFeatures> FromRuntimeDetection();
+
// Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
static std::unique_ptr<const InstructionSetFeatures> FromCpuInfo();
@@ -126,6 +140,10 @@
AddFeaturesFromSplitString(const std::vector<std::string>& features,
std::string* error_msg) const = 0;
+ // Add run-time detected architecture specific features in sub-classes.
+ virtual std::unique_ptr<const InstructionSetFeatures>
+ AddRuntimeDetectedFeatures(const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const;
+
private:
DISALLOW_COPY_AND_ASSIGN(InstructionSetFeatures);
};
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 3a39a2a..d9b2e3f 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <array>
+
#include "instruction_set_features.h"
#include <gtest/gtest.h>
@@ -161,4 +163,145 @@
<< "\nFeatures from build: " << *instruction_set_features.get();
}
+TEST(InstructionSetFeaturesTest, FeaturesFromRuntimeDetection) {
+ if (!InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ EXPECT_EQ(InstructionSetFeatures::FromRuntimeDetection(), nullptr);
+ }
+}
+
+// The instruction set feature string must not contain 'default' together with
+// other feature names.
+//
+// Test that InstructionSetFeatures::AddFeaturesFromString returns nullptr and
+// an error is reported when the value 'default' is specified together
+// with other feature names in an instruction set feature string.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringWithDefaultAndOtherNames) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> invalid_feature_strings = {
+ "a,default",
+ "default,a",
+ "a,default,b",
+ "a,b,default",
+ "default,a,b,c",
+ "a,b,default,c,d",
+ "a, default ",
+ " default , a",
+ "a, default , b",
+ "default,runtime"
+ };
+
+ for (const std::string& invalid_feature_string : invalid_feature_strings) {
+ std::string error_msg;
+ EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+ nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+ EXPECT_EQ(error_msg,
+ "Specific instruction set feature(s) cannot be used when 'default' is used.");
+ }
+}
+
+// The instruction set feature string must not contain 'runtime' together with
+// other feature names.
+//
+// Test that InstructionSetFeatures::AddFeaturesFromString returns nullptr and
+// an error is reported when the value 'runtime' is specified together
+// with other feature names in an instruction set feature string.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringWithRuntimeAndOtherNames) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> invalid_feature_strings = {
+ "a,runtime",
+ "runtime,a",
+ "a,runtime,b",
+ "a,b,runtime",
+ "runtime,a,b,c",
+ "a,b,runtime,c,d",
+ "a, runtime ",
+ " runtime , a",
+ "a, runtime , b",
+ "runtime,default"
+ };
+
+ for (const std::string& invalid_feature_string : invalid_feature_strings) {
+ std::string error_msg;
+ EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+ nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+ EXPECT_EQ(error_msg,
+ "Specific instruction set feature(s) cannot be used when 'runtime' is used.");
+ }
+}
+
+// Spaces and multiple commas are ignores in a instruction set feature string.
+//
+// Test that a use of spaces and multiple commas with 'default' and 'runtime'
+// does not cause errors.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromValidStringContainingDefaultOrRuntime) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> valid_feature_strings = {
+ "default",
+ ",,,default",
+ "default,,,,",
+ ",,,default,,,,",
+ "default, , , ",
+ " , , ,default",
+ " , , ,default, , , ",
+ " default , , , ",
+ ",,,runtime",
+ "runtime,,,,",
+ ",,,runtime,,,,",
+ "runtime, , , ",
+ " , , ,runtime",
+ " , , ,runtime, , , ",
+ " runtime , , , "
+ };
+ for (const std::string& valid_feature_string : valid_feature_strings) {
+ std::string error_msg;
+ EXPECT_NE(cpp_defined_features->AddFeaturesFromString(valid_feature_string, &error_msg),
+ nullptr) << " Valid feature string: '" << valid_feature_string << "'";
+ EXPECT_TRUE(error_msg.empty()) << error_msg;
+ }
+}
+
+// Spaces and multiple commas are ignores in a instruction set feature string.
+//
+// Test that a use of spaces and multiple commas without any feature names
+// causes errors.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromInvalidStringWithoutFeatureNames) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::vector<std::string> invalid_feature_strings = {
+ " ",
+ " ",
+ ",",
+ ",,",
+ " , , ,,,,,,",
+ "\t",
+ " \t ",
+ ",",
+ ",,",
+ " , , ,,,,,,"
+ };
+ for (const std::string& invalid_feature_string : invalid_feature_strings) {
+ std::string error_msg;
+ EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+ nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+ EXPECT_EQ(error_msg, "No instruction set features specified");
+ }
+}
+
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringRuntime) {
+ std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+ InstructionSetFeatures::FromCppDefines());
+ std::string error_msg;
+
+ const std::unique_ptr<const InstructionSetFeatures> features =
+ cpp_defined_features->AddFeaturesFromString("runtime", &error_msg);
+ EXPECT_NE(features, nullptr);
+ EXPECT_TRUE(error_msg.empty()) << error_msg;
+ if (!InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ EXPECT_TRUE(features->Equals(cpp_defined_features.get()));
+ }
+}
+
} // namespace art
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index 952ed25..99ce536 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -214,8 +214,9 @@
bool mips_isa_gte2 = mips_isa_gte2_;
bool r6 = r6_;
bool msa = msa_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "fpu32") {
fpu_32bit = true;
} else if (feature == "-fpu32") {
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index ea9f84b..2031433 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -114,8 +114,9 @@
Mips64InstructionSetFeatures::AddFeaturesFromSplitString(
const std::vector<std::string>& features, std::string* error_msg) const {
bool msa = msa_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "msa") {
msa = true;
} else if (feature == "-msa") {
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index e9e983c..0c3d26e 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -311,8 +311,9 @@
bool has_AVX = has_AVX_;
bool has_AVX2 = has_AVX2_;
bool has_POPCNT = has_POPCNT_;
- for (auto i = features.begin(); i != features.end(); i++) {
- std::string feature = android::base::Trim(*i);
+ for (const std::string& feature : features) {
+ DCHECK_EQ(android::base::Trim(feature), feature)
+ << "Feature name is not trimmed: '" << feature << "'";
if (feature == "ssse3") {
has_SSSE3 = true;
} else if (feature == "-ssse3") {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 44b80df..c7e41be 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -22,7 +22,6 @@
#include "arch/context.h"
#include "art_method-inl.h"
-#include "base/stringpiece.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "debugger.h"
@@ -166,12 +165,11 @@
}
}
-size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
- CHECK_LE(1U, shorty.length());
+size_t ArtMethod::NumArgRegisters(const char* shorty) {
+ CHECK_NE(shorty[0], '\0');
uint32_t num_registers = 0;
- for (size_t i = 1; i < shorty.length(); ++i) {
- char ch = shorty[i];
- if (ch == 'D' || ch == 'J') {
+ for (const char* s = shorty + 1; *s != '\0'; ++s) {
+ if (*s == 'D' || *s == 'J') {
num_registers += 2;
} else {
num_registers += 1;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index aabd093..feff91a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -49,7 +49,6 @@
class OatQuickMethodHeader;
class ProfilingInfo;
class ScopedObjectAccessAlreadyRunnable;
-class StringPiece;
class ShadowFrame;
namespace mirror {
@@ -386,7 +385,7 @@
}
// Number of 32bit registers that would be required to hold all the arguments
- static size_t NumArgRegisters(const StringPiece& shorty);
+ static size_t NumArgRegisters(const char* shorty);
ALWAYS_INLINE uint32_t GetDexMethodIndex() {
return dex_method_index_;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d29a6b7..46ae545 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -25,6 +25,7 @@
#include <memory>
#include <queue>
#include <string>
+#include <string_view>
#include <tuple>
#include <unordered_map>
#include <utility>
@@ -7040,11 +7041,13 @@
return FindSameNameAndSignature(cmp, rest...);
}
+namespace {
+
// Check that all vtable entries are present in this class's virtuals or are the same as a
// superclasses vtable entry.
-static void CheckClassOwnsVTableEntries(Thread* self,
- Handle<mirror::Class> klass,
- PointerSize pointer_size)
+void CheckClassOwnsVTableEntries(Thread* self,
+ Handle<mirror::Class> klass,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<2> hs(self);
Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
@@ -7074,47 +7077,185 @@
// Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
// method is overridden in a subclass.
-static void CheckVTableHasNoDuplicates(Thread* self,
- Handle<mirror::Class> klass,
- PointerSize pointer_size)
+template <PointerSize kPointerSize>
+void CheckVTableHasNoDuplicates(Thread* self, Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
int32_t num_entries = vtable->GetLength();
- for (int32_t i = 0; i < num_entries; i++) {
- ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
- // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+
+ // Observations:
+ // * The older implementation was O(n^2) and got too expensive for apps with larger classes.
+ // * Many classes do not override Object functions (e.g., equals/hashCode/toString). Thus,
+ // for many classes outside of libcore a cross-dexfile check has to be run anyways.
+ // * In the cross-dexfile case, with the O(n^2), in the best case O(n) cross checks would have
+ // to be done. It is thus OK in a single-pass algorithm to read all data, anyways.
+ // * The single-pass algorithm will trade memory for speed, but that is OK.
+
+ CHECK_GT(num_entries, 0);
+
+ auto log_fn = [&vtable, &klass](int32_t i, int32_t j) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* m1 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(i);
+ ArtMethod* m2 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+ LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
+ << klass->PrettyClass() << " in method " << m1->PrettyMethod()
+ << " (0x" << std::hex << reinterpret_cast<uintptr_t>(m2) << ") and "
+ << m2->PrettyMethod() << " (0x" << std::hex
+ << reinterpret_cast<uintptr_t>(m2) << ")";
+ };
+ struct BaseHashType {
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ };
+
+ // Check assuming all entries come from the same dex file.
+ {
+ // Find the first interesting method and its dex file.
+ int32_t start = 0;
+ for (; start < num_entries; ++start) {
+ ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+ // maybe).
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ break;
+ }
+ if (start == num_entries) {
+ return;
+ }
+ const DexFile* dex_file =
+ vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start)->
+ GetInterfaceMethodIfProxy(kPointerSize)->GetDexFile();
+
+ // Helper function to avoid logging if we have to run the cross-file checks.
+ auto check_fn = [&](bool log_warn) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Use a map to store seen entries, as the storage space is too large for a bitvector.
+ using PairType = std::pair<uint32_t, uint16_t>;
+ struct PairHash : BaseHashType {
+ size_t operator()(const PairType& key) const {
+ return BaseHashType::HashCombine(BaseHashType::HashCombine(0, key.first), key.second);
+ }
+ };
+ std::unordered_map<PairType, int32_t, PairHash> seen;
+ seen.reserve(2 * num_entries);
+ bool need_slow_path = false;
+ bool found_dup = false;
+ for (int i = start; i < num_entries; ++i) {
+ // Can use Unchecked here as the start loop already ensured that the arrays are correct
+ // wrt/ kPointerSize.
+ ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+ if (dex_file != m->GetDexFile()) {
+ need_slow_path = true;
+ break;
+ }
+ const dex::MethodId* m_mid = &dex_file->GetMethodId(m->GetDexMethodIndex());
+ PairType pair = std::make_pair(m_mid->name_idx_.index_, m_mid->proto_idx_.index_);
+ auto it = seen.find(pair);
+ if (it != seen.end()) {
+ found_dup = true;
+ if (log_warn) {
+ log_fn(it->second, i);
+ }
+ } else {
+ seen.emplace(pair, i);
+ }
+ }
+ return std::make_pair(need_slow_path, found_dup);
+ };
+ std::pair<bool, bool> result = check_fn(/* log_warn= */ false);
+ if (!result.first) {
+ if (result.second) {
+ check_fn(/* log_warn= */ true);
+ }
+ return;
+ }
+ }
+
+ // Need to check across dex files.
+ struct Entry {
+ size_t cached_hash = 0;
+ const char* name = nullptr;
+ Signature signature = Signature::NoSignature();
+ uint32_t name_len = 0;
+
+ Entry(const DexFile* dex_file, const dex::MethodId& mid)
+ : name(dex_file->StringDataAndUtf16LengthByIdx(mid.name_idx_, &name_len)),
+ signature(dex_file->GetMethodSignature(mid)) {
+ }
+
+ bool operator==(const Entry& other) const {
+ if (name_len != other.name_len || strcmp(name, other.name) != 0) {
+ return false;
+ }
+ return signature == other.signature;
+ }
+ };
+ struct EntryHash {
+ size_t operator()(const Entry& key) const {
+ return key.cached_hash;
+ }
+ };
+ std::unordered_map<Entry, int32_t, EntryHash> map;
+ for (int32_t i = 0; i < num_entries; ++i) {
+ // Can use Unchecked here as the first loop already ensured that the arrays are correct
+ // wrt/ kPointerSize.
+ ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+ // maybe).
if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
vtable_entry->GetAccessFlags())) {
continue;
}
- MethodNameAndSignatureComparator name_comparator(
- vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
- for (int32_t j = i + 1; j < num_entries; j++) {
- ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
- if (!klass->CanAccessMember(other_entry->GetDeclaringClass(),
- other_entry->GetAccessFlags())) {
- continue;
- }
- if (vtable_entry == other_entry ||
- name_comparator.HasSameNameAndSignature(
- other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
- LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
- << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod()
- << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
- << other_entry->PrettyMethod() << " (0x" << std::hex
- << reinterpret_cast<uintptr_t>(other_entry) << ")";
- }
+ ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+ const DexFile* dex_file = m->GetDexFile();
+ const dex::MethodId& mid = dex_file->GetMethodId(m->GetDexMethodIndex());
+
+ Entry e(dex_file, mid);
+
+ size_t string_hash = std::hash<std::string_view>()(std::string_view(e.name, e.name_len));
+ size_t sig_hash = std::hash<std::string>()(e.signature.ToString());
+ e.cached_hash = BaseHashType::HashCombine(BaseHashType::HashCombine(0u, string_hash),
+ sig_hash);
+
+ auto it = map.find(e);
+ if (it != map.end()) {
+ log_fn(it->second, i);
+ } else {
+ map.emplace(e, i);
}
}
}
+void CheckVTableHasNoDuplicates(Thread* self,
+ Handle<mirror::Class> klass,
+ PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ switch (pointer_size) {
+ case PointerSize::k64:
+ CheckVTableHasNoDuplicates<PointerSize::k64>(self, klass);
+ break;
+ case PointerSize::k32:
+ CheckVTableHasNoDuplicates<PointerSize::k32>(self, klass);
+ break;
+ }
+}
+
static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
CheckClassOwnsVTableEntries(self, klass, pointer_size);
CheckVTableHasNoDuplicates(self, klass, pointer_size);
}
+} // namespace
+
void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
ArtMethod* unimplemented_method,
ArtMethod* imt_conflict_method,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index d72003c..5473b52 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2772,13 +2772,6 @@
} else {
LOG(FATAL) << "Invalid current allocator " << current_allocator_;
}
- if (IsGcConcurrent()) {
- // Disable concurrent GC check so that we don't have spammy JNI requests.
- // This gets recalculated in GrowForUtilization. It is important that it is disabled /
- // calculated in the same thread so that there aren't any races that can cause it to become
- // permanantly disabled. b/17942071
- concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
- }
CHECK(collector != nullptr)
<< "Could not find garbage collector with collector_type="
@@ -3662,14 +3655,15 @@
// If the throughput of the current sticky GC >= throughput of the non sticky collector, then
// do another sticky collection next.
- // We also check that the bytes allocated aren't over the footprint limit in order to prevent a
+ // We also check that the bytes allocated aren't over the target_footprint, or
+ // concurrent_start_bytes in case of concurrent GCs, in order to prevent a
// pathological case where dead objects which aren't reclaimed by sticky could get accumulated
// if the sticky GC throughput always remained >= the full/partial throughput.
size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
if (current_gc_iteration_.GetEstimatedThroughput() * sticky_gc_throughput_adjustment >=
non_sticky_collector->GetEstimatedMeanThroughput() &&
non_sticky_collector->NumberOfIterations() > 0 &&
- bytes_allocated <= target_footprint) {
+ bytes_allocated <= (IsGcConcurrent() ? concurrent_start_bytes_ : target_footprint)) {
next_gc_type_ = collector::kGcTypeSticky;
} else {
next_gc_type_ = non_sticky_gc_type;
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index e15e9f3..0bdb5c8 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -354,7 +354,7 @@
// This function might print warnings into the log if the member is hidden.
template<typename T>
inline bool ShouldDenyAccessToMember(T* member,
- std::function<AccessContext()> fn_get_access_context,
+ const std::function<AccessContext()>& fn_get_access_context,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 72a8727..7a1b7eb 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -181,17 +181,21 @@
return param->AsString();
}
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(
+ ShadowFrame* frame) {
+ return [=]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+ };
+}
+
template<typename T>
static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
- return hiddenapi::ShouldDenyAccessToMember(
- member,
- [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
- },
- kAccessMethod);
+ return hiddenapi::ShouldDenyAccessToMember(member,
+ GetHiddenapiAccessContextFunction(frame),
+ kAccessMethod);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -390,22 +394,23 @@
Runtime* runtime = Runtime::Current();
bool transaction = runtime->IsActiveTransaction();
PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+ auto fn_hiddenapi_access_context = GetHiddenapiAccessContextFunction(shadow_frame);
ObjPtr<mirror::Method> method;
if (transaction) {
if (pointer_size == PointerSize::k64) {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
} else {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
}
} else {
if (pointer_size == PointerSize::k64) {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
} else {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, false>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
}
}
if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a6a5ba2..d9d88e1 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,15 +224,31 @@
}
}
-template<typename T, VerifyObjectFlags kVerifyFlags>
-inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx) {
// C style casts here since we sometimes have T be a pointer, or sometimes an integer
// (for stack traces).
- if (ptr_size == PointerSize::k64) {
+ if (kPointerSize == PointerSize::k64) {
return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) {
+ // C style casts here since we sometimes have T be a pointer, or sometimes an integer
+ // (for stack traces).
+ if (kPointerSize == PointerSize::k64) {
+ return (T)static_cast<uintptr_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+ }
+ return (T)static_cast<uintptr_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+}
+template<typename T, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+ if (ptr_size == PointerSize::k64) {
+ return GetElementPtrSize<T, PointerSize::k64, kVerifyFlags>(idx);
+ }
+ return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
+}
template<bool kTransactionActive, bool kUnchecked>
inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8816c61..2e894d5 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -32,6 +32,8 @@
class MANAGED Array : public Object {
public:
+ static constexpr size_t kFirstElementOffset = 12u;
+
// The size of a java.lang.Class representing an array.
static uint32_t ClassSize(PointerSize pointer_size);
@@ -79,6 +81,17 @@
<< "Array data offset isn't aligned with component size";
return MemberOffset(data_offset);
}
+ template <size_t kComponentSize>
+ static constexpr MemberOffset DataOffset() {
+ static_assert(IsPowerOfTwo(kComponentSize), "Invalid component size");
+ constexpr size_t data_offset = RoundUp(kFirstElementOffset, kComponentSize);
+ static_assert(RoundUp(data_offset, kComponentSize) == data_offset, "RoundUp fail");
+ return MemberOffset(data_offset);
+ }
+
+ static constexpr size_t FirstElementOffset() {
+ return OFFSETOF_MEMBER(Array, first_element_);
+ }
void* GetRawData(size_t component_size, int32_t index)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -86,12 +99,24 @@
+ (index * component_size);
return reinterpret_cast<void*>(data);
}
+ template <size_t kComponentSize>
+ void* GetRawData(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+ + (index * kComponentSize);
+ return reinterpret_cast<void*>(data);
+ }
const void* GetRawData(size_t component_size, int32_t index) const {
intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset(component_size).Int32Value() +
+ (index * component_size);
return reinterpret_cast<void*>(data);
}
+ template <size_t kComponentSize>
+ const void* GetRawData(int32_t index) const {
+ intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+ + (index * kComponentSize);
+ return reinterpret_cast<void*>(data);
+ }
// Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
// returns false.
@@ -132,11 +157,11 @@
const T* GetData() const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
+ return reinterpret_cast<const T*>(GetRawData<sizeof(T)>(0));
}
T* GetData() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return reinterpret_cast<T*>(GetRawData(sizeof(T), 0));
+ return reinterpret_cast<T*>(GetRawData<sizeof(T)>(0));
}
T Get(int32_t i) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -196,6 +221,15 @@
template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+ T GetElementPtrSize(uint32_t idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Same as GetElementPtrSize, but uses unchecked version of array conversion. It is thus not
+ // checked whether kPtrSize matches the underlying array. Only use after at least one invocation
+ // of GetElementPtrSize!
+ template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+ T GetElementPtrSizeUnchecked(uint32_t idx)
+ REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
void** ElementAddress(size_t index, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 53c9cc7..fcd3714 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -35,6 +35,7 @@
#include "gc/accounting/card_table-inl.h"
#include "gc/heap-inl.h"
#include "handle_scope-inl.h"
+#include "hidden_api.h"
#include "subtype_check.h"
#include "method.h"
#include "object-inl.h"
@@ -1252,18 +1253,50 @@
return (type_id == nullptr) ? dex::TypeIndex() : dex_file.GetIndexForTypeId(*type_id);
}
+ALWAYS_INLINE
+static bool IsMethodPreferredOver(ArtMethod* orig_method,
+ bool orig_method_hidden,
+ ArtMethod* new_method,
+ bool new_method_hidden) {
+ DCHECK(new_method != nullptr);
+
+ // Is this the first result?
+ if (orig_method == nullptr) {
+ return true;
+ }
+
+ // Original method is hidden, the new one is not?
+ if (orig_method_hidden && !new_method_hidden) {
+ return true;
+ }
+
+ // We iterate over virtual methods first and then over direct ones,
+ // so we can never be in situation where `orig_method` is direct and
+ // `new_method` is virtual.
+ DCHECK(!orig_method->IsDirect() || new_method->IsDirect());
+
+ // Original method is synthetic, the new one is not?
+ if (orig_method->IsSynthetic() && !new_method->IsSynthetic()) {
+ return true;
+ }
+
+ return false;
+}
+
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Method> Class::GetDeclaredMethodInternal(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args) {
- // Covariant return types permit the class to define multiple
- // methods with the same name and parameter types. Prefer to
- // return a non-synthetic method in such situations. We may
- // still return a synthetic method to handle situations like
- // escalated visibility. We never return miranda methods that
- // were synthesized by the runtime.
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context) {
+ // Covariant return types (or smali) permit the class to define
+ // multiple methods with the same name and parameter types.
+ // Prefer (in decreasing order of importance):
+ // 1) non-hidden method over hidden
+ // 2) virtual methods over direct
+ // 3) non-synthetic methods over synthetic
+ // We never return miranda methods that were synthesized by the runtime.
StackHandleScope<3> hs(self);
auto h_method_name = hs.NewHandle(name);
if (UNLIKELY(h_method_name == nullptr)) {
@@ -1272,8 +1305,13 @@
}
auto h_args = hs.NewHandle(args);
Handle<Class> h_klass = hs.NewHandle(klass);
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kNone;
ArtMethod* result = nullptr;
+ bool result_hidden = false;
for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) {
+ if (m.IsMiranda()) {
+ continue;
+ }
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr<String> np_name = np_method->ResolveNameString();
@@ -1283,14 +1321,24 @@
}
continue;
}
- if (!m.IsMiranda()) {
- if (!m.IsSynthetic()) {
- return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
- }
- result = &m; // Remember as potential result if it's not a miranda method.
+ bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+ if (!m_hidden && !m.IsSynthetic()) {
+ // Non-hidden, virtual, non-synthetic. Best possible result, exit early.
+ return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+ } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+ // Remember as potential result.
+ result = &m;
+ result_hidden = m_hidden;
}
}
- if (result == nullptr) {
+
+ if ((result != nullptr) && !result_hidden) {
+ // We have not found a non-hidden, virtual, non-synthetic method, but
+ // if we have found a non-hidden, virtual, synthetic method, we cannot
+ // do better than that later.
+ DCHECK(!result->IsDirect());
+ DCHECK(result->IsSynthetic());
+ } else {
for (auto& m : h_klass->GetDirectMethods(kPointerSize)) {
auto modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) != 0) {
@@ -1310,12 +1358,20 @@
continue;
}
DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods.
- if ((modifiers & kAccSynthetic) == 0) {
+ bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+ if (!m_hidden && !m.IsSynthetic()) {
+ // Non-hidden, direct, non-synthetic. Any virtual result could only have been
+ // hidden, therefore this is the best possible match. Exit now.
+ DCHECK((result == nullptr) || result_hidden);
return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+ } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+ // Remember as potential result.
+ result = &m;
+ result_hidden = m_hidden;
}
- result = &m; // Remember as potential result.
}
}
+
return result != nullptr
? Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
: nullptr;
@@ -1326,25 +1382,29 @@
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Constructor> Class::GetDeclaredConstructorInternal(
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 981ecf1..c9c542d 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -37,6 +37,10 @@
class TypeList;
} // namespace dex
+namespace hiddenapi {
+class AccessContext;
+} // namespace hiddenapi
+
template<typename T> class ArraySlice;
class ArtField;
class ArtMethod;
@@ -702,10 +706,12 @@
REQUIRES_SHARED(Locks::mutator_lock_);
template <PointerSize kPointerSize, bool kTransactionActive>
- static ObjPtr<Method> GetDeclaredMethodInternal(Thread* self,
- ObjPtr<Class> klass,
- ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args)
+ static ObjPtr<Method> GetDeclaredMethodInternal(
+ Thread* self,
+ ObjPtr<Class> klass,
+ ObjPtr<String> name,
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context)
REQUIRES_SHARED(Locks::mutator_lock_);
template <PointerSize kPointerSize, bool kTransactionActive>
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2c2ad9b..005e272 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -253,9 +253,13 @@
}
template<VerifyObjectFlags kVerifyFlags>
+inline IntArray* Object::AsIntArrayUnchecked() {
+ return down_cast<IntArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
inline IntArray* Object::AsIntArray() {
DCHECK((IsIntArray<kVerifyFlags>()));
- return down_cast<IntArray*>(this);
+ return AsIntArrayUnchecked<kVerifyFlags>();
}
template<VerifyObjectFlags kVerifyFlags>
@@ -264,9 +268,13 @@
}
template<VerifyObjectFlags kVerifyFlags>
+inline LongArray* Object::AsLongArrayUnchecked() {
+ return down_cast<LongArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
inline LongArray* Object::AsLongArray() {
DCHECK((IsLongArray<kVerifyFlags>()));
- return down_cast<LongArray*>(this);
+ return AsLongArrayUnchecked<kVerifyFlags>();
}
template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index ba222f6..ca8867d 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -223,11 +223,15 @@
bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ IntArray* AsIntArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ LongArray* AsLongArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsFloatArray() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f4b8ba5..27987c0 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -70,8 +70,6 @@
ASSERT_EQ(string->IsValueNull(), false);
// strlen is necessary because the 1-character string "\x00\x00" is interpreted as ""
ASSERT_TRUE(string->Equals(utf8_in) || (expected_utf16_length == 1 && strlen(utf8_in) == 0));
- ASSERT_TRUE(string->Equals(StringPiece(utf8_in)) ||
- (expected_utf16_length == 1 && strlen(utf8_in) == 0));
for (int32_t i = 0; i < expected_utf16_length; i++) {
EXPECT_EQ(utf16_expected[i], string->CharAt(i));
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01315e7..bf99c37 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -234,19 +234,6 @@
}
}
-bool String::Equals(const uint16_t* that_chars, int32_t that_offset, int32_t that_length) {
- if (this->GetLength() != that_length) {
- return false;
- } else {
- for (int32_t i = 0; i < that_length; ++i) {
- if (this->CharAt(i) != that_chars[that_offset + i]) {
- return false;
- }
- }
- return true;
- }
-}
-
bool String::Equals(const char* modified_utf8) {
const int32_t length = GetLength();
int32_t i = 0;
@@ -274,30 +261,6 @@
return *modified_utf8 == '\0';
}
-bool String::Equals(const StringPiece& modified_utf8) {
- const int32_t length = GetLength();
- const char* p = modified_utf8.data();
- for (int32_t i = 0; i < length; ++i) {
- uint32_t ch = GetUtf16FromUtf8(&p);
-
- if (GetLeadingUtf16Char(ch) != CharAt(i)) {
- return false;
- }
-
- const uint16_t trailing = GetTrailingUtf16Char(ch);
- if (trailing != 0) {
- if (i == (length - 1)) {
- return false;
- }
-
- if (CharAt(++i) != trailing) {
- return false;
- }
- }
- }
- return true;
-}
-
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string String::ToModifiedUtf8() {
size_t byte_count = GetUtfLength();
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index c367957..4a7f4ae 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -27,7 +27,6 @@
template<class T> class Handle;
struct StringOffsets;
-class StringPiece;
class StubTest_ReadBarrierForRoot_Test;
namespace mirror {
@@ -154,27 +153,10 @@
static String* AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
- // TODO: This is only used in the interpreter to compare against
- // entries from a dex files constant pool (ArtField names). Should
- // we unify this with Equals(const StringPiece&); ?
bool Equals(const char* modified_utf8) REQUIRES_SHARED(Locks::mutator_lock_);
- // TODO: This is only used to compare DexCache.location with
- // a dex_file's location (which is an std::string). Do we really
- // need this in mirror::String just for that one usage ?
- bool Equals(const StringPiece& modified_utf8)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
bool Equals(ObjPtr<String> that) REQUIRES_SHARED(Locks::mutator_lock_);
- // Compare UTF-16 code point values not in a locale-sensitive manner
- int Compare(int32_t utf16_length, const char* utf8_data_in);
-
- // TODO: do we need this overload? give it a more intention-revealing name.
- bool Equals(const uint16_t* that_chars, int32_t that_offset,
- int32_t that_length)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string ToModifiedUtf8() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 4115791..db62475 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -113,15 +113,18 @@
: hiddenapi::AccessContext(caller);
}
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(Thread* self) {
+ return [=]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); };
+}
+
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template<typename T>
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldDenyAccessToMember(
- member,
- [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
- hiddenapi::AccessMethod::kReflection);
+ return hiddenapi::ShouldDenyAccessToMember(member,
+ GetHiddenapiAccessContextFunction(self),
+ hiddenapi::AccessMethod::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
@@ -563,7 +566,8 @@
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
- soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(args),
+ GetHiddenapiAccessContextFunction(soa.Self())));
if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 00a0a39..a4425ce 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -50,7 +50,9 @@
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
+#include "class_linker.h"
#include "oat_quick_method_header.h"
+#include "runtime.h"
#include "thread-current-inl.h"
#endif
@@ -287,11 +289,17 @@
}
static bool PcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) NO_THREAD_SAFETY_ANALYSIS {
- uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(
- method->GetEntryPointFromQuickCompiledCode()));
- if (code == 0) {
+ const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+ if (entry_point == nullptr) {
return pc == 0;
}
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (class_linker->IsQuickGenericJniStub(entry_point) ||
+ class_linker->IsQuickResolutionStub(entry_point) ||
+ class_linker->IsQuickToInterpreterBridge(entry_point)) {
+ return false;
+ }
+ uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(entry_point));
uintptr_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].GetCodeSize();
return code <= pc && pc <= (code + code_size);
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index d179b80..8a0a1e7 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -838,10 +838,10 @@
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table.
- StringPiece key(oat_dex_file->GetDexFileLocation());
+ std::string_view key(oat_dex_file->GetDexFileLocation());
oat_dex_files_.Put(key, oat_dex_file);
if (canonical_location != dex_file_location) {
- StringPiece canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
+ std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
@@ -1664,7 +1664,7 @@
// without any performance loss, for example by not doing the first lock-free lookup.
const OatDexFile* oat_dex_file = nullptr;
- StringPiece key(dex_location);
+ std::string_view key(dex_location);
// Try to find the key cheaply in the oat_dex_files_ map which holds dex locations
// directly mentioned in the oat file and doesn't require locking.
auto primary_it = oat_dex_files_.find(key);
@@ -1683,7 +1683,7 @@
// We haven't seen this dex_location before, we must check the canonical location.
std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
if (dex_canonical_location != dex_location) {
- StringPiece canonical_key(dex_canonical_location);
+ std::string_view canonical_key(dex_canonical_location);
auto canonical_it = oat_dex_files_.find(canonical_key);
if (canonical_it != oat_dex_files_.end()) {
oat_dex_file = canonical_it->second;
@@ -1692,7 +1692,7 @@
// Copy the key to the string_cache_ and store the result in secondary map.
string_cache_.emplace_back(key.data(), key.length());
- StringPiece key_copy(string_cache_.back());
+ std::string_view key_copy(string_cache_.back());
secondary_oat_dex_files_.PutBefore(secondary_lb, key_copy, oat_dex_file);
}
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 1ba6e49..37dbe6a 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -19,13 +19,13 @@
#include <list>
#include <string>
+#include <string_view>
#include <vector>
#include "base/array_ref.h"
#include "base/mutex.h"
#include "base/os.h"
#include "base/safe_map.h"
-#include "base/stringpiece.h"
#include "base/tracking_safe_map.h"
#include "class_status.h"
#include "compiler_filter.h"
@@ -398,12 +398,13 @@
// Owning storage for the OatDexFile objects.
std::vector<const OatDexFile*> oat_dex_files_storage_;
- // NOTE: We use a StringPiece as the key type to avoid a memory allocation on every
- // lookup with a const char* key. The StringPiece doesn't own its backing storage,
+ // NOTE: We use a std::string_view as the key type to avoid a memory allocation on every
+ // lookup with a const char* key. The std::string_view doesn't own its backing storage,
// therefore we're using the OatDexFile::dex_file_location_ as the backing storage
// for keys in oat_dex_files_ and the string_cache_ entries for the backing storage
// of keys in secondary_oat_dex_files_ and oat_dex_files_by_canonical_location_.
- typedef AllocationTrackingSafeMap<StringPiece, const OatDexFile*, kAllocatorTagOatFile> Table;
+ using Table =
+ AllocationTrackingSafeMap<std::string_view, const OatDexFile*, kAllocatorTagOatFile>;
// Map each location and canonical location (if different) retrieved from the
// oat file to its OatDexFile. This map doesn't change after it's constructed in Setup()
@@ -422,7 +423,7 @@
// Cache of strings. Contains the backing storage for keys in the secondary_oat_dex_files_
// and the lazily initialized oat_dex_files_by_canonical_location_.
- // NOTE: We're keeping references to contained strings in form of StringPiece and adding
+ // NOTE: We're keeping references to contained strings in form of std::string_view and adding
// new strings to the end. The adding of a new element must not touch any previously stored
// elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't.
mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index feade83..8f89f52 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -199,6 +199,7 @@
};
namespace {
+
#ifdef __APPLE__
inline char** GetEnviron() {
// When Google Test is built as a framework on MacOS X, the environ variable
@@ -212,6 +213,11 @@
extern "C" char** environ;
inline char** GetEnviron() { return environ; }
#endif
+
+void CheckConstants() {
+ CHECK_EQ(mirror::Array::kFirstElementOffset, mirror::Array::FirstElementOffset());
+}
+
} // namespace
Runtime::Runtime()
@@ -284,6 +290,7 @@
verifier_logging_threshold_ms_(100) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
+ CheckConstants();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
interpreter::CheckInterpreterAsmConstants();
@@ -2475,10 +2482,15 @@
instruction_set += GetInstructionSetString(kRuntimeISA);
argv->push_back(instruction_set);
- std::unique_ptr<const InstructionSetFeatures> features(InstructionSetFeatures::FromCppDefines());
- std::string feature_string("--instruction-set-features=");
- feature_string += features->GetFeatureString();
- argv->push_back(feature_string);
+ if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+ argv->push_back("--instruction-set-features=runtime");
+ } else {
+ std::unique_ptr<const InstructionSetFeatures> features(
+ InstructionSetFeatures::FromCppDefines());
+ std::string feature_string("--instruction-set-features=");
+ feature_string += features->GetFeatureString();
+ argv->push_back(feature_string);
+ }
}
void Runtime::CreateJitCodeCache(bool rwx_memory_allowed) {
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 91be00d..150d35c 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -55,18 +55,22 @@
const NullType* NullType::instance_ = nullptr;
PrimitiveType::PrimitiveType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id) {
CHECK(klass != nullptr);
CHECK(!descriptor.empty());
}
-Cat1Type::Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+Cat1Type::Cat1Type(ObjPtr<mirror::Class> klass,
+ const std::string_view& descriptor,
+ uint16_t cache_id)
: PrimitiveType(klass, descriptor, cache_id) {
}
-Cat2Type::Cat2Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+Cat2Type::Cat2Type(ObjPtr<mirror::Class> klass,
+ const std::string_view& descriptor,
+ uint16_t cache_id)
: PrimitiveType(klass, descriptor, cache_id) {
}
@@ -132,7 +136,7 @@
}
const DoubleHiType* DoubleHiType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new DoubleHiType(klass, descriptor, cache_id);
@@ -147,7 +151,7 @@
}
const DoubleLoType* DoubleLoType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new DoubleLoType(klass, descriptor, cache_id);
@@ -162,7 +166,7 @@
}
const LongLoType* LongLoType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new LongLoType(klass, descriptor, cache_id);
@@ -170,7 +174,7 @@
}
const LongHiType* LongHiType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new LongHiType(klass, descriptor, cache_id);
@@ -192,7 +196,7 @@
}
const FloatType* FloatType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new FloatType(klass, descriptor, cache_id);
@@ -207,7 +211,7 @@
}
const CharType* CharType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new CharType(klass, descriptor, cache_id);
@@ -222,7 +226,7 @@
}
const ShortType* ShortType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new ShortType(klass, descriptor, cache_id);
@@ -237,7 +241,7 @@
}
const ByteType* ByteType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new ByteType(klass, descriptor, cache_id);
@@ -252,7 +256,7 @@
}
const IntegerType* IntegerType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new IntegerType(klass, descriptor, cache_id);
@@ -267,7 +271,7 @@
}
const ConflictType* ConflictType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new ConflictType(klass, descriptor, cache_id);
@@ -282,7 +286,7 @@
}
const BooleanType* BooleanType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(BooleanType::instance_ == nullptr);
instance_ = new BooleanType(klass, descriptor, cache_id);
@@ -301,7 +305,7 @@
}
const UndefinedType* UndefinedType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new UndefinedType(klass, descriptor, cache_id);
@@ -316,7 +320,7 @@
}
PreciseReferenceType::PreciseReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id) {
// Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError
@@ -352,47 +356,47 @@
std::string UnresolvedReferenceType::Dump() const {
std::stringstream result;
- result << "Unresolved Reference" << ": " << PrettyDescriptor(GetDescriptor().as_string().c_str());
+ result << "Unresolved Reference: " << PrettyDescriptor(std::string(GetDescriptor()).c_str());
return result.str();
}
std::string UnresolvedUninitializedRefType::Dump() const {
std::stringstream result;
- result << "Unresolved And Uninitialized Reference" << ": "
- << PrettyDescriptor(GetDescriptor().as_string().c_str())
+ result << "Unresolved And Uninitialized Reference: "
+ << PrettyDescriptor(std::string(GetDescriptor()).c_str())
<< " Allocation PC: " << GetAllocationPc();
return result.str();
}
std::string UnresolvedUninitializedThisRefType::Dump() const {
std::stringstream result;
- result << "Unresolved And Uninitialized This Reference"
- << PrettyDescriptor(GetDescriptor().as_string().c_str());
+ result << "Unresolved And Uninitialized This Reference: "
+ << PrettyDescriptor(std::string(GetDescriptor()).c_str());
return result.str();
}
std::string ReferenceType::Dump() const {
std::stringstream result;
- result << "Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+ result << "Reference: " << mirror::Class::PrettyDescriptor(GetClass());
return result.str();
}
std::string PreciseReferenceType::Dump() const {
std::stringstream result;
- result << "Precise Reference" << ": "<< mirror::Class::PrettyDescriptor(GetClass());
+ result << "Precise Reference: " << mirror::Class::PrettyDescriptor(GetClass());
return result.str();
}
std::string UninitializedReferenceType::Dump() const {
std::stringstream result;
- result << "Uninitialized Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+ result << "Uninitialized Reference: " << mirror::Class::PrettyDescriptor(GetClass());
result << " Allocation PC: " << GetAllocationPc();
return result.str();
}
std::string UninitializedThisReferenceType::Dump() const {
std::stringstream result;
- result << "Uninitialized This Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+ result << "Uninitialized This Reference: " << mirror::Class::PrettyDescriptor(GetClass());
result << "Allocation PC: " << GetAllocationPc();
return result.str();
}
@@ -990,7 +994,7 @@
}
const NullType* NullType::CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) {
CHECK(instance_ == nullptr);
instance_ = new NullType(klass, descriptor, cache_id);
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 3369784..56073db 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -21,12 +21,12 @@
#include <limits>
#include <set>
#include <string>
+#include <string_view>
#include "base/arena_object.h"
#include "base/bit_vector.h"
#include "base/locks.h"
#include "base/macros.h"
-#include "base/stringpiece.h"
#include "dex/primitive.h"
#include "gc_root.h"
#include "handle_scope.h"
@@ -185,7 +185,7 @@
bool IsJavaLangObjectArray() const
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsInstantiableTypes() const REQUIRES_SHARED(Locks::mutator_lock_);
- const StringPiece& GetDescriptor() const {
+ const std::string_view& GetDescriptor() const {
DCHECK(HasClass() ||
(IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
!IsUnresolvedSuperClass()));
@@ -319,7 +319,7 @@
protected:
RegType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: descriptor_(descriptor),
klass_(klass),
@@ -336,7 +336,7 @@
virtual AssignmentType GetAssignmentTypeImpl() const = 0;
- const StringPiece descriptor_;
+ const std::string_view descriptor_;
mutable GcRoot<mirror::Class> klass_; // Non-const only due to moving classes.
const uint16_t cache_id_;
@@ -389,7 +389,7 @@
// Create the singleton instance.
static const ConflictType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -402,7 +402,7 @@
private:
ConflictType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -425,7 +425,7 @@
// Create the singleton instance.
static const UndefinedType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -438,7 +438,7 @@
private:
UndefinedType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -450,7 +450,7 @@
class PrimitiveType : public RegType {
public:
PrimitiveType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
bool HasClassVirtual() const override { return true; }
@@ -458,7 +458,8 @@
class Cat1Type : public PrimitiveType {
public:
- Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
+ Cat1Type(ObjPtr<mirror::Class> klass,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -467,7 +468,7 @@
bool IsInteger() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const IntegerType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const IntegerType* GetInstance() PURE;
@@ -479,7 +480,7 @@
private:
IntegerType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -492,7 +493,7 @@
bool IsBoolean() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const BooleanType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const BooleanType* GetInstance() PURE;
@@ -504,7 +505,7 @@
private:
BooleanType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -518,7 +519,7 @@
bool IsByte() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const ByteType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const ByteType* GetInstance() PURE;
@@ -530,7 +531,7 @@
private:
ByteType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -543,7 +544,7 @@
bool IsShort() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const ShortType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const ShortType* GetInstance() PURE;
@@ -554,7 +555,7 @@
}
private:
- ShortType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
+ ShortType(ObjPtr<mirror::Class> klass, const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -567,7 +568,7 @@
bool IsChar() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const CharType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const CharType* GetInstance() PURE;
@@ -579,7 +580,7 @@
private:
CharType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -592,7 +593,7 @@
bool IsFloat() const override { return true; }
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
static const FloatType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const FloatType* GetInstance() PURE;
@@ -604,7 +605,7 @@
private:
FloatType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat1Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -615,7 +616,7 @@
class Cat2Type : public PrimitiveType {
public:
Cat2Type(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -625,7 +626,7 @@
bool IsLongLo() const override { return true; }
bool IsLong() const override { return true; }
static const LongLoType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const LongLoType* GetInstance() PURE;
@@ -637,7 +638,7 @@
private:
LongLoType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -650,7 +651,7 @@
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsLongHi() const override { return true; }
static const LongHiType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const LongHiType* GetInstance() PURE;
@@ -662,7 +663,7 @@
private:
LongHiType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -676,7 +677,7 @@
bool IsDoubleLo() const override { return true; }
bool IsDouble() const override { return true; }
static const DoubleLoType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const DoubleLoType* GetInstance() PURE;
@@ -688,7 +689,7 @@
private:
DoubleLoType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -701,7 +702,7 @@
std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
bool IsDoubleHi() const override { return true; }
static const DoubleHiType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
static const DoubleHiType* GetInstance() PURE;
@@ -713,7 +714,7 @@
private:
DoubleHiType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: Cat2Type(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -886,7 +887,7 @@
// Create the singleton instance.
static const NullType* CreateInstance(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -905,7 +906,7 @@
}
private:
- NullType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+ NullType(ObjPtr<mirror::Class> klass, const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -920,7 +921,7 @@
class UninitializedType : public RegType {
public:
UninitializedType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint32_t allocation_pc,
uint16_t cache_id)
: RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {}
@@ -945,7 +946,7 @@
class UninitializedReferenceType final : public UninitializedType {
public:
UninitializedReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint32_t allocation_pc,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -964,8 +965,9 @@
// constructor.
class UnresolvedUninitializedRefType final : public UninitializedType {
public:
- UnresolvedUninitializedRefType(const StringPiece& descriptor,
- uint32_t allocation_pc, uint16_t cache_id)
+ UnresolvedUninitializedRefType(const std::string_view& descriptor,
+ uint32_t allocation_pc,
+ uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, allocation_pc, cache_id) {
CheckConstructorInvariants(this);
@@ -986,7 +988,7 @@
class UninitializedThisReferenceType final : public UninitializedType {
public:
UninitializedThisReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(klass, descriptor, 0, cache_id) {
@@ -1005,8 +1007,7 @@
class UnresolvedUninitializedThisRefType final : public UninitializedType {
public:
- UnresolvedUninitializedThisRefType(const StringPiece& descriptor,
- uint16_t cache_id)
+ UnresolvedUninitializedThisRefType(const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UninitializedType(nullptr, descriptor, 0, cache_id) {
CheckConstructorInvariants(this);
@@ -1027,7 +1028,7 @@
class ReferenceType final : public RegType {
public:
ReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
CheckConstructorInvariants(this);
@@ -1052,7 +1053,7 @@
class PreciseReferenceType final : public RegType {
public:
PreciseReferenceType(ObjPtr<mirror::Class> klass,
- const StringPiece& descriptor,
+ const std::string_view& descriptor,
uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1072,7 +1073,7 @@
// Common parent of unresolved types.
class UnresolvedType : public RegType {
public:
- UnresolvedType(const StringPiece& descriptor, uint16_t cache_id)
+ UnresolvedType(const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: RegType(nullptr, descriptor, cache_id) {}
@@ -1088,7 +1089,7 @@
// of this type must be conservative.
class UnresolvedReferenceType final : public UnresolvedType {
public:
- UnresolvedReferenceType(const StringPiece& descriptor, uint16_t cache_id)
+ UnresolvedReferenceType(const std::string_view& descriptor, uint16_t cache_id)
REQUIRES_SHARED(Locks::mutator_lock_)
: UnresolvedType(descriptor, cache_id) {
CheckConstructorInvariants(this);
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index ceba748..7bff255 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -135,7 +135,7 @@
}
}
-bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) {
+bool RegTypeCache::MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise) {
const RegType* entry = entries_[idx];
if (descriptor != entry->descriptor_) {
return false;
@@ -170,20 +170,20 @@
return klass;
}
-StringPiece RegTypeCache::AddString(const StringPiece& string_piece) {
- char* ptr = allocator_.AllocArray<char>(string_piece.length());
- memcpy(ptr, string_piece.data(), string_piece.length());
- return StringPiece(ptr, string_piece.length());
+std::string_view RegTypeCache::AddString(const std::string_view& str) {
+ char* ptr = allocator_.AllocArray<char>(str.length());
+ memcpy(ptr, str.data(), str.length());
+ return std::string_view(ptr, str.length());
}
const RegType& RegTypeCache::From(ObjPtr<mirror::ClassLoader> loader,
const char* descriptor,
bool precise) {
- StringPiece sp_descriptor(descriptor);
- // Try looking up the class in the cache first. We use a StringPiece to avoid continual strlen
- // operations on the descriptor.
+ std::string_view sv_descriptor(descriptor);
+ // Try looking up the class in the cache first. We use a std::string_view to avoid
+ // repeated strlen operations on the descriptor.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- if (MatchDescriptor(i, sp_descriptor, precise)) {
+ if (MatchDescriptor(i, sv_descriptor, precise)) {
return *(entries_[i]);
}
}
@@ -205,9 +205,9 @@
DCHECK(!(klass->IsAbstract()) || klass->IsArrayClass());
DCHECK(!klass->IsInterface());
entry =
- new (&allocator_) PreciseReferenceType(klass, AddString(sp_descriptor), entries_.size());
+ new (&allocator_) PreciseReferenceType(klass, AddString(sv_descriptor), entries_.size());
} else {
- entry = new (&allocator_) ReferenceType(klass, AddString(sp_descriptor), entries_.size());
+ entry = new (&allocator_) ReferenceType(klass, AddString(sv_descriptor), entries_.size());
}
return AddEntry(entry);
} else { // Class not resolved.
@@ -221,7 +221,7 @@
}
if (IsValidDescriptor(descriptor)) {
return AddEntry(
- new (&allocator_) UnresolvedReferenceType(AddString(sp_descriptor), entries_.size()));
+ new (&allocator_) UnresolvedReferenceType(AddString(sv_descriptor), entries_.size()));
} else {
// The descriptor is broken return the unknown type as there's nothing sensible that
// could be done at runtime
@@ -254,7 +254,7 @@
return nullptr;
}
-const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor,
+const RegType* RegTypeCache::InsertClass(const std::string_view& descriptor,
ObjPtr<mirror::Class> klass,
bool precise) {
// No reference to the class was found, create new reference.
@@ -272,7 +272,7 @@
DCHECK(klass != nullptr);
const RegType* reg_type = FindClass(klass, precise);
if (reg_type == nullptr) {
- reg_type = InsertClass(AddString(StringPiece(descriptor)), klass, precise);
+ reg_type = InsertClass(AddString(std::string_view(descriptor)), klass, precise);
}
return *reg_type;
}
@@ -488,7 +488,7 @@
const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
UninitializedType* entry = nullptr;
- const StringPiece& descriptor(type.GetDescriptor());
+ const std::string_view& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
@@ -525,7 +525,7 @@
RegType* entry;
if (uninit_type.IsUnresolvedTypes()) {
- const StringPiece& descriptor(uninit_type.GetDescriptor());
+ const std::string_view& descriptor(uninit_type.GetDescriptor());
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedReference() &&
@@ -575,7 +575,7 @@
const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
UninitializedType* entry;
- const StringPiece& descriptor(type.GetDescriptor());
+ const std::string_view& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
@@ -656,7 +656,7 @@
return Conflict();
} else if (array.IsUnresolvedTypes()) {
DCHECK(!array.IsUnresolvedMergedReference()); // Caller must make sure not to ask for this.
- const std::string descriptor(array.GetDescriptor().as_string());
+ const std::string descriptor(array.GetDescriptor());
return FromDescriptor(loader, descriptor.c_str() + 1, false);
} else {
ObjPtr<mirror::Class> klass = array.GetClass()->GetComponentType();
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index d668222..a9a8116 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_H_
#include <stdint.h>
+#include <string_view>
#include <vector>
#include "base/casts.h"
@@ -32,7 +33,6 @@
class ClassLoader;
} // namespace mirror
class ScopedArenaAllocator;
-class StringPiece;
namespace verifier {
@@ -80,7 +80,7 @@
const RegType* FindClass(ObjPtr<mirror::Class> klass, bool precise) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Insert a new class with a specified descriptor, must not already be in the cache.
- const RegType* InsertClass(const StringPiece& descriptor,
+ const RegType* InsertClass(const std::string_view& descriptor,
ObjPtr<mirror::Class> klass,
bool precise)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -164,7 +164,7 @@
void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<mirror::Class> ResolveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> loader)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise)
+ bool MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise)
REQUIRES_SHARED(Locks::mutator_lock_);
const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -173,9 +173,9 @@
template <class RegTypeType>
RegTypeType& AddEntry(RegTypeType* new_entry) REQUIRES_SHARED(Locks::mutator_lock_);
- // Add a string piece to the arena allocator so that it stays live for the lifetime of the
- // verifier.
- StringPiece AddString(const StringPiece& string_piece);
+ // Add a string to the arena allocator so that it stays live for the lifetime of the
+ // verifier and return a string view.
+ std::string_view AddString(const std::string_view& str);
static void CreatePrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/690-hiddenapi-same-name-methods/build b/test/690-hiddenapi-same-name-methods/build
new file mode 100644
index 0000000..c364b3b
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2019 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/690-hiddenapi-same-name-methods/expected.txt b/test/690-hiddenapi-same-name-methods/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
new file mode 100644
index 0000000..001ab80
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
@@ -0,0 +1,9 @@
+LSpecificClass;->foo()Ljava/lang/Double;,blacklist
+LDirectMethods;->foo()Ljava/lang/Integer;,blacklist
+LDirectMethods;->foo()Ljava/lang/Boolean;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Integer;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Boolean;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/info.txt b/test/690-hiddenapi-same-name-methods/info.txt
new file mode 100644
index 0000000..be5b195
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/info.txt
@@ -0,0 +1 @@
+Test that Class::GetDeclaredMethodInternal() takes hidden API into account.
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
new file mode 100644
index 0000000..8564976
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LDirectMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method private foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method private foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
new file mode 100644
index 0000000..f47219f
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LNonSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
new file mode 100644
index 0000000..afb4d33
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public synthetic foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public synthetic foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
new file mode 100644
index 0000000..fb26c74
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LVirtualMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
new file mode 100644
index 0000000..c404402
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface GenericInterface<T extends Number> {
+ public T foo();
+}
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
new file mode 100644
index 0000000..dd3a835
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SpecificClass implements GenericInterface<Double> {
+ public Double foo() {
+ return 42.0;
+ }
+}
diff --git a/test/690-hiddenapi-same-name-methods/src/Main.java b/test/690-hiddenapi-same-name-methods/src/Main.java
new file mode 100644
index 0000000..12cfdd7
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src/Main.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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 ClassNotFoundException, NoSuchMethodException {
+ 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, /* isCorePlatform */ false);
+
+ // All test classes contain just methods named "foo" with different return types
+ // and access flags. Check that:
+ // (a) only the non-hidden ones are returned from getDeclaredMethods
+ // (they have return types Number and Double), and
+ // (b) getDeclaredMethod picks virtual/non-synthetic methods over direct/synthetic
+ // (the right one always has return type Number).
+ Class<?> covariantClass = Class.forName(JAVA_CLASS_NAME, true, BOOT_CLASS_LOADER);
+ checkMethodList(covariantClass, /* expectedLength= */ 1);
+ checkMethod(covariantClass);
+
+ String[] classes = new String[] {
+ "VirtualMethods",
+ "DirectMethods",
+ "SyntheticMethods",
+ "NonSyntheticMethods"
+ };
+ for (String className : classes) {
+ Class<?> klass = Class.forName(className, true, BOOT_CLASS_LOADER);
+ checkMethodList(klass, /* expectedLength= */ 2);
+ checkMethod(klass);
+ }
+ }
+
+ private static void checkMethodList(Class<?> klass, int expectedLength) {
+ String className = klass.getName();
+ Method[] methods = klass.getDeclaredMethods();
+ if (methods.length != expectedLength) {
+ throw new RuntimeException(className + ": expected " + expectedLength +
+ " declared method(s), got " + methods.length);
+ }
+ boolean hasNumberReturnType = false;
+ boolean hasDoubleReturnType = false;
+ for (Method method : methods) {
+ if (!METHOD_NAME.equals(method.getName())) {
+ throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+ "\", got: \"" + method.getName() + "\"");
+ }
+ if (Number.class == method.getReturnType()) {
+ hasNumberReturnType = true;
+ } else if (Double.class == method.getReturnType()) {
+ hasDoubleReturnType = true;
+ }
+ }
+ if (methods.length >= 1 && !hasNumberReturnType) {
+ throw new RuntimeException(className + ": expected a method with return type \"Number\"");
+ }
+ if (methods.length >= 2 && !hasDoubleReturnType) {
+ throw new RuntimeException(className + ": expected a method with return type \"Double\"");
+ }
+ }
+
+ private static void checkMethod(Class<?> klass) throws NoSuchMethodException {
+ String className = klass.getName();
+ Method method = klass.getDeclaredMethod(METHOD_NAME);
+ if (!METHOD_NAME.equals(method.getName())) {
+ throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+ "\", got: \"" + method.getName() + "\"");
+ } else if (Number.class != method.getReturnType()) {
+ throw new RuntimeException(className + ": expected method return type: \"Number\", got \"" +
+ method.getReturnType().toString() + "\"");
+ }
+ }
+
+ private static final String DEX_EXTRA = new File(System.getenv("DEX_LOCATION"),
+ "690-hiddenapi-same-name-methods-ex.jar").getAbsolutePath();
+
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+ private static final String JAVA_CLASS_NAME = "SpecificClass";
+ private static final String METHOD_NAME = "foo";
+
+ // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void init();
+}
diff --git a/test/HiddenApi/AbstractPackageClass.java b/test/HiddenApi/AbstractPackageClass.java
new file mode 100644
index 0000000..8f955ca
--- /dev/null
+++ b/test/HiddenApi/AbstractPackageClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+abstract class AbstractPackageClass {
+ public void publicMethod2() {}
+}
diff --git a/test/HiddenApi/PackageClass.java b/test/HiddenApi/PackageClass.java
new file mode 100644
index 0000000..eece100
--- /dev/null
+++ b/test/HiddenApi/PackageClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+class PackageClass extends AbstractPackageClass implements PublicInterface {
+ public void publicMethod1() {}
+}
diff --git a/test/HiddenApi/PublicInterface.java b/test/HiddenApi/PublicInterface.java
new file mode 100644
index 0000000..77a3709
--- /dev/null
+++ b/test/HiddenApi/PublicInterface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface PublicInterface {
+ void publicMethod1();
+ void publicMethod2();
+}
diff --git a/test/HiddenApiStubs/HiddenApi b/test/HiddenApiStubs/HiddenApi
new file mode 100644
index 0000000..6841ab5
--- /dev/null
+++ b/test/HiddenApiStubs/HiddenApi
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface PublicInterface {
+ void publicMethod();
+}
diff --git a/test/HiddenApiStubs/PublicInterface.java b/test/HiddenApiStubs/PublicInterface.java
new file mode 100644
index 0000000..77a3709
--- /dev/null
+++ b/test/HiddenApiStubs/PublicInterface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface PublicInterface {
+ void publicMethod1();
+ void publicMethod2();
+}
diff --git a/test/etc/default-build b/test/etc/default-build
index 5eba804..d203698 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -80,6 +80,12 @@
HAS_JASMIN_MULTIDEX=false
fi
+if [ -d smali-ex ]; then
+ HAS_SMALI_EX=true
+else
+ HAS_SMALI_EX=false
+fi
+
if [ -d src-ex ]; then
HAS_SRC_EX=true
else
@@ -454,13 +460,25 @@
javac_with_bootclasspath -d classes-tmp-for-ex `find src-art -name '*.java'`
src_tmp_for_ex="-cp classes-tmp-for-ex"
fi
- mkdir classes-ex
+ mkdir -p classes-ex
javac_with_bootclasspath -d classes-ex $src_tmp_for_ex `find src-ex -name '*.java'`
fi
if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then
make_dex classes-ex
+fi
+if [ "${HAS_SMALI_EX}" = "true" -a ${NEED_DEX} = "true" ]; then
+ # Compile Smali classes
+ ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes-ex.dex `find smali-ex -name '*.smali'`
+ if [[ ! -s smali_classes-ex.dex ]] ; then
+ fail "${SMALI} produced no output."
+ fi
+ # Merge smali files into classes-ex.dex.
+ make_dexmerge classes-ex.dex smali_classes-ex.dex
+fi
+
+if [[ -f classes-ex.dex ]]; then
# 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
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 8de9574..cf6e69c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -534,8 +534,9 @@
"097-duplicate-method",
"138-duplicate-classes-check2",
"159-app-image-fields",
- "674-hiddenapi",
"649-vdex-duplicate-method",
+ "674-hiddenapi",
+ "690-hiddenapi-same-name-methods",
"804-class-extends-itself",
"921-hello-failure",
"999-redefine-hiddenapi"
@@ -555,6 +556,7 @@
"629-vdex-speed",
"647-jni-get-field-id",
"674-hiddenapi",
+ "690-hiddenapi-same-name-methods",
"944-transform-classloaders",
"999-redefine-hiddenapi"
],
@@ -1085,6 +1087,7 @@
"678-quickening",
"679-locks",
"688-shared-library",
+ "690-hiddenapi-same-name-methods",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",
@@ -1185,7 +1188,7 @@
},
{
"tests": ["719-dm-verify-redefinition"],
- "variant": "jvm | speed-profile | interp-ac | target",
+ "variant": "jvm | speed-profile | interp-ac | target | no-prebuild",
"description": ["Doesn't run on RI because of boot class redefintion.",
"Doesn't work with profiles because the run-test is not setup to",
"support both. It also needs full verification, so no interp-ac.",
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index f426d02..2692f68 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -124,6 +124,7 @@
}
inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
+ inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
inline bool Equals(const DexClass& other) const {
bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
@@ -344,13 +345,13 @@
// See comment on Hierarchy::ForEachResolvableMember.
template<typename Fn>
bool ForEachResolvableMember(const DexMember& other, Fn fn) {
- return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
+ std::vector<HierarchyClass*> visited;
+ return ForEachResolvableMember_Impl(other, fn, true, true, visited);
}
// Returns true if this class contains at least one member matching `other`.
bool HasMatchingMember(const DexMember& other) {
- return ForEachMatchingMember(
- other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
+ return ForEachMatchingMember(other, [](const DexMember&) { return true; });
}
// Recursively iterates over all subclasses of this class and invokes `fn`
@@ -366,62 +367,60 @@
}
private:
- // Result of resolution which takes into account whether the member was found
- // for the first time or not. This is just a performance optimization to prevent
- // re-visiting previously visited members.
- // Note that order matters. When accumulating results, we always pick the maximum.
- enum class ResolutionResult {
- kNotFound,
- kFoundOld,
- kFoundNew,
- };
-
- inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) {
- return static_cast<ResolutionResult>(
- std::max(static_cast<unsigned>(a), static_cast<unsigned>(b)));
- }
-
template<typename Fn>
- ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) {
- // First try to find a member matching `other` in this class.
- ResolutionResult foundInClass = ForEachMatchingMember(other, fn);
-
- switch (foundInClass) {
- case ResolutionResult::kFoundOld:
- // A matching member was found and previously explored. All subclasses
- // must have been explored too.
- break;
-
- case ResolutionResult::kFoundNew:
- // A matching member was found and this was the first time it was visited.
- // If it is a virtual method, visit all methods overriding/implementing it too.
- if (other.IsVirtualMethod()) {
- for (HierarchyClass* subclass : extended_by_) {
- subclass->ForEachOverridingMember(other, fn);
- }
- }
- break;
-
- case ResolutionResult::kNotFound:
- // A matching member was not found in this class. Explore the superclasses
- // and implemented interfaces.
- for (HierarchyClass* superclass : extends_) {
- foundInClass = Accumulate(
- foundInClass, superclass->ForEachResolvableMember_Impl(other, fn));
- }
- break;
+ bool ForEachResolvableMember_Impl(const DexMember& other,
+ Fn fn,
+ bool allow_explore_up,
+ bool allow_explore_down,
+ std::vector<HierarchyClass*> visited) {
+ if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
+ visited.push_back(this);
+ } else {
+ return false;
}
- return foundInClass;
+ // First try to find a member matching `other` in this class.
+ bool found = ForEachMatchingMember(other, fn);
+
+ // If not found, see if it is inherited from parents. Note that this will not
+ // revisit parents already in `visited`.
+ if (!found && allow_explore_up) {
+ for (HierarchyClass* superclass : extends_) {
+ found |= superclass->ForEachResolvableMember_Impl(
+ other,
+ fn,
+ /* allow_explore_up */ true,
+ /* allow_explore_down */ false,
+ visited);
+ }
+ }
+
+ // If this is a virtual method, continue exploring into subclasses so as to visit
+ // all overriding methods. Allow subclasses to explore their superclasses if this
+ // is an interface. This is needed to find implementations of this interface's
+ // methods inherited from superclasses (b/122551864).
+ if (allow_explore_down && other.IsVirtualMethod()) {
+ for (HierarchyClass* subclass : extended_by_) {
+ subclass->ForEachResolvableMember_Impl(
+ other,
+ fn,
+ /* allow_explore_up */ GetOneDexClass().IsInterface(),
+ /* allow_explore_down */ true,
+ visited);
+ }
+ }
+
+ return found;
}
template<typename Fn>
- ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
- ResolutionResult found = ResolutionResult::kNotFound;
+ bool ForEachMatchingMember(const DexMember& other, Fn fn) {
+ bool found = false;
auto compare_member = [&](const DexMember& member) {
+ // TODO(dbrazdil): Check whether class of `other` can access `member`.
if (member == other) {
- found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
- : ResolutionResult::kFoundOld);
+ found = true;
+ fn(member);
}
};
for (const DexClass& dex_class : dex_classes_) {
@@ -435,20 +434,6 @@
return found;
}
- template<typename Fn>
- void ForEachOverridingMember(const DexMember& other, Fn fn) {
- CHECK(other.IsVirtualMethod());
- ResolutionResult found = ForEachMatchingMember(other, fn);
- if (found == ResolutionResult::kFoundOld) {
- // No need to explore further.
- return;
- } else {
- for (HierarchyClass* subclass : extended_by_) {
- subclass->ForEachOverridingMember(other, fn);
- }
- }
- }
-
// DexClass entries of this class found across all the provided dex files.
std::vector<DexClass> dex_classes_;
@@ -1070,12 +1055,7 @@
std::string entry = boot_member.GetApiEntry();
auto it = boot_members.find(entry);
CHECK(it != boot_members.end());
- if (it->second.Contains(stub_api_list)) {
- return false; // has been marked before
- } else {
- it->second |= stub_api_list;
- return true; // marked for the first time
- }
+ it->second |= stub_api_list;
});
if (!resolved) {
unresolved.insert(stub_member.GetApiEntry());
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 7ef5b3d..74feb8a 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -16,6 +16,8 @@
#include <fstream>
+#include "android-base/strings.h"
+
#include "base/unix_file/fd_file.h"
#include "base/zip_archive.h"
#include "common_runtime_test.h"
@@ -41,9 +43,9 @@
return file_path;
}
- std::unique_ptr<const DexFile> RunHiddenApi(const ScratchFile& flags_csv,
- const std::vector<std::string>& extra_args,
- ScratchFile* out_dex) {
+ std::unique_ptr<const DexFile> RunHiddenapiEncode(const ScratchFile& flags_csv,
+ const std::vector<std::string>& extra_args,
+ const ScratchFile& out_dex) {
std::string error;
ScratchFile in_dex;
std::unique_ptr<ZipArchive> jar(
@@ -68,18 +70,42 @@
argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
argv_str.push_back("encode");
argv_str.push_back("--input-dex=" + in_dex.GetFilename());
- argv_str.push_back("--output-dex=" + out_dex->GetFilename());
+ argv_str.push_back("--output-dex=" + out_dex.GetFilename());
argv_str.push_back("--api-flags=" + flags_csv.GetFilename());
argv_str.push_back("--no-force-assign-all");
int return_code = ExecAndReturnCode(argv_str, &error);
if (return_code == 0) {
- return OpenDex(*out_dex);
+ return OpenDex(out_dex);
} else {
LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
return nullptr;
}
}
+ bool RunHiddenapiList(const ScratchFile& out_flags_csv) {
+ std::string error;
+ std::string boot_jar = GetTestDexFileName("HiddenApi");
+ std::string stub_jar = GetTestDexFileName("HiddenApiStubs");
+ std::string boot_cp = android::base::Join(GetLibCoreDexFileNames(), ":");
+
+ std::vector<std::string> argv_str;
+ argv_str.push_back(GetHiddenApiCmd());
+ argv_str.push_back("list");
+ for (const std::string& core_jar : GetLibCoreDexFileNames()) {
+ argv_str.push_back("--boot-dex=" + core_jar);
+ }
+ argv_str.push_back("--boot-dex=" + boot_jar);
+ argv_str.push_back("--public-stub-classpath=" + boot_cp + ":" + stub_jar);
+ argv_str.push_back("--out-api-flags=" + out_flags_csv.GetFilename());
+ int return_code = ExecAndReturnCode(argv_str, &error);
+ if (return_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
+ return false;
+ }
+ }
+
std::unique_ptr<const DexFile> OpenDex(const ScratchFile& file) {
ArtDexFileLoader dex_loader;
std::string error_msg;
@@ -113,6 +139,31 @@
return ofs;
}
+ std::map<std::string, std::string> ReadFlagsCsvFile(const ScratchFile& file) {
+ std::ifstream ifs(file.GetFilename());
+ std::map<std::string, std::string> flags;
+
+ for (std::string line; std::getline(ifs, line);) {
+ std::size_t comma = line.find(",");
+ if (comma == std::string::npos) {
+ flags.emplace(line, "");
+ } else {
+ flags.emplace(line.substr(0, comma), line.substr(comma + 1));
+ }
+ }
+
+ return flags;
+ }
+
+ std::string SafeMapGet(const std::string& key, const std::map<std::string, std::string>& map) {
+ auto it = map.find(key);
+ if (it == map.end()) {
+ LOG(FATAL) << "Key not found: " << key;
+ UNREACHABLE();
+ }
+ return it->second;
+ }
+
const dex::ClassDef& FindClass(const char* desc, const DexFile& dex_file) {
const dex::TypeId* type_id = dex_file.FindTypeId(desc);
CHECK(type_id != nullptr) << "Could not find class " << desc;
@@ -220,7 +271,7 @@
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file));
}
@@ -231,7 +282,7 @@
<< "LMain;->ifield:I,greylist" << std::endl
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
}
@@ -242,7 +293,7 @@
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:I,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file));
}
@@ -253,7 +304,7 @@
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:I,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file));
}
@@ -263,7 +314,7 @@
OpenStream(flags_csv)
<< "LMain;->ifield:LBadType1;,greylist" << std::endl
<< "LMain;->ifield:I,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -272,7 +323,7 @@
OpenStream(flags_csv)
<< "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->ifield:I,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -281,7 +332,7 @@
OpenStream(flags_csv)
<< "LMain;->ifield:I,greylist,greylist-max-o" << std::endl
<< "LMain;->ifield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -291,7 +342,7 @@
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file));
}
@@ -302,7 +353,7 @@
<< "LMain;->sfield:Ljava/lang/Object;,greylist" << std::endl
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file));
}
@@ -313,7 +364,7 @@
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file));
}
@@ -324,7 +375,7 @@
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file));
}
@@ -334,7 +385,7 @@
OpenStream(flags_csv)
<< "LMain;->sfield:LBadType1;,greylist" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -343,7 +394,7 @@
OpenStream(flags_csv)
<< "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
<< "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -352,7 +403,7 @@
OpenStream(flags_csv)
<< "LMain;->sfield:Ljava/lang/Object;,greylist,greylist-max-o" << std::endl
<< "LMain;->sfield:LBadType3;,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -362,7 +413,7 @@
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file));
}
@@ -373,7 +424,7 @@
<< "LMain;->imethod(J)V,greylist" << std::endl
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file));
}
@@ -384,7 +435,7 @@
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(J)V,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file));
}
@@ -395,7 +446,7 @@
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(J)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file));
}
@@ -405,7 +456,7 @@
OpenStream(flags_csv)
<< "LMain;->imethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->imethod(J)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -414,7 +465,7 @@
OpenStream(flags_csv)
<< "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->imethod(J)V,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -423,7 +474,7 @@
OpenStream(flags_csv)
<< "LMain;->imethod(J)V,greylist,greylist-max-o" << std::endl
<< "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -433,7 +484,7 @@
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file));
}
@@ -444,7 +495,7 @@
<< "LMain;->smethod(Ljava/lang/Object;)V,greylist" << std::endl
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file));
}
@@ -455,7 +506,7 @@
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file));
}
@@ -466,7 +517,7 @@
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file));
}
@@ -476,7 +527,7 @@
OpenStream(flags_csv)
<< "LMain;->smethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -485,7 +536,7 @@
OpenStream(flags_csv)
<< "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -494,7 +545,7 @@
OpenStream(flags_csv)
<< "LMain;->smethod(Ljava/lang/Object;)V,greylist,greylist-max-o" << std::endl
<< "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -504,7 +555,7 @@
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file));
}
@@ -515,7 +566,7 @@
<< "LMain;->inmethod(C)V,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file));
}
@@ -526,7 +577,7 @@
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(C)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file));
}
@@ -537,7 +588,7 @@
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->inmethod(C)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file));
}
@@ -547,7 +598,7 @@
OpenStream(flags_csv)
<< "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->inmethod(C)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -556,7 +607,7 @@
OpenStream(flags_csv)
<< "LMain;->inmethod(C)V,blacklist,greylist" << std::endl
<< "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -565,7 +616,7 @@
OpenStream(flags_csv)
<< "LMain;->inmethod(C)V,greylist,greylist-max-o" << std::endl
<< "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -575,7 +626,7 @@
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -586,7 +637,7 @@
<< "LMain;->snmethod(Ljava/lang/Integer;)V,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -597,7 +648,7 @@
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(Ljava/lang/Integer;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -608,7 +659,7 @@
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
<< "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_NE(dex_file.get(), nullptr);
ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file));
}
@@ -618,7 +669,7 @@
OpenStream(flags_csv)
<< "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
<< "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -627,7 +678,7 @@
OpenStream(flags_csv)
<< "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist" << std::endl
<< "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
@@ -636,8 +687,35 @@
OpenStream(flags_csv)
<< "LMain;->snmethod(Ljava/lang/Integer;)V,greylist,greylist-max-o" << std::endl
<< "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
- auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+ auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
ASSERT_EQ(dex_file.get(), nullptr);
}
+// The following tests use this class hierarchy:
+//
+// AbstractPackageClass PublicInterface
+// | |
+// | ┌----------------┘
+// | |
+// PackageClass
+//
+// Only PublicInterface is in stubs.
+
+// Test a method declared in PublicInterface and defined in PackageClass.
+TEST_F(HiddenApiTest, InterfaceMethodImplemented) {
+ ScratchFile flags_csv;
+ ASSERT_TRUE(RunHiddenapiList(flags_csv));
+ auto flags = ReadFlagsCsvFile(flags_csv);
+ ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "whitelist");
+}
+
+// Test a method declared in PublicInterface, defined in AbstractPackageClass and
+// inherited by PackageClass.
+TEST_F(HiddenApiTest, InterfaceMethodImplementedInParent) {
+ ScratchFile flags_csv;
+ ASSERT_TRUE(RunHiddenapiList(flags_csv));
+ auto flags = ReadFlagsCsvFile(flags_csv);
+ ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "whitelist");
+}
+
} // namespace art